设备对象不是创建出来就能用。它可能尚未通电、驱动未加载、端口已打开但初始化失败,也可能刚刚断连,而上一次运动仍留在控制器中。若代码只用一个 bool IsConnected 描述这些情况,重复连接、退出卡死和“界面显示在线但命令全部失败”几乎不可避免。
本文建立一套通用的设备生命周期模型,重点不是枚举多少个状态,而是让启动、运行、停止和故障后的每一步都有唯一责任、明确超时和可验证的最终状态。
1. 连接、健康和就绪是三件事
“TCP 已连接”或“句柄不为空”只能证明通信资源曾经建立,不能证明设备可以执行任务。至少要区分:
- 连接性(Connectivity):端口、会话或驱动句柄是否可用;
- 健康度(Health):设备是否报告故障、心跳是否新鲜、关键传感器是否可信;
- 就绪度(Readiness):完成当前业务操作所需的前置条件是否满足。
一台相机可以处于已连接但过温报警的状态;一根轴可以健康,却因为尚未回零而不能执行绝对定位。把三者压成一个绿色图标,会让 UI 和自动流程得到错误结论。
推荐由设备模型发布不可变快照:
| |
快照里的时间表示“这份观测何时产生”,调用方还要判断其新鲜度。通信中断后保留最后位置用于诊断是有价值的,但不能把旧位置继续当成当前事实。
2. 生命周期是一组受控转换
一条常见主路径是:
| |
实际系统可以增加 Degraded、Recovering 或 Maintenance,但不要为每个布尔组合造一个状态。状态机应回答三个问题:
- 当前允许哪些命令?
- 哪个事件能够触发下一次转换?
- 转换失败后,系统能诚实确认的状态是什么?
Faulted 不是所有异常的垃圾桶。参数校验失败时设备状态可能完全没变;读取一次遥测超时也未必意味着设备故障。只有当失败改变了设备可信度,或者无法确认物理状态时,才需要降级到故障或未知状态。
3. 初始化应当可分步、可清理
把所有初始化塞进构造函数会带来两个问题:构造无法异步等待,部分成功后的资源也难以释放。更好的做法是把初始化拆成显式阶段,并为每一步记录结果:
| |
失败清理按已完成步骤的逆序执行,但“逆序补偿”并不保证物理世界回到原点。例如阀门已经打开后通信断开,上位机无法仅凭调用 CloseAsync 证明阀门已关闭。此时应进入未知或故障状态,并依赖下位机的失联策略和现场确认。
4. 用异步互斥保护生命周期转换
连接和断开不能同时执行,也不能让两个按钮各自启动一轮初始化。下面的简化实现使用 SemaphoreSlim 串行化生命周期转换:
| |
示例刻意没有把 Faulted 自动改回 Offline:清理调用完成不等于已经确认物理状态安全。生产代码还应实现受限时的 DisconnectAsync,阻止释放期间的新命令,并避免 DisposeAsync 与正在执行的方法竞态。
生命周期互斥锁不应覆盖数分钟的工艺流程。长任务应交给后续的命令队列和流程执行器;生命周期锁只保护建立、切换和销毁会话这些短而关键的转换。
5. 取消、超时和停止的含义不同
设备调用至少涉及三种时间边界:
- 调用方取消:用户或上层流程不再需要结果;
- 操作超时:在约定期限内没有达到完成条件;
- 应用停止:进程正在优雅退出,不再接受新工作。
CancellationToken 传递的是协作式取消请求,不会强制终止线程,更不会天然停止硬件。适配器收到取消后,需要根据操作契约发出停止、等待安全点,或者明确报告“等待已取消,但设备动作可能仍在继续”。
清理操作也不应继续使用已经取消的调用方令牌,否则 CloseAsync 可能在执行任何清理步骤之前就因取消而抛出异常;但使用 CancellationToken.None 又可能让退出永久卡住。常见做法是为清理创建独立、受限时的令牌,并把超时后的不确定状态记录下来。
6. 重连必须重新建立事实
重连不是简单再次调用 Open。旧会话失效后,至少要重新核对:
- 设备身份、序列号和固件是否仍是预期对象;
- 轴是否移动过、是否需要重新回零;
- 输出、阀门和光源的真实状态;
- 控制器内是否仍有未完成或排队的命令;
- 当前 Recipe、校准和上位机缓存是否仍匹配;
- 谁仍持有当前任务和物料的所有权。
因此自动重连通常只能恢复“通信”,不能自动恢复“生产”。是否继续任务应由恢复策略根据可证明的检查点决定。涉及材料损失或人身安全时,宁可要求人工确认,也不要从一个推测状态继续。
7. 进程启动与设备启动分开
应用进程成功启动,不等于所有设备都必须已经 Ready。可以让 Generic Host 先建立日志、配置和诊断能力,再由 DeviceSupervisor 并行或按依赖关系初始化设备。对非关键设备,可以进入降级模式;对关键设备,则阻止流程进入可运行状态。
同样,应用退出应先停止接收新命令,再取消流程、等待设备进入安全点、关闭会话,最后释放 Host。不要直接调用 Environment.Exit 跳过受控关闭;但也不能假设优雅停止必然发生,硬件安全仍需独立保证。
总结
设备生命周期不是一个连接布尔值,而是从资源建立、能力确认到安全关闭的一组受控转换。连接性、健康度和就绪度应分别建模;初始化要能处理部分失败;取消等待不能冒充物理停止;重连后必须重新建立事实。
下一篇将把执行入口收敛到状态机和有界命令队列,并区分暂停、取消、正常停止和急停,避免多个调用方争夺设备写控制权。