设备软件架构与控制模型(六):故障处理、报警与安全恢复

设备现场最常见的“恢复方案”是弹窗、重试和重启:捕获异常后提示用户,用户点击确定,程序再发一次命令;仍然失败就重启软件。这种做法偶尔能恢复通信,却无法证明机械位置、物料状态和安全条件已经恢复,反而可能覆盖最有价值的故障证据。

可靠的故障处理不是一个 catch 代码块,而是一条从检测、隔离、报警到恢复验证的闭环。本文讨论通用方法,不替代具体设备的风险评估、安全标准和操作规程;半导体设备异常恢复的领域实践见《半导体设备软件(六):实时性、并发与异常恢复》。

1. 事件、提示、报警和故障不要混用

系统中出现的每条消息并不都需要操作员处理:

  • 事件(Event):值得记录的事实,例如连接建立、Recipe 激活、任务完成;
  • 提示(Notification):需要了解但通常不要求立即行动的信息,例如维护周期临近;
  • 报警(Alarm):需要人在规定时间内采取行动的异常条件;
  • 故障(Fault):设备或软件无法满足预期能力的状态,可能触发报警和联锁;
  • 日志(Log):供诊断使用的技术记录,数量通常远多于报警。

一次相机断连是故障;系统可以产生一条报警提醒操作员,并写入多条重连和驱动日志。若把每次重试都弹成报警,真正需要行动的信息会被报警洪泛淹没。

ISA-18.2 面向过程工业的报警管理,但其中的生命周期思想同样值得设备软件借鉴:报警需要经过识别、合理化、详细设计、运行监测和变更管理等阶段,而不是开发者看到异常就随手加一个红色弹窗。离散设备采用时仍应结合自身行业和风险边界。

2. 故障模型同时保存判断和证据

一条可操作的故障记录应包含稳定分类、来源、严重级别、时间和原始证据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
public enum FaultSeverity
{
    Warning,
    Recoverable,
    Critical
}

public sealed record EquipmentFault(
    string FaultId,
    string Source,
    string Kind,
    FaultSeverity Severity,
    string OperatorMessage,
    string? VendorCode,
    DateTimeOffset OccurredAt,
    string CorrelationId,
    bool RequiresAcknowledgement,
    bool IsLatched);

注意"严重级别"和"可恢复性"其实是两个维度:Warning/Critical 描述影响程度,Recoverable 描述恢复方式。示例为保持简单混用在一个枚举里;生产代码中若两个维度都需要独立决策,应拆成两个属性分别建模。

Kind 应是软件长期维护的稳定分类,例如 CommunicationLostInterlockOpenPositionUnknownVendorCode 保留厂商证据。面向操作员的消息说明“发生什么、影响什么、建议做什么”,技术堆栈和原始报文进入诊断记录,而不是全部塞进弹窗。

严重级别不应只根据异常类型决定。同一个读取超时,在非关键温度趋势采样中可能只是降级,在等待夹具到位的步骤中却可能使物料状态未知。

3. 先判断物理副作用,再决定重试

网络和 SDK 调用失败时,最重要的问题不是“能否再试一次”,而是第一次操作有没有发生:

结果例子处理重点
明确未执行本地参数校验失败修正输入后可以重新提交
明确已完成控制器返回完成并通过回读确认不应重复执行
执行中断移动过程中驱动报警先停止并确认位置
结果未知命令发出后连接断开先查询或进入人工确认,不能盲重试

读取状态通常比改变物理世界的命令更容易安全重试,但也要考虑设备和总线负载。写命令只有在协议提供幂等键、序列号或可查询结果时,才能可靠地去重。没有这些能力时,自动重试可能造成重复运动、重复出料或覆盖数据。

4. 恢复是一台独立状态机

不要在各层 catch 中零散地写复位命令。恢复流程本身应建模并保留检查点:

1
2
3
4
5
6
7
Detect
  → Contain(阻止新命令、隔离故障设备)
  → ReachSafeState(请求受控停止或依赖安全链)
  → Diagnose(采集状态、错误码和上下文)
  → Correct(重连、复位、重新初始化或人工维修)
  → Verify(回读、联锁、回零和试运行检查)
  → Resume / Abort

“复位命令返回成功”只是 Correct 阶段的一步。只有 Verify 重新证明必要条件,状态机才可以从 Faulted 回到 Ready。确认报警也只是说明操作员已经看见,不应自动清除故障条件。

恢复步骤需要声明:

  • 是否自动执行,还是必须授权人员确认;
  • 可执行的设备状态和安全前提;
  • 超时及失败后的终止状态;
  • 是否幂等,重复进入会不会造成额外动作;
  • 成功判据采用什么独立回读;
  • 是否需要重新回零、重新校准或报废当前物料。

5. 安全动作必须有独立保证

应用层的异常处理无法替代安全功能。GC 暂停、线程阻塞、操作系统故障和进程崩溃都可能让上位机不能及时执行。涉及人身或设备危险的联锁、急停和能量切断,需要按风险评估放在具备相应安全完整性的硬件、安全 PLC、驱动器或回路中。

上位机的责任是:不绕过安全条件、准确显示安全链状态、在触发后停止业务流程、保存上下文,并引导受控复位。软件中的 try/finally 可以释放资源,却不能证明继电器已经断开或气缸已经回到安全位置。

6. 降级比“全好或全坏”更实用

并非所有故障都要求整机停机。诊断相机不可用时,可以禁用高级诊断但保留手动维护;非关键温度传感器失效时,可以降低速度并禁止自动生产;关键联锁失效则必须阻止危险动作。

降级模式必须显式定义:

  • 哪些功能仍可使用;
  • 性能或质量承诺如何变化;
  • 操作员需要看到什么持续提示;
  • 多久后必须升级为停机;
  • 恢复到正常模式需要哪些验证。

不要在异常处理中临时“忽略一次”,这会形成未记录的隐式降级。

7. 抑制报警洪泛,但不抹掉事实

断开一根总线可能让十个从设备同时报错。如果每个轮询周期都创建新报警,操作员无法找到根因。可以采用:

  • 相同来源和故障键合并为一条活动报警;
  • 记录首次发生、最近发生和重复次数;
  • 基于因果关系抑制下游派生报警;
  • 对抖动信号设置经过工程确认的延时和死区;
  • 恢复后保留历史,而不是删除记录;
  • 定期统计报警频率、持续时间和洪泛区间。

抑制只改变呈现和通知,不应删除底层事件。关键报警的停用、阈值和优先级变更也应纳入审计和测试。

8. 用故障注入验证恢复路径

正常路径跑一百次,不能证明恢复路径可靠。模拟器和硬件在环(Hardware-in-the-Loop,HIL)环境应能够注入:

  • 命令发送前、发送后和完成确认前断连;
  • 状态长时间不变化;
  • 迟到、重复或乱序响应;
  • 传感器抖动和不合理组合;
  • 磁盘写满、日志后端不可用;
  • 进程在检查点前后终止。

验证的不只是“抛出了预期异常”,还包括设备最终状态、是否继续接受命令、证据是否完整、报警是否可操作,以及重启后是否拒绝从未知状态继续。

总结

可靠故障处理先区分事件、报警和故障,再根据物理副作用判断能否重试。恢复是一条带安全前提、检查点和成功判据的状态机;确认消息、清除报警和修复故障是不同动作。安全链负责最后保障,上位机负责不绕过它并保存足够证据。

下一篇将搭建从纯软件模拟器到 HIL 的测试梯度,说明如何模拟时间、失败和设备动态,而不是只让 Mock 永远返回成功。

参考资料

Licensed under CC BY-NC-SA 4.0