报警功能做得最失败的形态,不是报不出来,而是报得太多,多到没人看。我们接手过一个改造项目:一个班 8 小时,报警列表滚了 1400 多条,其中 1300 条是同一点位反复触发又恢复的"闪烁报警",操作工早把报警声音当背景噪音了。报警管理的目标不是"全报",而是"报得准、报得稳、报得有人管"。这篇把我们在用的那套机制——分级、防抖、确认、搁置——完整讲一遍。

报警不是布尔量:一条报警的完整生命周期

很多人把报警做成"条件为真就弹一条",于是一个抖动信号一分钟能刷出几十条记录。正确的做法是给每条报警建一个状态机,从触发到归档走完整个生命周期:

① 触发条件成立+防抖连续 N 个周期越限才触发,滤掉毛刺
② 活动未确认/已确认条件仍在,操作员点确认后停止闪烁但保留红色
③ 恢复条件消失+延迟回到正常且持续几秒,状态转"已恢复"
④ 清除恢复+已确认两个条件都满足才从活动列表消失
⑤ 归档落库存历史触发时刻、恢复时刻、确认人、确认时间全记录

这里最关键的一对概念是"确认"和"清除"分离:确认只代表"人看到了",清除才代表"事过去了"。一条报警可能恢复了但没人确认(要追责),也可能确认了但迟迟不恢复(要跟踪处理进度),两种情况都必须留在活动列表里。把这两件事混成一个按钮,是报警逻辑里最常见的错误。

三级够用,别搞五级八级:报警分级怎么定

分级不是越多越好。级别一多,定义就模糊,最后谁也不知道该选哪级。我们固定用三级,判断标准就一条——这条报警需要人多快做出反应

提示级(蓝)

需要知道,不需要动手。如:批次完成、配方切换、门打开。只进列表,不响铃,不弹窗。

警告级(黄)

参数偏离但设备还能跑。如:温度接近上限、料位偏低。响铃一声,列表闪烁,要求当班内确认。

紧急级(红)

必须立即处理,通常伴随设备停机或安全动作。如:超温联锁、急停触发、通信全断。持续响铃+弹窗置顶,直到确认。

以一条热处理线为例,报警定义表长这样——每个点位一行,量程、级别、防抖参数全写死在配置里,不允许代码里散落硬编码:

点位报警描述条件级别触发防抖恢复延迟联动动作
一区温度一区超温> 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 分钟,到期自动恢复,搁置动作本身记入审计日志。注意搁置不是删除,紧急级报警我们一律不允许搁置,只能走停机流程。

报警泛滥的验收指标:行业里有个经验值——单个操作员每 10 分钟活动报警不超过 6 条,超过就说明报警配置失控。上线前拿历史数据跑一遍模拟触发,把高频闪烁的点位先治完再交付,比上线后被投诉强得多。

报警引擎怎么写?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,红色行置顶;确认操作要记录操作员账号——制药食品行业这是合规项,其他行业留着也是扯皮时的证据。

收尾:声光提示别帮倒忙

最后说下报警的"表达层"。蜂鸣器策略我们固定为:紧急级持续响直到确认,警告级响三声即停,提示级不响。最忌讳的是所有报警一个响法——响得越勤,人越麻木。画面上的报警栏永远显示"未确认数量"而不是总数,这个数字才是操作员真正需要盯的。

如果你手上的系统报警列表已经没人看了,建议今天就做一件事:导出最近一周的报警记录,按点位统计频次,把前三名高频报警的防抖和回差配上。通常这一步就能砍掉七八成的噪音。