本节摘要:状态机把一个工位的行为组织成有限的状态集合与明确的状态转移规则:任一时刻只处在一个状态,条件满足则迁移。本节用青线灌装头完整演示状态机的设计与CASE实现,重点讲异常态设计与状态回退——这两处才是状态机真正防事故的地方。
4.1节的顺序启动链已经露出状态机的雏形:步序计数器加转移条件。当一个工位的动作多起来、分支复杂起来,散落的IF语句就会互相纠缠——这一节把那套雏形升级成完整的工程方法。它是青线灌装区稳定性最高的功臣,也是代码评审时最能一眼看出功力的地方。
灌装头是青线心思最密的工位:待命时等瓶到位;瓶到位先吹扫零点五秒;然后开阀灌装,灌到目标液位或时限二者先到;灌完稳压零点三秒防滴挂;出瓶的同时判断是否进入剔除窗口;任何环节收不到预期反馈就进故障态,故障态里保持安全输出并等待人工复位。把它画成地图——状态是地点,转移条件是道路——逻辑评审就有了统一的底稿:

状态机的代码实现几乎是填空题——前提是守住书写纪律。青线规范要求:每个状态一个CASE分支,分支内第一件事写本状态的输出组合,第二件事写转移条件;任何输出不允许在两个状态里各自为政地写。配套的还有一条评审技巧:把所有状态的输出组合抄成一张"状态输出矩阵",横向看(同一状态内)查遗漏,纵向看(同一输出跨状态)查冲突——矩阵五分钟扫完,比通读代码快得多。先看骨架:
CASE Filler_State OF IDLE: Valve_Out := FALSE; Purge_Out := FALSE; // 安全组合 IF Bottle_Present AND No_Alarm THEN Filler_State := PURGE; END_IF; FILL: Valve_Out := TRUE; IF Level_Reach OR (T_Fill.Q) THEN // 二者先到 Valve_Out := FALSE; T_Settle(IN := TRUE, PT := T#300MS); Filler_State := SETTLE; ELSIF Fill_Timeout THEN Filler_State := FAULT; // 超时进故障 END_IF; FAULT: Valve_Out := FALSE; // 安全侧输出 IF Manual_Reset AND Bottle_Cleared THEN Filler_State := IDLE; // 只回待命 END_IF; END_CASE;
这条纪律的深意是消除"输出记忆跨状态存活":灌装阀在FILL态置真、离开时显式置假,若哪一步漏写,输出会不会残留在下一个状态就变成了碰运气。状态与输出组合一一对应后,评审者扫一眼每个分支的前几行就知道此刻现场是什么样子——这也是状态机代码比IF嵌套代码易审查的根本原因。初学者常问"状态变量用什么类型":用3.1节说的枚举,状态名即文档,编译器还帮你查非法赋值——比裸整数的状态号安全一个档次。
正常流程的状态机人人会画,工程成色看异常路径。三个设计要点,全部来自青线实践。第一,故障态只进不越:任何故障只允许复位回待命,不允许"故障消除直接续跑灌装"——续跑的前提是瓶位状态可信任,而故障之后没人能担保,回到待命从瓶到位重新开始,损失的只是半秒,换来的是状态可信任。第二,故障要带行李:进故障态时把来源状态、时间戳、关键模拟量快照一并记录,6.4节的诊断工具会感谢这三行代码。第三,清洗等独占模式做成独立状态而非全局标志:独占态从待命进入、回到待命退出,与生产路径互斥关系一目了然,避免"清洗标志位与生产状态打架"这类经典乱局。
核心地图之外,一套成熟的工位状态机还配四件附件,青线把它们做成了标准件。附件一是状态迁移日志:每次迁移记录"从哪到哪、因什么条件、何时",联调期的时序争议靠它一锤定音——工艺说等了五秒,日志说零点四秒,数据面前没有辩论。附件二是状态超时监视:每个工作态声明最长允许驻留时间,超时即告警或进故障——状态机"卡在某一步"是现场最常见的病,超时监视就是它的心电图。附件三是手动步进:维护模式下允许逐步强制迁移,验证每一个状态与动作组合,联锁验证(7.3节)一半的效率提升来自它。附件四是状态回退保护:只允许沿定义好的回退边走(如故障回待命),严禁任意跳转——地图上没有的路,代码里也不能有。
四件附件各只花几十行代码,却把状态机从"能跑的结构"升级成"可诊断、可维护、可验证的结构"。评审一个工位的状态机时,先看附件齐不齐,再看地图对不对——顺序别反。
初学者画状态机最常见的困难是"不知道该有几个状态"。青线教新人用三问法从需求里榨出状态清单。第一问:这个工位在物理上有哪些"姿势"?——待命、吹扫、灌装、稳压,姿势是状态的天然候选。第二问:哪些姿势的输出组合不同?——输出组合相同的机会性合并(如两种等待合并成待命),不同的必须分开,这是状态定义的机械判据。第三问:异常时需要"记忆"什么姿势吗?——故障态要记录来源,清洗态要与生产互斥,这两问逼出隐藏状态。三问过完,状态清单十有八九已经完整,剩下的评审只是微调。
状态数也是个健康指标:单工位状态超过十二个,通常意味着该拆成两个协作的状态机(如机械动作一个、流程审批一个);少于三个,多半用IF更直白,不必强行套状态机。方法的边界感与方法本身同样重要。
复杂工位常需要两个状态机协作:灌装区可以拆成"灌装头状态机"与"瓶流协调状态机",前者管自己的动作时序,后者管瓶位分配与剔除窗口。协作的标准姿势是指令加握手:协调机向灌装头发"允许灌装"指令,灌装头完成或故障后回"完成/故障"握手——两个状态机各自独立跑各自的任务节拍,靠握手信号同步。禁忌是共享内部变量:一台状态机直接读另一台的内部状态做判断,耦合从此失控,任何一方的修改都开始波及对方。
判断要不要拆分,用青线的"心跳测试":两个职责的心跳频率不同就该拆——灌装头随主循环十毫秒心跳,瓶流协调随输送段事件心跳,频率不同写在一起必然互相迁就。拆开之后各自单纯,握手接口成了天然的文档边界。
布尔世界管住了,下一步进入连续变化的世界:模拟量的标定与PID整定。