系统状态流转图

如果没有 状态机 的逻辑,分析仪就是一堆乱飞的继电器和乱跳的数字。状态机逻辑不仅决定了仪器怎么动,更决定了环保局收到的数据上打什么“标记”。例如:反吹时数据不能算超标,必须打上“保持 (Hold)”标记。

一、 CEMS 系统状态流转图

下面这张图展示了一个典型的完全抽取式 CEMS 系统(含反吹、自动校准功能)的软件逻辑

stateDiagram-v2 %% 定义全局样式 classDef measure fill:#90EE90,color:black,stroke:#006400,stroke-width:2px; classDef maintain fill:#FFD700,color:black,stroke:#DAA520,stroke-width:2px; classDef fault fill:#FF6347,color:white,stroke:#8B0000,stroke-width:2px; classDef init fill:#D3D3D3,color:black,stroke:#696969; %% --- 初始状态 --- [*] --> WARMUP : 上电启动 %% 1. 预热状态 state "预热/自检 (Warm-up)" as WARMUP WARMUP : 动作:加热炉/探头升温 WARMUP : 数据:无效 (Invalid) WARMUP : 泵:关闭 WARMUP --> MEASURE : 温度达标 && 无故障 WARMUP --> FAULT : 自检失败 (如传感器断路) %% 2. 测量主循环 state "正常测量 (Measurement)" as MEASURE class MEASURE measure MEASURE : 动作:采样阀开,泵开 MEASURE : 数据:正常输出 (Valid) MEASURE : 逻辑:实时计算浓度 %% --- 周期性任务 (中断测量) --- %% 3. 反吹流程 state "系统反吹 (Blowback)" as PURGE_CYCLE { state "高压反吹 (Purging)" as BLOW state "样气置换/恢复 (Recovery)" as RECOV BLOW --> RECOV : 反吹时间到 (如30s) BLOW : 动作:反吹阀开,采样泵停/隔离 RECOV : 动作:切回采样,等待气体稳定 } class PURGE_CYCLE maintain MEASURE --> PURGE_CYCLE : 定时器触发 (如每4小时) PURGE_CYCLE --> MEASURE : 恢复时间到 (如120s) %% 4. 校准流程 state "校准 (Calibration)" as CALIBRATION { state "校零 (Zero Cal)" as ZERO state "校标 (Span Cal)" as SPAN ZERO --> SPAN : 零点成功 -> 切标气 ZERO : 动作:切零气阀 SPAN : 动作:切标气阀 } class CALIBRATION maintain MEASURE --> CALIBRATION : 用户指令 或 定时触发 CALIBRATION --> MEASURE : 校准完成/退出 %% 5. 故障状态 state "系统故障 (System Fault)" as FAULT class FAULT fault FAULT : 触发条件:温度低、流量低、通讯断 FAULT : 动作:切断加热、停泵 (保护) FAULT : 数据:故障标记 (Fault Flag) %% 全局故障中断 MEASURE --> FAULT : 发生故障 PURGE_CYCLE --> FAULT : 发生故障 CALIBRATION --> FAULT : 发生故障 FAULT --> WARMUP : 故障复位/重启 FAULT --> MEASURE : 故障清除 (非致命) %% 数据标记说明 note right of PURGE_CYCLE 关键逻辑: 进入非测量状态时, 数据必须 "保持 (Hold)" 不能输出 0 或 20.9% end note

二、 深度解剖:状态背后的“潜规则”

看懂了图,我们还要看懂图背后的工程逻辑。这是写 PLC 程序或嵌入式代码的核心。

1. 预热状态 (Warm-up) —— 严厉的门卫

为什么不能一上电就测量?

  • 物理原因: 钼炉没到 315°C,$\text{NO}_2$ 转换效率极低;氧化锆没到 700°C,内阻无穷大;光具座没恒温,漂移巨大。
  • 软件逻辑: 这是一个 While Loop。只要温度(Temp)不在设定范围内(比如 $Set \pm 5^{\circ}C$),系统死锁,禁止开启采样泵。
    • 防止冷凝水吸入冷光路,这是保护仪器的第一道防线

2. 反吹与恢复 (Blowback & Recovery) —— 容易被遗忘的“死时间”

新手写程序,往往只写“反吹动作”。

  • 错误逻辑: 反吹阀关 $\rightarrow$ 马上输出数据。
  • 后果: 反吹时管路里全是压缩空气($\text{O}_2 \approx 21%, \text{SO}_2 = 0$)。反吹刚结束那几十秒,样气还没抽上来。如果这时候输出数据,DCS 会记录到一个“排放跌零”的异常数据。
  • 正确逻辑: 必须加一个 Recovery Time (恢复时间)
    • 反吹 10 秒。
    • 等待 120 秒(让样气置换掉管路里的空气)。
    • 这 130 秒内,数据全部打上“保持 (Hold)”标记,DCS 维持显示反吹前一刻的数值。

3. 故障状态 (Fault) —— 优先级最高的中断

注意看图中的红线,故障是可以从任何状态切入的。

  • 安全逻辑:
    • 如果在“校准”时,突然检测到伴热管线温度低(可能断了),必须立即强行中断校准,跳到 Fault 状态,并关闭采样泵
    • 为什么?因为低温会导致结露,含水的酸性气体吸进来会腐蚀分析仪。保命优先于干活。

4. 测量状态 (Measure) —— 唯一的“有效数据”时刻

只有在这个状态下,软件输出的状态位(Status Bit)才是 “Valid” (有效)

  • 环保局的数采仪(DAS)只统计标记为 Valid 的数据来计算日均值。
  • 如果你的仪器老是跳到故障或反吹,有效数据率不足 75%,企业就会面临环保处罚。

从“图”到“码”的映射法则

在工业控制领域,只要你的状态机(State Machine)设计得足够完善,将它“翻译”成代码,确实就是一个非常机械、甚至可以自动化的过程。

如果我们以前面的 CEMS 状态图为例,写成 C 语言(单片机)或者 ST 语言(PLC),几乎是一一对应的:

状态图要素 (Mermaid) 代码要素 (C/C++) 例子
圆角矩形 (State) Case 分支 case MEASURE:
箭头 (Transition) If 判断 if (Temp > 300) …
文本描述 (Action) 函数调用 OpenValve();
状态变量 枚举 (Enum) enum {WARMUP, MEASURE…}

为什么说“其实就比较简单”?

因为状态机图帮你解决了编程中最让人头秃的三个问题:

  1. 互斥问题:
    • 没图时:你可能会担心“反吹的时候会不会同时在测量?”
    • 有图后:不可能。因为 case BLOWBACKcase MEASURE 是互斥的,程序永远只能跑在一个分支里。
  2. 死锁问题:
    • 没图时:容易出现“卡在校准里出不来”。
    • 有图后:你会盯着图看,发现“校准”状态必须有一条箭头指出去(比如超时退出),否则图就是断的。图没断,代码就不会死锁。
  3. 优先级问题:
    • 没图时:故障发生了,但程序还在跑校准。
    • 有图后:你在图上画了 FAULT 可以从任何地方切入,写代码时就会把 if (Error) 放在最外层,优先级自然就清楚了。

既然这么简单,难点剩下什么?

虽然**“逻辑流”(骨架)简单了,但作为 CEMS 专家,我要提醒你,在转换成代码时,真正的难点转移到了“血肉”(底层驱动)上。

1. 动作函数的实现 (The Drivers)

状态机里写一句 OpenValve(V1) 很简单。但在代码里,你得去写这个函数:

  • GPIO 是哪个引脚?
  • 是高电平开还是低电平开?
  • 有没有防抖动处理?
  • 这部分是“脏活累活”,状态图帮不了你,得查硬件原理图。

2. 非阻塞计时 (Non-blocking Timing)

这是一个大坑。

  • 状态图上写:“等待 120 秒”。
  • 新手写代码: delay(120000); (死等)。
    • 后果:这 120 秒内,CPU 睡着了,按键没反应,串口不通讯,看门狗可能会叫,程序会崩溃。
  • 高手写代码:必须使用“时间戳轮询”。你需要构建一个软件定时器架构,这比画图要难一些。

3. 故障检测的灵敏度

  • 状态图上写: “温度低 $\rightarrow$ 故障”。
  • 实际代码:
    • 是一瞬间低于 315°C 就报警吗?(那风一吹就误报了)。
    • 还是连续 10 秒低于 315°C 才报警?
    • 这需要**“滤波算法”,这也是图上体现不出来的细节。

终极境界:代码是“生成”出来的

正如我之前提到的,在现代高端工程中(如汽车电子、航空、高端医疗仪器),工程师甚至不写这部分 C 代码。

他们使用 Model-Based Design (MBD) 工具(如 MATLAB/Simulink 或 现在的 AI 辅助编程):

  1. 在软件里画好你刚才看到的那个 Mermaid 状态图。
  2. 点击 “Generate Code” 按钮。
  3. 软件自动生成几千行完美无缺、符合工业标准(MISRA-C)的 C 代码。
  4. 工程师只需要负责画图和写底层驱动。
Licensed under CC BY-NC-SA 4.0