工作流编排系统:把阶段串成流水线
这是「从零搭建工业控制系统」系列第8篇。前两篇讲了配方和序列——配方管参数,序列管编排。但一个完整的生产流程不只是跑序列,还有进料、预热、检测、出料等阶段。这篇讲最外层的工作流编排。
序列不够用吗
序列能编排配方,循环、跳转、暂停取消都有了。为什么还需要更外层?
因为一个完整的生产流程不只是"跑序列"这一件事。
真实场景:操作员放好物料,点"开始",设备要做的远不止跑工艺序列——
- 进料— 检查物料到位、关门、密封
- 预热— 加热器升温到目标温度,等稳定
- 完整性检测— 检查腔体密封性,不漏气才能继续
- 序列执行— 跑工艺序列(这是前面讲的序列层)
- 清理— 安全关闭加工相关设备
- 出料— 泄压、开门、提示取物料
六个阶段,一个接一个。序列层只管第4阶段,其他五个阶段谁管?
如果硬塞进序列层,序列配置里就得混入"进料""出料"这些非工艺步骤。工艺工程师看序列配置时一脸懵——“这个步骤的recipe字段为什么是空的?为什么有个type字段叫load?”
所以需要更外层。工作流层管全局阶段编排,序列层管工艺配方编排,各管各的。
工作流配置
工作流不是JSON配置驱动的——阶段是固定的,不需要工艺工程师改。工作流是代码里定义的流程控制器:
工作流定义(代码): 阶段1: 进料 阶段2: 预热 阶段3: 完整性检测 阶段4: 序列执行(由操作员选哪个序列) 阶段5: 清理 阶段6: 出料为什么工作流不做成配置?因为阶段顺序是固定的工艺逻辑——进料必须在序列执行之前,清理必须在序列执行之后。这不是工艺工程师该改的东西,改了可能出安全事故。
序列和配方做成配置,是因为它们需要频繁调整——换产品、换工艺、调参数。工作流阶段基本不变,做成配置反而增加复杂度。
工作流执行逻辑
执行工作流(序列名): 阶段1: 进料 检查物料到位信号 关闭腔体门 等待密封确认 失败 → 立即停止,报错 阶段2: 预热 启动加热器 等待温度稳定(轮询温度值) 超时未稳定 → 停止,报错 取消 → 跳到清理阶段 阶段3: 完整性检测 执行泄漏测试 判定是否合格 不合格 → 停止,报错 阶段4: 序列执行 从注册表获取序列配置 调用序列执行器 失败 → 跳到清理阶段 阶段5: 清理 执行清理序列(安全关闭设备) 不管前面成功还是失败,这步都要跑 阶段6: 出料 泄压到大气 开门 提示操作员取物料关键设计点:任何阶段失败都跳到清理阶段,而不是直接退出。设备不能停在"加热中"或"真空状态"就不管了,必须安全关闭。
阶段间的依赖与跳转
不是所有阶段都直线往下走。有些阶段有条件分支:
完整性检测: 如果合格 → 继续阶段4 如果不合格 → 跳到清理,然后停止序列执行: 如果成功 → 继续阶段5(清理) 如果取消 → 跳到阶段5(清理) 如果故障 → 跳到阶段5(清理),然后报警不管哪条路径,清理阶段都会执行。这是安全设计——设备永远不能停留在不安全状态。
有个设计决策:清理阶段失败怎么办?理论上清理失败意味着设备无法安全关闭,这是严重故障。实际做法是:清理失败触发最高级别报警,灯塔转红灯+蜂鸣器响,通知操作员手动介入。
工作流与序列的协作
工作流层调用序列层时,需要传递一些上下文:
- 序列名— 操作员在UI上选的
- 取消令牌— 工作流的取消要传播到序列层
- 进度回调— 序列层的进度要报告给工作流,工作流再统一通知UI
工作流执行阶段4: 获取序列配置(从注册表) 调用序列执行器(传入取消令牌、进度回调) 等待序列执行完成 根据返回结果决定下一步工作流不关心序列内部怎么跑配方、怎么循环。它只关心结果——成功还是失败。这就是分层的意义:每层只跟相邻层打交道,不跨层。
进度报告:三层叠加
现在有三层执行,进度报告也要三层叠加:
工作流进度:阶段 4/6 — 序列执行 序列进度:Step 2/5(第1次循环,共3次) 配方进度:Step 3/4 — 压力30,加工60sUI上显示完整位置:
当前状态:运行中 工作流:阶段 4/6 — 序列执行 序列:Step 2/5(1/3) 配方:Step 3/4操作员一眼就能看出当前在哪个阶段、跑到哪个序列步骤、配方执行到哪。如果卡住了,也知道卡在哪一层。
进度报告的实现是回调机制——每层执行时调用上层传入的进度回调,上层再往上传:
工作流 → 序列执行器 → 配方执行器 ↑ ↑ ↑ 进度回调 进度回调 进度回调UI订阅最外层的进度事件,收到通知后更新界面。
工作流状态机
工作流本身也有状态:
| 状态 | 含义 |
|---|---|
| 空闲 | 没有在执行 |
| 运行中 | 某个阶段正在执行 |
| 已完成 | 所有阶段正常完成 |
| 已取消 | 操作员取消 |
| 故障 | 某阶段出错 |
跟序列层的状态几乎一样。但工作流多一个"空闲"状态——序列执行完就结束了,工作流是常驻的,执行完回到空闲等下一次。
UI上的"开始"按钮绑定工作流状态——只有空闲时才能点。运行中按钮变灰,防止重复启动。
多种工作流类型
实际项目中不只有一种工作流。不同的操作对应不同的工作流:
| 工作流 | 阶段 | 用途 |
|---|---|---|
| 主流程 | 进料→预热→检测→序列→清理→出料 | 完整生产流程 |
| 仅加载 | 进料→预热→检测 | 只进料不加工 |
| 仅卸载 | 清理→出料 | 只出料 |
| 维护流程 | 清理→检查 | 维护模式 |
操作员在UI上选工作流类型。不同工作流的阶段组合不同,但每个阶段内部的逻辑是复用的——进料就是进料,不管在哪个工作流里都一样。
踩坑记录
坑1:预热阶段超时判定
一开始预热阶段的超时设得太短。冬天车间温度低,加热器从5度升到200度需要的时间比夏天长很多。操作员反映"经常超时报错"。后来把超时改成动态计算——根据当前温度和目标温度的差值估算需要时间,再乘以1.5倍余量。
坑2:清理阶段执行两次
序列执行失败时,序列层自己触发了一次清理,工作流层又触发了一次。两次清理导致有些阀门开关了两次,设备状态混乱。后来明确职责:序列层不触发清理,清理是工作流层的职责。序列出错只返回错误码,由工作流层决定是否清理。
坑3:工作流状态没复位
某次故障后工作流状态卡在"故障",操作员点"开始"没反应——因为状态不是"空闲"。后来加了自动复位:故障状态下操作员确认后,状态自动回到空闲。
本篇小结
| 概念 | 职责 |
|---|---|
| 工作流(Workflow) | 完整生产流程的阶段编排 |
| 六个阶段 | 进料→预热→检测→序列→清理→出料 |
| 安全设计 | 任何阶段失败都跳到清理 |
| 三层进度 | 工作流+序列+配方三层叠加 |
| 多种工作流 | 主流程/仅加载/仅卸载/维护 |
| 工作流状态 | 空闲/运行中/已完成/已取消/故障 |
工作流层的核心思想:把完整的生产流程拆成固定阶段,每个阶段独立执行,失败时统一走清理。序列层管工艺编排,工作流层管全局流程。
下期预告
第9篇:报警系统设计
工艺跑起来了,万一设备出问题怎么报?下一篇讲报警规则引擎、报警通知机制、报警历史持久化。