严格状态机已经能拒绝部分非法操作,但距离“工站唯一状态来源”和工业级架构还有以下问题

严格状态机已经能拒绝部分非法操作,但距离“工站唯一状态来源”和工业级架构还有以下问题。

高优先级问题

  1. 状态仍然不是唯一来源

当前同时存在多套状态:

  • StationWorkflowStateMachine
  • StationRuntime
  • IsBatchOpen
  • _isWorkflowRunning
  • _isResetting
  • _isBatchClosing
  • RunMode
  • EnvironmentData.StationStatus
  • EnvironmentData.ElectricalTestState
  • AutomaticPipelineActivity

例如正式测试开始时:

StationControlViewModel.cs 设置 _isWorkflowRunning = true,同时调用状态机进入 WorkflowRunning;但测试结束时只设置 _isWorkflowRunning = false,并没有同步更新状态机。

可能出现:

_isWorkflowRunning = false
WorkflowState = WorkflowRunning

因此状态机和实际业务状态可能不一致。

建议后续由状态机统一维护生命周期状态,StationRuntime 只维护运行数据,不能再让业务直接修改多个布尔字段。

  1. 命令准入不是原子操作

当前命令大致是:

检查状态
进入业务方法
业务方法再次进入邮箱
再次检查状态
执行具体业务

虽然已经增加了邮箱内二次校验,但“状态检查”和“业务占用”仍不是一个原子过程。

例如:

StartLot 检查通过
Stop 命令改变状态
StartLot 继续进入业务

目前只能依赖后续业务再次判断,不能从架构上保证命令已经成功占用工站。

建议增加:

CommandContext
ExpectedState
StateVersion
OperationId

命令必须携带状态版本,执行时使用:

当前版本未变化
且状态符合预期
才允许占用并执行
  1. 停止状态与实际流水线状态含义不完全一致

当前停止按钮只停止电学测试,之后仍然要继续:

吹扫
下料搬运
人工取料

但是停止流程可能很快将状态设置为:

StoppedAwaitingReset

此时实际上流水线可能仍有产品在吹扫或等待人工取料。

因此:

StoppedAwaitingReset

不能简单表示“所有生产流程都已经停止”,它实际表示:

测试已停止,流程正在收尾,等待复位

建议增加明确的状态或状态组合:

TestingStopped
Draining
WaitingForUnload
StoppedAwaitingReset

或者保留生命周期状态,同时通过 PipelineActivity 明确区分“测试停止”和“流水线收尾”。

  1. 状态机允许 Offline 从任意状态进入

当前迁移规则允许:

任意状态 -> Offline

但软件状态进入 Offline 不代表硬件已经停止,也不代表器件已经离开设备。

如果将来某个异常分支直接调用:

ApplyButtonState(StationButtonState.Offline);

可能出现:

界面显示离线
后台 Worker 仍在运行
PLC 仍有器件
主控板仍在测试

建议 Offline 只能由生命周期服务在完成以下动作后进入:

  1. 停止测试;
  2. 停止自动上料;
  3. 等待流水线退出;
  4. 关闭主控输出;
  5. PLC 复位或安全释放;
  6. 确认硬件状态。

中优先级问题

  1. StationControlViewModel 仍然承担过多业务职责

虽然已经拆分为多个 .partial.cs,但本质仍然是一个巨大类,仍包含:

  • 测试流程;
  • 自动上料;
  • 自动化 SOT;
  • EAP Host 放行;
  • 复位;
  • 停止;
  • 结批;
  • PLC 判断;
  • 窗体弹窗;
  • 按钮状态;
  • 数据文件;
  • 日志;
  • 设备状态。

例如自动化方法仍在:

StationControlViewModel.Automation.cs

后续应将 HandleEapStartLotAsyncHandleEapSOTAsync 等方法迁移到:

StationAutomationService
StationEapService
StationLifecycleService
StationMaterialService
StationTestService

ViewModel 最终只保留命令转发和界面属性更新。

  1. Automation 和 EAP 仍共用过于靠近协议的服务

当前 EAP 网关和 Automation 服务最终都依赖:

StationAutomationService

共享底层工站业务是合理的,但不应让 EAP 直接依赖名称带有 Automation 语义的服务。

当前容易造成误解:

EAP -> StationAutomationService
Automation -> StationAutomationService

建议调整为:

Automation -> StationWorkflowFacade
EAP -> StationWorkflowFacade

协议层分别独立:

AutomationTcpProtocolAdapter
AutomationCommandRouter
StationAutomationService

SecsGemService
EapCommandMapper
StationEapService

二者只在领域业务层汇合,不共用协议语义。

  1. 命令名称仍使用字符串映射

StationAutomationService.cs

当前通过字符串判断:

"StartLot"
"SOTReady"
"SOT"
"EQPUnload"

存在风险:

  • 拼写错误时无法被状态机拦截;
  • 新增命令容易忘记加入映射;
  • 未识别命令可能绕过状态准入;
  • 重命名命令时编译器不会提示。

建议将 EnqueueCommandAsync 改为接收强类型:

StationWorkflowCommand.StartLot
StationWorkflowCommand.Sot
StationWorkflowCommand.EndLot

不要让核心业务依赖字符串命令名。

  1. 硬件资源锁还不是完整的工站级互斥

StationDeviceContext.cs

当前 PLC、主控板、负载板各自有独立锁。这可以防止同一设备同时访问,但不能保证一个完整工艺动作的原子性。

例如:

先写主控板
再写 PLC
再写负载板

如果中途插入复位命令,可能出现:

主控板已配置
PLC 尚未配置
复位已经开始

建议增加两种锁:

设备级锁:保护单个设备通讯
工站级事务锁:保护跨多个设备的完整动作

涉及多个设备的初始化、复位、测试启动、结批动作,必须持有工站级锁。

  1. PLC 快照和实时读取仍未完全统一

当前项目中同时存在:

ReadSignal()
ReadSignalFresh()
AutomaticRealtimeSnapshot
PLC 直接读取

这会造成不同业务看到不同时间点的数据。

例如:

实时服务看到允许上料 = 1
SOTReady 直接读取到允许上料 = 0

建议统一为:

PlcSnapshot
SnapshotSequence
CapturedAt
IsCommunicationValid
SignalValues

业务只消费带时间戳的快照;只有必须确认最新硬件状态的安全动作,才使用明确标注的 Fresh Read。

  1. 状态变化事件缺少异常隔离

TryTransitionStrict 更新状态后触发:

StateChanged?.Invoke(...)

如果后续订阅者抛出异常,可能出现:

状态已经改变
调用方却收到异常
按钮未刷新
业务认为转移失败

建议:

  • 状态变更事件内部独立捕获异常;
  • 状态提交和 UI 通知分离;
  • UI 通知失败不能回滚状态;
  • 日志记录订阅者异常。
  1. 运行态字段仍可能组成矛盾状态

StationRuntime.cs

当前多个字段分别使用 Volatile 读写,例如:

Resetting
StopRequested
WorkflowRunning
BatchClosing
AutomaticInitializationCompleted

单字段读写是线程安全的,但多个字段组合不是原子的,可能读到:

Resetting = false
StopRequested = true
BatchClosing = false
WorkflowRunning = true

建议改成不可变快照整体替换:

StationRuntimeSnapshot

所有运行态变化通过一个原子方法更新,避免读到半更新状态。

  1. 操作日志数据库写入方式会阻塞业务

StationOperationJournal.cs

当前每次记录都同步打开数据库、建表、插入,并且数据库异常被直接吞掉。

风险:

  • PLC 等待线程可能被数据库阻塞;
  • 高速阶段变化产生大量 SQLite 操作;
  • 数据库异常时不容易被发现;
  • 日志事实可能丢失。

建议:

业务线程 -> 内存日志队列
后台日志 Worker -> SQLite

同时保留文本日志作为故障兜底,并记录数据库写入失败次数。

建议优化顺序

  1. 增加状态机转移矩阵单元测试;
  2. StationWorkflowStateMachine 移到 Services.Workflow
  3. StationRuntimeSnapshot 替换多个独立布尔字段;
  4. 所有控制命令改成强类型命令;
  5. 将状态检查和命令占用合并为原子操作;
  6. 增加工站级事务锁;
  7. 统一 PLC 快照和 Fresh Read 规则;
  8. 将停止、收尾、复位状态重新定义清楚;
  9. 继续迁移 ViewModel 中的测试、生命周期和协议业务;
  10. 最后完善操作日志队列、恢复机制和统一释放流程。

目前最需要优先处理的是前三项:状态唯一来源、命令原子占用、停止状态语义。这三项如果不先解决,继续拆分文件只能降低代码长度,不能彻底解决状态错乱和并发竞态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张工在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值