云计算百科
云计算领域专业知识百科平台

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

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

高优先级问题

  • 状态仍然不是唯一来源
  • 当前同时存在多套状态:

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

    例如正式测试开始时:

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

    可能出现:

    _isWorkflowRunning = false
    WorkflowState = WorkflowRunning

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

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

  • 命令准入不是原子操作
  • 当前命令大致是:

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

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

    例如:

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

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

    建议增加:

    CommandContext
    ExpectedState
    StateVersion
    OperationId

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

    当前版本未变化
    且状态符合预期
    才允许占用并执行

  • 停止状态与实际流水线状态含义不完全一致
  • 当前停止按钮只停止电学测试,之后仍然要继续:

    吹扫
    下料搬运
    人工取料

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

    StoppedAwaitingReset

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

    因此:

    StoppedAwaitingReset

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

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

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

    TestingStopped
    Draining
    WaitingForUnload
    StoppedAwaitingReset

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

  • 状态机允许 Offline 从任意状态进入
  • 当前迁移规则允许:

    任意状态 -> Offline

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

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

    ApplyButtonState(StationButtonState.Offline);

    可能出现:

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

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

  • 停止测试;
  • 停止自动上料;
  • 等待流水线退出;
  • 关闭主控输出;
  • PLC 复位或安全释放;
  • 确认硬件状态。
  • 中优先级问题

  • StationControlViewModel 仍然承担过多业务职责
  • 虽然已经拆分为多个 .partial.cs,但本质仍然是一个巨大类,仍包含:

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

    例如自动化方法仍在:

    StationControlViewModel.Automation.cs

    后续应将 HandleEapStartLotAsync、HandleEapSOTAsync 等方法迁移到:

    StationAutomationService
    StationEapService
    StationLifecycleService
    StationMaterialService
    StationTestService

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

  • Automation 和 EAP 仍共用过于靠近协议的服务
  • 当前 EAP 网关和 Automation 服务最终都依赖:

    StationAutomationService

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

    当前容易造成误解:

    EAP -> StationAutomationService
    Automation -> StationAutomationService

    建议调整为:

    Automation -> StationWorkflowFacade
    EAP -> StationWorkflowFacade

    协议层分别独立:

    AutomationTcpProtocolAdapter
    AutomationCommandRouter
    StationAutomationService

    SecsGemService
    EapCommandMapper
    StationEapService

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

  • 命令名称仍使用字符串映射
  • StationAutomationService.cs

    当前通过字符串判断:

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

    存在风险:

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

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

    StationWorkflowCommand.StartLot
    StationWorkflowCommand.Sot
    StationWorkflowCommand.EndLot

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

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

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

    例如:

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

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

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

    建议增加两种锁:

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

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

  • PLC 快照和实时读取仍未完全统一
  • 当前项目中同时存在:

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

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

    例如:

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

    建议统一为:

    PlcSnapshot
    SnapshotSequence
    CapturedAt
    IsCommunicationValid
    SignalValues

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

  • 状态变化事件缺少异常隔离
  • TryTransitionStrict 更新状态后触发:

    StateChanged?.Invoke(...)

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

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

    建议:

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

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

    Resetting
    StopRequested
    WorkflowRunning
    BatchClosing
    AutomaticInitializationCompleted

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

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

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

    StationRuntimeSnapshot

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

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

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

    风险:

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

    建议:

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

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

    建议优化顺序

  • 增加状态机转移矩阵单元测试;
  • 将 StationWorkflowStateMachine 移到 Services.Workflow;
  • 用 StationRuntimeSnapshot 替换多个独立布尔字段;
  • 所有控制命令改成强类型命令;
  • 将状态检查和命令占用合并为原子操作;
  • 增加工站级事务锁;
  • 统一 PLC 快照和 Fresh Read 规则;
  • 将停止、收尾、复位状态重新定义清楚;
  • 继续迁移 ViewModel 中的测试、生命周期和协议业务;
  • 最后完善操作日志队列、恢复机制和统一释放流程。
  • 目前最需要优先处理的是前三项:状态唯一来源、命令原子占用、停止状态语义。这三项如果不先解决,继续拆分文件只能降低代码长度,不能彻底解决状态错乱和并发竞态。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 严格状态机已经能拒绝部分非法操作,但距离“工站唯一状态来源”和工业级架构还有以下问题
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!