从零搭建工业控制系统(十二):灯塔与蜂鸣器——工业界的仪表盘

从零搭建工业控制系统(十二):灯塔与蜂鸣器——工业界的仪表盘 灯塔与蜂鸣器工业界的仪表盘这是「从零搭建工业控制系统」系列第12篇。阀门控制聊完了这篇说灯塔。工厂车间里抬头就能看到的那根三色灯柱比任何弹窗都管用。为什么不用弹窗做软件开发的人有个本能反应出问题了弹个MessageBox。但在工厂环境操作员可能戴着防尘手套站在2米外的操作台前根本看不清屏幕上的弹窗。灯塔不一样。红灯亮了蜂鸣器响了隔着车间都能看到听到。不需要操作员盯着屏幕。项目里用了一根五段式灯塔蓝、绿、橙、红四个LED加一个蜂鸣器分别对应五个数字输出点DO点位号视硬件配置而定。灯塔规则定义先明确每个灯代表什么输出点颜色含义触发条件DO_蓝蓝灯通信连接上位机与下位机连接时亮DO_绿绿灯运行中Sequence运行时亮(RunState1)DO_橙橙灯完成Sequence运行完成时亮(RunState: 1→0)DO_红红灯报警AlarmState发生报警时亮(AlarmState!0)DO_蜂蜂鸣器声音报警橙灯或红灯亮时响5秒这些规则直接写在TowerLightService的注释里作为需求文档/// summary/// 灯塔控制服务/// 灯塔规则/// - 蓝灯: 上位机与下位机连接时亮灯断开时不亮/// - 绿灯: Sequence运行时亮灯(RunState1)其他时候不亮/// - 橙灯: Sequence运行完成时亮灯(RunState: 1→0)保持到下次运行开始/// - 红灯: AlarmState发生报警时亮灯(AlarmState!0)报警解除后熄灭/// - 蜂鸣器: 橙灯亮或红灯亮时响5秒/// /summary服务架构TowerLightService用500毫秒的定时器轮询系统状态自动控制灯塔publicpartialclassTowerLightService:ObservableObject,IDisposable{privateconstintMONITOR_INTERVAL_MS500;privateconstintBUZZER_DURATION_MS5000;privatereadonlyDeviceCmdService_deviceCmdService;privatereadonlyChamberSubsystemStatus_status;privatereadonlyChamberCoilStatusHelper_coilStatusHelper;privatereadonlyTimer_monitorTimer;privateTimer?_buzzerTimer;// 状态追踪privateint_previousAlarmState;privateint_previousRunState;privatebool_sequenceCompleted;// 橙灯保持标志// 线圈缓存privateModels.ModbusCoil?_buzzerCoil;privateModels.ModbusCoil?_blueLedCoil;privateModels.ModbusCoil?_greenLedCoil;privateModels.ModbusCoil?_orangeLedCoil;privateModels.ModbusCoil?_redLedCoil;}每个灯和蜂鸣器都对应一个ModbusCoil对象初始化时通过枚举获取privatevoidInitializeCoils(){_buzzerCoil_deviceCmdService.GetCoilByEnum(DeviceType.DigitalIO,DigitalDOCodes.Buzzer);_blueLedCoil_deviceCmdService.GetCoilByEnum(DeviceType.DigitalIO,DigitalDOCodes.BlueLed);_greenLedCoil_deviceCmdService.GetCoilByEnum(DeviceType.DigitalIO,DigitalDOCodes.GreenLed);_orangeLedCoil_deviceCmdService.GetCoilByEnum(DeviceType.DigitalIO,DigitalDOCodes.OrangeLed);_redLedCoil_deviceCmdService.GetCoilByEnum(DeviceType.DigitalIO,DigitalDOCodes.RedLed);}用枚举而不是硬编码地址换硬件时只改枚举映射不用改业务代码。状态更新逻辑定时器回调里更新所有灯的状态privateasyncTaskUpdateTowerLightStateAsync(){if(!IsDeviceConnected){_logger.Debug(DigitalIO设备未连接跳过灯塔状态更新);return;}// 1. 蓝灯连接状态varisConnected_deviceCmdService.DigitalIO?.IsConnected??false;if(BlueLedOn!isConnected){awaitSetBlueLedAsync(isConnected);}// 2. 获取当前报警状态// 合并硬件报警(DI)与软件追踪报警varhwAlarmState_coilStatusHelper.CalculateTriggerStatus();varswAlarmState_status.TriggerStatus;varcurrentAlarmState(hwAlarmStateTriggerStatus.Alarm||swAlarmStateTriggerStatus.Alarm)?(int)TriggerStatus.Alarm:(int)TriggerStatus.NoAlarm;// 3. 红灯报警状态// 4. 绿灯/橙灯运行状态// 5. 蜂鸣器橙灯或红灯亮时触发}红灯的逻辑有个细节硬件报警和软件追踪报警要合并。任何一个触发红灯都亮。这俩来源不同——硬件报警来自DI输入软件追踪报警来自TraceMonitorService写入的状态。橙灯的保持逻辑橙灯表示完成有个特殊逻辑Sequence从运行状态(RunState1)变回空闲(RunState0)时亮一直保持到下次运行开始。// 检测RunState从1→0的跳变if(_previousRunState(int)RunStatus.RunningcurrentRunState(int)RunStatus.Idle){_sequenceCompletedtrue;awaitSetOrangeLedAsync(true);}// 下次运行开始时熄灭橙灯if(currentRunState(int)RunStatus.Running){_sequenceCompletedfalse;awaitSetOrangeLedAsync(false);}用_sequenceCompleted标志位追踪状态避免重复设置。蜂鸣器定时关闭蜂鸣器不能一直响响5秒自动关。用一个单独的定时器publicasyncTaskTriggerBuzzerAsync(intdurationMs5000){awaitSetBuzzerAsync(true);_buzzerTimer?.Dispose();_buzzerTimernewTimer(async_{awaitSetBuzzerAsync(false);},null,durationMs,Timeout.Infinite);}蜂鸣器持续时间从配置文件读取privatevoidLoadConfiguration(){variniConfigConfigReader.Instance.ReadConfig();IsEnablediniConfig.TowerLightServiceEnable;BuzzerDurationSecondsiniConfig.BuzzerDurationSeconds0?iniConfig.BuzzerDurationSeconds:5;}Config.ini 里的配置[TowerLight] TowerLightServiceEnable True BuzzerDurationSeconds 5UI状态绑定所有灯的状态都是[ObservableProperty]直接绑定到UI[ObservableProperty]privateboolblueLedOn;[ObservableProperty]privateboolgreenLedOn;[ObservableProperty]privateboolorangeLedOn;[ObservableProperty]privateboolredLedOn;[ObservableProperty]privateboolbuzzerOn;界面上用圆形图标表示灯塔状态操作员一眼就能看到当前系统处于什么状态。手动控制除了自动模式还支持手动控制方便测试publicasyncTaskManualControlAsync(boolblue,boolgreen,boolorange,boolred,boolbuzzer){awaitSetBlueLedAsync(blue);awaitSetGreenLedAsync(green);awaitSetOrangeLedAsync(orange);awaitSetRedLedAsync(red);awaitSetBuzzerAsync(buzzer);}停止时全部关闭服务停止时关掉所有灯和蜂鸣器避免灯一直亮着误导操作员publicvoidStopMonitoring(){_monitorTimer.Change(Timeout.Infinite,Timeout.Infinite);_buzzerTimer?.Change(Timeout.Infinite,Timeout.Infinite);_TurnOffAllAsync();IsRunningfalse;}灯塔踩坑清单坑现象解决初始化误触发启动时红灯闪一下用安全默认值初始化_previousAlarmState蜂鸣器一直响没有关闭定时器单独Timer定时关闭橙灯不保持完成后马上灭_sequenceCompleted标志位设备断开还在写写入失败刷日志IsDeviceConnected检查硬件软件报警没合并软件报警红灯不亮hwAlarmState和swAlarmState取或本篇小结知识点关键做法五路输出蜂鸣器 四色LED蓝绿橙红轮询监控500ms Timer定时更新状态合并硬件报警 软件追踪报警取或橙灯保持标志位追踪下次运行才灭蜂鸣器定时单独Timer 5秒后自动关闭配置化Config.ini控制启用和蜂鸣时长灯塔的价值在于不需要操作员看屏幕一眼就知道系统状态。下期预告第13篇安全监控服务灯塔是显示安全状态下篇讲执行安全动作——压力超标自动关阀门、断电自动启动保护。