本节摘要:鲁棒性是程序面对异常输入、异常环境、自身缺陷时的体面:输入做合理性校验,运行做错误捕获,输出默认安全侧,全程留诊断痕迹。本节给出青线验证过的异常四件套与降级策略,让程序"不自己倒下,倒下也倒得明白"。
本章支柱页把试车第一周的故事摆在门口,这一节从第一晚的事故残骸讲起——那段把主循环撑到看门狗边缘的轮询代码,是异常处理反面的活教材。它没有做错任何"计算",它错在假设了环境永远配合:通信永远及时、数据永远合理、自己永远不疏忽。鲁棒性设计就是逐条取消这种天真。
第一件:输入合理性校验。 现场进来的每个值都带着撒谎的可能:传感器断线给负值(4.3节已用断线检测抓过它)、通信对端给的配方号超出库容、模拟量尖峰超出物理可能。青线规矩是凡跨边界的值先验身再入内:范围检查、变化率检查、枚举合法性三道关。看一个真实的防御块:
// 配方号接收防御(来自上位下发) Recipe_Valid := FALSE; IF Recipe_No_In >= 1 AND Recipe_No_In <= 20 THEN // 范围关 IF Recipe_Lib[Recipe_No_In].Checksum_OK THEN // 完整性关 Recipe_Valid := TRUE; ELSE Alarm_Log(Code := 4102, Text := '配方校验和错误'); END_IF; ELSE Alarm_Log(Code := 4101, Text := '配方号越界'); END_IF; IF NOT Recipe_Valid THEN Recipe_No_Cur := Recipe_No_Cur; // 拒收,维持原值 END_IF;
第二件:错误捕获。 程序自身的异常——除零、数组越界、访问失败——不该让CPU直接停机了事。平台都提供错误组织块机制:错误发生时进入指定处理块,你可以记录现场、置故障标志、执行安全动作,然后决定是继续还是停机。青线的策略分级:可恢复错误记入诊断并继续,不可恢复错误走受控停车——停机是最后的体面,不是第一反应。
第三件:输出安全侧。 每个输出都要回答"程序懵了的时候你该是什么状态"。答案写进数据字典:进料阀失电关闭、报警灯失电点亮(常闭语义)、变频器使能失电撤销。所谓安全侧,就是让失电、失效、停机这些异常统统落到物理上安全的状态。4.2节状态机里"故障态输出安全侧"的纪律,正是这一件在工位层的落地。
第四件:全程留痕。 异常发生时要留下三样东西:时间、位置、当时的量值。青线的Alarm_Log函数把这三样连同事件码写进环形日志,同时送6.4节的诊断缓冲。留痕的深意是替六小时后的自己保存现场——排错最贵的成本是"现场已不在"。
四件套管住单点,降级策略管全局。青线的降级路线图按"功能与安全的距离"排布:通信失效时,5.2节埋下的通信健康位翻转,相关输送段按预设策略降速到三分之一;模拟量失效时,对应回路从自动切手动并保持最后安全输出;远程站失联时,该站设备按安全侧输出,其余段维持生产。每条降级路径都要在仿真里走一遍——6.5节的事故复盘会展示"没走过的降级路径"有多危险。
降级设计的常见误区是把降级做成"默默继续":通信断了逻辑还拿着旧数据跑,流量计断了累计还在涨。青线的红线是:任何降级必须显式可见——HMI上有降级图标、日志里有降级事件、恢复时有一条"已恢复正常"的反向记录。让系统诚实,是鲁棒性最深的一层。检验自己系统诚实度的办法很简单:随机挑一个降级状态问操作员——他知道现在系统处于降级吗?知道该做什么吗?两次问不出清醒的答案,降级设计就还没完成。
回到支柱页里那段轮询代码——它在一个循环里等待通信响应标志,通信高峰时等待拉长,主循环周期被顶到看门狗极限。按本节四件套逐条审判:输入无校验(没有对通信超时设上限)、运行无捕获(靠看门狗兜底属于裸奔)、输出无安全侧(超时停机时输出何去何从没定义)、无留痕(停机后只能猜)。重写版把等待改成状态机查询:每拍看一眼标志,不在就干别的去,超时按错误处理——十毫秒的预算从此谁也偷不走。这段代码的完整验尸报告在6.5节,此处先立骨灰龛。
鲁棒性也有反面:防御堆过头,程序变成刺猬,维护者寸步难行。青线用三条原则把握分寸。原则一,防御放在边界,不放进行业逻辑——跨界处(通信、模拟量、人工输入)严防死守,块内部的运算信任已验身的输入,层层重复检查只会淹没真正的告警。原则二,告警要有信噪比——每个防御性告警问一句"谁收到它之后要做什么",答不出的降级为日志不进报警栏;报警栏被垃圾填满的那天,真报警就成了狼来了。原则三,拒收要给出口——输入校验失败后流程怎么走要有定义(维持原值、进安全侧、还是请求人工),只拒不导的防御是把异常延迟放大。
把这三条与四件套合并成一句评审口诀:边界设防、告警有用、拒收有路。新项目的代码评审按这句过一遍异常处理,青线试车第一周那类事故的土壤就基本清除了。
错误捕获机制值得展开一页。平台普遍提供一组错误组织块:程序错误(寻址越界、除零)、同步错误(访问失败)、异步错误(模块故障、电源波动)各有入口。青线的分级策略表挂在编码规范里:可自愈类(如单个模拟量通道瞬时故障)——记录、置标志、下拍重试,不打扰生产;可降级类(如远程站部分通道失效)——触发6.1节的降级路径,产线降速不停机;致命类(安全相关失效、看门狗连发)——受控停车,输出安全侧,等待人工。分级的关键收益是让"程序对错误的反应"变得可预期:评审时对着表问"这个错误进了哪一级",级别不符的当场改。
新手常犯的反向错误是把一切错误都接到致命级——错误处理本身变成了最大的单点。错误的处置深度应该与它威胁的功能等级对齐,这个对齐表每年随报警字典一起复审,是鲁棒性体系里少有的"活文档"。
程序自身站稳了,下一道防线关乎人命:功能安全,急停回路里的工程信仰。