控制系統(tǒng)(十三):安全監(jiān)控服務(wù)——壓力超標(biāo)了怎么辦)
安全監(jiān)控服務(wù)壓力超標(biāo)了怎么辦這是「從零搭建工業(yè)控制系統(tǒng)」系列第13篇。燈塔能顯示狀態(tài)但光顯示不夠。壓力超標(biāo)了得自動(dòng)關(guān)閥門斷電了得自動(dòng)啟動(dòng)保護(hù)。這篇講安全監(jiān)控服務(wù)怎么執(zhí)行安全動(dòng)作。一次差點(diǎn)炸腔的事故去年冬天Chamber腔體正在抽真空壓力傳感器突然故障——讀數(shù)卡在0。系統(tǒng)以為還在真空狀態(tài)繼續(xù)抽。實(shí)際上腔體已經(jīng)漏氣壓力回到大氣壓了。如果繼續(xù)抽下去泵會(huì)過(guò)載。那次靠操作員手動(dòng)按了急停才沒(méi)出大事。事后我花了兩天寫了ChamberSafetyControlService讓系統(tǒng)自己處理這種故障。核心思路壓力數(shù)據(jù)無(wú)效時(shí)按最安全的狀態(tài)處理。不知道壓力是多少就當(dāng)它不安全。雙重壓力監(jiān)控架構(gòu)系統(tǒng)有兩套壓力監(jiān)控保護(hù)不同的設(shè)備監(jiān)控服務(wù)監(jiān)控對(duì)象閾值保護(hù)設(shè)備ChamberPressureMonitorService腔體壓力模擬量輸入大氣壓閾值腔體閥PumpPressureMonitorService泵口壓力安全閾值泵控制輸出Chamber監(jiān)控保護(hù)腔體蓋的安全泵口壓力監(jiān)控保護(hù)真空泵的安全。兩套獨(dú)立運(yùn)行互不干擾。Chamber安全控制服務(wù)ChamberSafetyControlService監(jiān)聽(tīng)壓力變化自動(dòng)控制AutoChamberValve自動(dòng)腔體閥和ManualChamberValve手動(dòng)腔體閥publicpartialclassChamberSafetyControlService:ObservableObject,IRecipientHeaterPCWConfigChangedMessage,IDisposable{privatereadonlyIDeviceCmdService_deviceCmdService;privatereadonlyChamberPressureMonitorService_pressureMonitor;[ObservableProperty]privateboolautoAllowControl;// False: AutoChamberValve直接關(guān)閉; True: 基于壓力智能控制[ObservableProperty]privateboolmanualAllowControl;// False: ManualChamberValve直接關(guān)閉; True: 啟用壓力保護(hù)[ObservableProperty]privateboolautoValveAutoEnable;// AutoChamberValve自動(dòng)開(kāi)關(guān)計(jì)算結(jié)果[ObservableProperty]privateboolmanualValveManualEnable;// ManualChamberValve手動(dòng)開(kāi)關(guān)計(jì)算結(jié)果}構(gòu)造函數(shù)里訂閱壓力監(jiān)控的屬性變化publicChamberSafetyControlService(IDeviceCmdServicemcuCmdService,ChamberPressureMonitorServicepressureMonitor){_deviceCmdServicemcuCmdService;_pressureMonitorpressureMonitor;// 訂閱壓力監(jiān)控變化事件_pressureMonitor.PropertyChangedOnPressureMonitorPropertyChanged;// 注冊(cè)消息接收器監(jiān)聽(tīng)配置變更WeakReferenceMessenger.Default.Register(this);InitializeConfiguration();}壓力變化的響應(yīng)邏輯壓力監(jiān)控服務(wù)的IsPressureAboveThreshold變化時(shí)安全控制服務(wù)收到通知privatevoidOnPressureMonitorPropertyChanged(objectsender,PropertyChangedEventArgse){if(e.PropertyNamenameof(ChamberPressureMonitorService.IsPressureAboveThreshold)){UpdateControlOutputs();}elseif(e.PropertyNamenameof(ChamberPressureMonitorService.IsDataValid)){// 數(shù)據(jù)無(wú)效時(shí)也要更新——按最安全狀態(tài)處理UpdateControlOutputs();}}關(guān)鍵設(shè)計(jì)數(shù)據(jù)無(wú)效也觸發(fā)更新。傳感器故障時(shí)IsDataValidfalse這時(shí)候不能放任不管要按不安全處理。控制輸出計(jì)算privateasyncvoidUpdateControlOutputs(){// 計(jì)算AutoChamberValve自動(dòng)控制if(!autoAllowControl){autoValveAutoEnablefalse;// 配置禁用直接關(guān)}elseif(!_pressureMonitor.IsDataValid){autoValveAutoEnablefalse;// 數(shù)據(jù)無(wú)效按不安全處理}else{autoValveAutoEnable_pressureMonitor.IsPressureAboveThreshold;// 正常邏輯}// 計(jì)算ManualChamberValve手動(dòng)控制if(!manualAllowControl){manualValveManualEnablefalse;}elseif(!_pressureMonitor.IsDataValid){manualValveManualEnablefalse;}else{manualValveManualEnable_pressureMonitor.IsPressureAboveThreshold;}// 寫入硬件awaitWriteCoilValueAsync(_autoValveCoil,_autoValveCoil.GetWriteValue(autoValveAutoEnable));awaitWriteCoilValueAsync(_manualValveCoil,_manualValveCoil.GetWriteValue(manualValveManualEnable));}三層判斷配置是否啟用 → 數(shù)據(jù)是否有效 → 壓力是否達(dá)標(biāo)。任何一層不通過(guò)都關(guān)閥門。跨線程寫入的坑安全控制服務(wù)運(yùn)行在后臺(tái)線程但線圈寫入涉及UI綁定的ModbusCoil對(duì)象。直接調(diào)用WriteValueManuallyCommand會(huì)報(bào)跨線程異常。解決方案繞過(guò)Command直接調(diào)用底層API// 錯(cuò)誤做法通過(guò)Command寫入會(huì)觸發(fā)UI確認(rèn)對(duì)話框 跨線程異常// await coil.WriteValueManuallyCommand.ExecuteAsync(value);// 正確做法直接調(diào)用底層APIawait_deviceCmdService.WriteCoilValueAsync(coil,coil.GetWriteValue(enable));這個(gè)問(wèn)題排查了很久?,F(xiàn)象是啟動(dòng)后偶現(xiàn)InvalidOperationException: 調(diào)用線程無(wú)法訪問(wèn)此對(duì)象。最后發(fā)現(xiàn)是安全控制服務(wù)的Timer回調(diào)在后臺(tái)線程觸發(fā)了UI綁定屬性的更新。配置熱響應(yīng)配置變更時(shí)不需要重啟服務(wù)。通過(guò)消息機(jī)制實(shí)時(shí)響應(yīng)publicvoidReceive(HeaterPCWConfigChangedMessagemessage){// 配置變了重新計(jì)算控制輸出UpdateControlOutputs();}操作員在設(shè)置界面修改AutoAllowControl后保存安全控制服務(wù)立即收到消息并更新硬件輸出。線圈監(jiān)控防止外部篡改除了響應(yīng)壓力變化還有定時(shí)器監(jiān)控線圈狀態(tài)。如果有人手動(dòng)操作了AutoChamberValve或ManualChamberValve服務(wù)會(huì)檢測(cè)到并糾正privateasyncvoidOnCoilMonitorTick(objectstate){if(_isApplyingControl)return;// 自己寫入時(shí)跳過(guò)varautoValveActual_autoValveCoil.IsActive();if(autoValveActual!autoValveAutoEnable){_logger.Warn($AutoChamberValve被外部修改: 期望{autoValveAutoEnable}, 實(shí)際{autoValveActual}, 正在糾正);awaitWriteCoilValueAsync(_autoValveCoil,_autoValveCoil.GetWriteValue(autoValveAutoEnable));}}_isApplyingControl標(biāo)志位防止自己寫入時(shí)觸發(fā)監(jiān)控糾正形成死循環(huán)。三大觸發(fā)時(shí)機(jī)安全服務(wù)的啟動(dòng)有三個(gè)入口1. 應(yīng)用啟動(dòng)App.xaml.cs里延遲2秒自動(dòng)啟動(dòng)2. 設(shè)備連接設(shè)備連上后檢查是否需要啟動(dòng)安全服務(wù)3. 配置保存操作員修改配置后立即響應(yīng)// DeviceCmdService中的設(shè)備連接回調(diào)privatevoidOnDeviceStatusChanged(objectsender,DeviceStatusEventArgse){if(e.DeviceTypeDeviceType.DigitalIOe.IsConnected){// DigitalIO連接了檢查Chamber控制服務(wù)CheckAndStartChamberSafetyService();}}安全監(jiān)控踩坑清單坑現(xiàn)象解決跨線程寫入InvalidOperationException繞過(guò)Command直接調(diào)API傳感器故障讀數(shù)卡0繼續(xù)抽真空IsDataValidfalse時(shí)按不安全處理外部篡改手動(dòng)操作后狀態(tài)不一致定時(shí)器監(jiān)控糾正配置不生效改了配置要重啟消息機(jī)制實(shí)時(shí)響應(yīng)死循環(huán)監(jiān)控糾正觸發(fā)監(jiān)控_isApplyingControl標(biāo)志位本篇小結(jié)知識(shí)點(diǎn)關(guān)鍵做法雙重監(jiān)控腔體壓力 泵口壓力獨(dú)立監(jiān)控三層判斷配置啟用→數(shù)據(jù)有效→壓力達(dá)標(biāo)數(shù)據(jù)無(wú)效按最安全狀態(tài)處理不放任跨線程繞過(guò)Command直接WriteCoilValueAsync線圈監(jiān)控定時(shí)檢測(cè)外部篡改并糾正三入口啟動(dòng)/設(shè)備連接/配置保存安全監(jiān)控的核心原則不知道狀態(tài)時(shí)按最危險(xiǎn)的情況處理。下期預(yù)告第14篇配置系統(tǒng)基礎(chǔ)安全服務(wù)靠配置驅(qū)動(dòng)下篇講配置系統(tǒng)——INI文件、JSON配置、熱加載機(jī)制怎么設(shè)計(jì)。