安全监控服务:压力超标了怎么办
这是「从零搭建工业控制系统」系列第13篇。灯塔能"显示"状态,但光显示不够。压力超标了得自动关阀门,断电了得自动启动保护。这篇讲安全监控服务怎么"执行"安全动作。
一次差点炸腔的事故
去年冬天,Chamber腔体正在抽真空,压力传感器突然故障——读数卡在0。系统以为还在真空状态,继续抽。实际上腔体已经漏气,压力回到大气压了。如果继续抽下去,泵会过载。
那次靠操作员手动按了急停才没出大事。事后我花了两天写了 ChamberSafetyControlService,让系统自己处理这种故障。
核心思路:压力数据无效时,按最安全的状态处理。 不知道压力是多少,就当它不安全。
双重压力监控架构
系统有两套压力监控,保护不同的设备:
| 监控服务 | 监控对象 | 阈值 | 保护设备 |
|---|---|---|---|
| ChamberPressureMonitorService | 腔体压力(模拟量输入) | 大气压阈值 | 腔体阀 |
| PumpPressureMonitorService | 泵口压力 | 安全阈值 | 泵控制输出 |
Chamber监控保护腔体盖的安全,泵口压力监控保护真空泵的安全。两套独立运行,互不干扰。

Chamber安全控制服务
ChamberSafetyControlService 监听压力变化,自动控制AutoChamberValve(自动腔体阀)和ManualChamberValve(手动腔体阀):
public partial class ChamberSafetyControlService : ObservableObject,
IRecipient<HeaterPCWConfigChangedMessage>, IDisposable
{
private readonly IDeviceCmdService _deviceCmdService;
private readonly ChamberPressureMonitorService _pressureMonitor;
[ObservableProperty]
private bool autoAllowControl; // False: AutoChamberValve直接关闭; True: 基于压力智能控制
[ObservableProperty]
private bool manualAllowControl; // False: ManualChamberValve直接关闭; True: 启用压力保护
[ObservableProperty]
private bool autoValveAutoEnable; // AutoChamberValve自动开关计算结果
[ObservableProperty]
private bool manualValveManualEnable; // ManualChamberValve手动开关计算结果
}
构造函数里订阅压力监控的属性变化:
public ChamberSafetyControlService(
IDeviceCmdService mcuCmdService,
ChamberPressureMonitorService pressureMonitor)
{
_deviceCmdService = mcuCmdService;
_pressureMonitor = pressureMonitor;
// 订阅压力监控变化事件
_pressureMonitor.PropertyChanged += OnPressureMonitorPropertyChanged;
// 注册消息接收器,监听配置变更
WeakReferenceMessenger.Default.Register(this);
InitializeConfiguration();
}
压力变化的响应逻辑
压力监控服务的 IsPressureAboveThreshold 变化时,安全控制服务收到通知:
private void OnPressureMonitorPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(ChamberPressureMonitorService.IsPressureAboveThreshold))
{
UpdateControlOutputs();
}
else if (e.PropertyName == nameof(ChamberPressureMonitorService.IsDataValid))
{
// 数据无效时也要更新——按最安全状态处理
UpdateControlOutputs();
}
}
关键设计:数据无效也触发更新。 传感器故障时 IsDataValid=false,这时候不能放任不管,要按"不安全"处理。
控制输出计算
private async void UpdateControlOutputs()
{
// 计算AutoChamberValve自动控制
if (!autoAllowControl)
{
autoValveAutoEnable = false; // 配置禁用,直接关
}
else if (!_pressureMonitor.IsDataValid)
{
autoValveAutoEnable = false; // 数据无效,按不安全处理
}
else
{
autoValveAutoEnable = _pressureMonitor.IsPressureAboveThreshold; // 正常逻辑
}
// 计算ManualChamberValve手动控制
if (!manualAllowControl)
{
manualValveManualEnable = false;
}
else if (!_pressureMonitor.IsDataValid)
{
manualValveManualEnable = false;
}
else
{
manualValveManualEnable = _pressureMonitor.IsPressureAboveThreshold;
}
// 写入硬件
await WriteCoilValueAsync(_autoValveCoil, _autoValveCoil.GetWriteValue(autoValveAutoEnable));
await WriteCoilValueAsync(_manualValveCoil, _manualValveCoil.GetWriteValue(manualValveManualEnable));
}
三层判断:配置是否启用 → 数据是否有效 → 压力是否达标。任何一层不通过都关阀门。

跨线程写入的坑
安全控制服务运行在后台线程,但线圈写入涉及UI绑定的 ModbusCoil 对象。直接调用 WriteValueManuallyCommand 会报跨线程异常。
解决方案:绕过Command,直接调用底层API:
// 错误做法:通过Command写入(会触发UI确认对话框 + 跨线程异常)
// await coil.WriteValueManuallyCommand.ExecuteAsync(value);
// 正确做法:直接调用底层API
await _deviceCmdService.WriteCoilValueAsync(coil, coil.GetWriteValue(enable));
这个问题排查了很久。现象是启动后偶现 InvalidOperationException: 调用线程无法访问此对象。最后发现是安全控制服务的Timer回调在后台线程触发了UI绑定属性的更新。
配置热响应
配置变更时不需要重启服务。通过消息机制实时响应:
public void Receive(HeaterPCWConfigChangedMessage message)
{
// 配置变了,重新计算控制输出
UpdateControlOutputs();
}
操作员在设置界面修改 AutoAllowControl 后保存,安全控制服务立即收到消息并更新硬件输出。
线圈监控:防止外部篡改
除了响应压力变化,还有定时器监控线圈状态。如果有人手动操作了AutoChamberValve或ManualChamberValve,服务会检测到并纠正:
private async void OnCoilMonitorTick(object state)
{
if (_isApplyingControl) return; // 自己写入时跳过
var autoValveActual = _autoValveCoil.IsActive();
if (autoValveActual != autoValveAutoEnable)
{
_logger.Warn($"AutoChamberValve被外部修改: 期望{autoValveAutoEnable}, 实际{autoValveActual}, 正在纠正");
await WriteCoilValueAsync(_autoValveCoil, _autoValveCoil.GetWriteValue(autoValveAutoEnable));
}
}
_isApplyingControl 标志位防止自己写入时触发监控纠正,形成死循环。

三大触发时机
安全服务的启动有三个入口:
1. 应用启动:App.xaml.cs里延迟2秒自动启动
2. 设备连接:设备连上后检查是否需要启动安全服务
3. 配置保存:操作员修改配置后立即响应
// DeviceCmdService中的设备连接回调
private void OnDeviceStatusChanged(object sender, DeviceStatusEventArgs e)
{
if (e.DeviceType == DeviceType.DigitalIO && e.IsConnected)
{
// DigitalIO连接了,检查Chamber控制服务
CheckAndStartChamberSafetyService();
}
}

安全监控踩坑清单
| 坑 | 现象 | 解决 |
|---|---|---|
| 跨线程写入 | InvalidOperationException | 绕过Command直接调API |
| 传感器故障 | 读数卡0继续抽真空 | IsDataValid=false时按不安全处理 |
| 外部篡改 | 手动操作后状态不一致 | 定时器监控+纠正 |
| 配置不生效 | 改了配置要重启 | 消息机制实时响应 |
| 死循环 | 监控纠正触发监控 | _isApplyingControl标志位 |
本篇小结
| 知识点 | 关键做法 |
|---|---|
| 双重监控 | 腔体压力 + 泵口压力独立监控 |
| 三层判断 | 配置启用→数据有效→压力达标 |
| 数据无效 | 按最安全状态处理,不放任 |
| 跨线程 | 绕过Command直接WriteCoilValueAsync |
| 线圈监控 | 定时检测外部篡改并纠正 |
| 三入口 | 启动/设备连接/配置保存 |
安全监控的核心原则:不知道状态时,按最危险的情况处理。
下期预告
第14篇:配置系统基础
安全服务靠配置驱动,下篇讲配置系统——INI文件、JSON配置、热加载机制怎么设计。
:安全监控服务——压力超标了怎么办&spm=1001.2101.3001.5002&articleId=163967038&d=1&t=3&u=6d966100f08f4a3e81b9b7f4deffae6e)
5534

被折叠的 条评论
为什么被折叠?



