报警功能做得最失败的形态,不是报不出来,而是报得太多,多到没人看。我们接手过一个改造项目:一个班 8 小时,报警列表滚了 1400 多条,其中 1300 条是同一点位反复触发又恢复的"闪烁报警",操作工早把报警声音当背景噪音了。报警管理的目标不是"全报",而是"报得准、报得稳、报得有人管"。这篇把我们在用的那套机制——分级、防抖、确认、搁置——完整讲一遍。
报警不是布尔量:一条报警的完整生命周期
很多人把报警做成"条件为真就弹一条",于是一个抖动信号一分钟能刷出几十条记录。正确的做法是给每条报警建一个状态机,从触发到归档走完整个生命周期:
这里最关键的一对概念是"确认"和"清除"分离:确认只代表"人看到了",清除才代表"事过去了"。一条报警可能恢复了但没人确认(要追责),也可能确认了但迟迟不恢复(要跟踪处理进度),两种情况都必须留在活动列表里。把这两件事混成一个按钮,是报警逻辑里最常见的错误。
三级够用,别搞五级八级:报警分级怎么定
分级不是越多越好。级别一多,定义就模糊,最后谁也不知道该选哪级。我们固定用三级,判断标准就一条——这条报警需要人多快做出反应:
提示级(蓝)
需要知道,不需要动手。如:批次完成、配方切换、门打开。只进列表,不响铃,不弹窗。
警告级(黄)
参数偏离但设备还能跑。如:温度接近上限、料位偏低。响铃一声,列表闪烁,要求当班内确认。
紧急级(红)
必须立即处理,通常伴随设备停机或安全动作。如:超温联锁、急停触发、通信全断。持续响铃+弹窗置顶,直到确认。
以一条热处理线为例,报警定义表长这样——每个点位一行,量程、级别、防抖参数全写死在配置里,不允许代码里散落硬编码:
| 点位 | 报警描述 | 条件 | 级别 | 触发防抖 | 恢复延迟 | 联动动作 |
|---|---|---|---|---|---|---|
| 一区温度 | 一区超温 | > 405 ℃ | 紧急 | 连续3周期 | 5 s | 切断加热输出 |
| 一区温度 | 一区温度偏高 | > 390 ℃ | 警告 | 连续5周期 | 10 s | — |
| 真空度 | 真空度不足 | < 0.05 MPa | 警告 | 连续5周期 | 10 s | — |
| 冷却水压 | 冷却水压低 | < 0.2 MPa | 紧急 | 连续2周期 | 5 s | 停炉并报警 |
| 通信状态 | PLC通信中断 | 心跳丢失 | 紧急 | 3 s 无响应 | — | 画面置灰禁操作 |
| 炉门信号 | 炉门打开 | = 1 | 提示 | 无 | — | — |
▲ 同一个点位可以挂多条不同阈值的报警(超温紧急 + 偏高警告),这是常规做法;但同一条件不要重复挂两条,那是报警泛滥的源头之一。
闪烁报警怎么治?防抖、回差、搁置三板斧
治报警泛滥,我们按顺序上三招:
第一招,触发防抖。模拟量在阈值附近抖动时,要求"连续 N 个采集周期都越限"才触发,N 按信号波动程度配 2~10。开关量信号(如接近开关)同样要防抖,机械触点抖一下就是两条报警。
第二招,恢复回差。温度设 400℃ 报警,掉到 399.9℃ 就恢复、399.8 又触发?加回差:触发阈值 400,恢复阈值 395,中间这 5 度是缓冲区。模拟量报警不做回差,等于把防抖白做了。
第三招,搁置(Shelving)白名单。有些报警在特定工况下就是正常的——比如设备检修时"通信中断"必然触发、升温阶段"温度偏低"必然出现。给这类点位做带时限的搁置:操作员可以手动搁置一条报警 30 分钟,到期自动恢复,搁置动作本身记入审计日志。注意搁置不是删除,紧急级报警我们一律不允许搁置,只能走停机流程。
报警引擎怎么写?C# 代码骨架
报警判断跑在采集线程里,但触发、确认、归档走事件队列,避免 UI 卡顿。核心判断逻辑的骨架:
public void Evaluate(AlarmDef def, double value) { var st = _states[def.Id]; // 每条报警一个状态对象 bool cond = def.Type == AlarmType.High ? value > (st.Active ? def.Limit - def.Hysteresis : def.Limit) : value < (st.Active ? def.Limit + def.Hysteresis : def.Limit); // 活动态用带回差的阈值判断,天然防抖 if (cond) { if (++st.HitCount >= def.DebounceN && !st.Active) { st.Active = true; st.TriggerTime = DateTime.Now; st.Value = value; _queue.Enqueue(AlarmEvent.Raise(def, st)); // 入库+响铃+上屏 } } else { st.HitCount = 0; if (st.Active && ++st.RecoverCount >= def.RecoverN) { st.Active = false; st.RecoverTime = DateTime.Now; _queue.Enqueue(AlarmEvent.Recover(def, st)); // 已确认且已恢复 → 从活动列表清除并归档 if (st.Acked) _queue.Enqueue(AlarmEvent.Clear(def, st)); } } }
几个实现细节:报警定义从数据库或 XML 加载,改阈值不改代码;活动报警列表用绑定集合直接投到 WinForm 的 DataGridView,红色行置顶;确认操作要记录操作员账号——制药食品行业这是合规项,其他行业留着也是扯皮时的证据。
收尾:声光提示别帮倒忙
最后说下报警的"表达层"。蜂鸣器策略我们固定为:紧急级持续响直到确认,警告级响三声即停,提示级不响。最忌讳的是所有报警一个响法——响得越勤,人越麻木。画面上的报警栏永远显示"未确认数量"而不是总数,这个数字才是操作员真正需要盯的。
如果你手上的系统报警列表已经没人看了,建议今天就做一件事:导出最近一周的报警记录,按点位统计频次,把前三名高频报警的防抖和回差配上。通常这一步就能砍掉七八成的噪音。
