From 48b0b154b6319de05df3b71b4457d12110bdb095 Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Wed, 12 Aug 2026 10:35:51 +0800 Subject: [PATCH] docs(ft546): design shadow-first safe cutover --- ...-ft546-shadow-first-safe-cutover-design.md | 367 ++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-12-ft546-shadow-first-safe-cutover-design.md diff --git a/docs/superpowers/specs/2026-08-12-ft546-shadow-first-safe-cutover-design.md b/docs/superpowers/specs/2026-08-12-ft546-shadow-first-safe-cutover-design.md new file mode 100644 index 0000000..62be36f --- /dev/null +++ b/docs/superpowers/specs/2026-08-12-ft546-shadow-first-safe-cutover-design.md @@ -0,0 +1,367 @@ +# FT546 调度中心影子先行安全接入设计 + +> 状态:已确认 +> 确认日期:2026-08-12 +> 实施分支:`optimize/wcs` +> 现场原则:稳定版本始终是唯一生产调度者和唯一 PLC 写入者,直到受控切换完成 + +## 1. 背景与决策 + +`javaTest` 中心调度已经覆盖父任务阶段、选车、路径规划、交通资源、换层、二段式任务、避让、故障注入和历史回放。`ruoyi-wcs` 与 FT546 Admin 已完成一部分标准内核、PLC 编码、Outbox、运行模式和内存端到端测试,但生产适配器、现场事实对账和运行模式隔离仍不完整。 + +本设计采用“影子先行”路线: + +1. 现场稳定版本不改动、不停机,继续独占现场任务、业务表、WMS 回调和 PLC 写入。 +2. 先运行独立 `javaTest` 离线影子,建立确定性算法基线。 +3. 再实现与生产控制进程隔离的实时只读采集与同核影子计算。 +4. 先根据现场差异证据决定算法和适配器修改,不预设新算法一定优于旧算法。 +5. 通过 SQL Server 集成、现场影子和 HIL 门禁后,才允许在维护窗口内由 `LEGACY` 受控切换到 `STANDARD`。 + +截至确认日,不能直接把 `ruoyi-admin --ft546.wcs.runtime-mode=SHADOW` 部署为现场影子。当前实现没有为 `SHADOW` 装配标准核心,却仍会在 `ScheduleThread.@PostConstruct` 中查询设备、构造 PLC reader、注册设备运行时并构造旧业务线程;无权限保护的线程操作接口还可以手动启动 PLC reader 和旧调度线程。模式名称本身不构成安全隔离。 + +## 2. 目标与非目标 + +### 2.1 目标 + +- 将 `javaTest` 已验证的调度语义逐步接入 `ruoyi-wcs`,不破坏 FT546 现有业务。 +- 保持 `LEGACY` 为默认模式和可验证回退路径。 +- 使 `SHADOW` 与未来 `STANDARD` 复用同一调度核心,只在出站能力和状态所有权上不同。 +- 在影子阶段强制实现零 PLC 写入、零 WMS 回调、零生产业务表写入和零旧业务调度线程。 +- 对旧队列字段和 PLC 地址、数据类型、值、顺序形成可审计差异报告。 +- 完整表达父任务阶段、同车绑定、换层、二段下发、避让、业务完成、车辆释放、Outbox 和重启恢复。 +- 在缺少真实 PLC 或隔离 SQL Server 时明确证据边界,不把内存测试或历史回放声明为可投产证明。 + +### 2.2 非目标 + +- 不在影子采集前主动修改调度规则来追求结果一致。 +- 不在现场稳定分支上开发、试验或合并未验收代码。 +- 不修改 FT546 PLC 控制流程、安全联锁和既有寄存器语义。 +- 不做 `LEGACY` 与 `STANDARD` 并行 PLC 双写。 +- 不做运行中热切换或遇错自动切回。 +- 不用历史 `agv_id`、`path_info`、完成状态或 `finish_date` 强制新调度作出相同选择。 + +## 3. 方案比较 + +### 3.1 采用:同核只读影子后受控接管 + +未来 `SHADOW` 与 `STANDARD` 使用同一个任务、调度、交通和恢复核心。`SHADOW` 只消费不可变现场事实并生成虚拟命令;`STANDARD` 在就绪、恢复和单写者门禁全部通过后才装配真实出站适配器。 + +优点是算法差异可以在无现场控制权的条件下观察,切换前能证明协议和值的一致性,切换后也没有第二套调度语义。代价是必须先完成持久化、只读采集、差异报告和运行模式硬隔离。 + +### 3.2 不采用:直接启用当前 STANDARD + +当前 `STANDARD` 绑定的多个 Repository、Gateway、Snapshot 和 Recovery 端口尚无生产实现,不能证明应用可启动、SQL Server 事务可恢复或真实 PLC 语义可靠。直接启用可能造成启动失败、首单失败或发送后结果不明。 + +### 3.3 不采用:把旧队列线程包装成新调度中心 + +这种做法会保留旧线程和新内核对任务、车辆、资源和完成事实的双重所有权,无法证明唯一调度者,也无法解决阶段语义、Outbox、`UNKNOWN` 和重启恢复问题。 + +## 4. 运行模式与单写者边界 + +| 模式 | 调度所有者 | 现场事实来源 | 核心状态写入 | PLC/WMS 写入 | 用途 | +| --- | --- | --- | --- | --- | --- | +| `LEGACY` | 旧线程 | 现有 PLC reader 与业务表 | 旧表 | 允许,唯一写者 | 当前生产和回退 | +| `SHADOW` | 标准核心,仅计算 | 离线快照或只读采集 | 独立影子库/Schema | 禁止 | 差异采集与准入 | +| `STANDARD` | 标准核心 | 标准快照采集器 | WCS 核心表 | 通过 Outbox 允许 | 最终受控接管 | + +强制规则: + +1. 默认值保持 `LEGACY`。 +2. `SHADOW` 不创建真实 `DeviceCommandGateway`、`WmsNotificationGateway`、旧业务调度线程或 PLC reader。 +3. `SHADOW` 的禁止写入必须同时由 Bean 装配、数据库权限、网络 ACL 和自动化测试保证。 +4. `STANDARD` 缺少任何必需端口、端点、能力证据或恢复条件时必须启动失败或保持未激活,不能降级为“首单运行时失败”。 +5. `LEGACY` 与 `STANDARD` 不能同时拥有有效调度租约或有效 PLC 写通道。 +6. 切换采用配置变更加进程重启,不支持运行中热切换。 + +## 5. 现场稳定版本保护 + +### 5.1 代码与制品 + +- 开发只在 `optimize/wcs` worktree 进行,不修改、合并或推送到现场稳定分支。 +- 设计确认时,本地远端跟踪引用 `origin/main` 为提交 `652607c`。现场回退必须锁定实际已部署制品、提交号、配置和校验和;不能只依赖可能移动或过期的分支名称。 +- 本地 `main` 与 `origin/main` 已分叉,不能把本地 `main` 当作现场回退来源。 +- 数据库迁移只允许新增表、列、索引和约束,不删除、不重命名现场旧对象。 + +### 5.2 进程、网络与数据库 + +- 实时影子使用独立进程、服务端口、日志目录和部署目录,优先部署在独立主机。 +- 影子数据库账号只允许读取批准的现场事实视图;禁止写现场订单、队列、设备、资源、Outbox 和回调表。 +- 影子结果写入独立数据库或独立 Schema,不能与生产核心表共用写权限。 +- 网络 ACL 禁止影子主机访问 PLC 写端口和生产 WMS 回调地址。 +- 影子查询使用独立连接池、短超时、限速和熔断;积压或超时时停止影子计算,不持续压迫生产数据库。 +- 影子进程的崩溃、阻塞、快照过期或输出磁盘故障不能反向暂停或修改现场稳定程序。 + +### 5.3 当前可运行边界 + +独立 `javaTest` 可以立即运行。它只读取打包的历史与地图资源,使用内存车辆、队列、PLC、WMS 和资源账本,不启动 Spring、不连接 SQL Server/PLC/WMS,只写命令参数指定的本地输出目录。 + +当前 `ruoyi-admin` 的 `SHADOW` 不允许直接运行现场影子,必须先完成以下整改: + +- `ScheduleThread` 按模式装配,`SHADOW` 不执行 `plcReadInit()` 和 `scheduleInit()`。 +- `ThreadOperationController` 在 `SHADOW` 禁止启动 PLC reader 和旧调度;接口恢复权限校验并按模式返回明确状态。 +- 建立专用 `Ft546ShadowConfiguration`,只装配只读快照源、同核调度、记录型命令网关和差异仓储。 +- 影子启动自检必须证明无 PLC/WMS 真实 Bean、无生产写权限和无旧调度线程。 + +## 6. 父任务与阶段模型 + +一个业务请求只创建一个父任务,阶段不会重新进入全局业务队列,也不会重新创建 WMS 任务: + +```text +WAITING_ASSIGNMENT + -> 可选 FLOOR_TRANSFER_TO_PICKUP + -> EMPTY_TO_PICKUP + -> PICKUP_CONFIRMED + -> 可选 LOADED_RETURN_TO_SOURCE_FRONT + -> LOADED_TO_PLACE + -> CARGO_DEPOSITED / BUSINESS_COMPLETED + -> 可选 EMPTY_RETURN_TO_TARGET_FRONT + -> VEHICLE_RELEASED +``` + +规则: + +- `task_type` 表示执行阶段,业务类型取 `default02`;入库、出库、移库分别为 `1/2/7`。 +- 取货确认后载货状态由 `EMPTY` 变为 `LOADED`,车辆与父任务锁定;普通等待、断线和重启不能自动换车。 +- 到放货点并取得可靠完成事实后记录 `CARGO_DEPOSITED`,只归档一次并只产生一次 WMS 业务效果。 +- 车辆可能在业务完成后继续空车返回目标通道前端,车辆释放时间与业务完成时间分别记录。 +- 每个阶段从设备确认的实际位置重新规划,不复用历史路径或上一阶段估算位置。 +- 命令身份至少包含 `parentTaskId + phase + routeVersion + commandSegment`;旧 ACK 不能推进新阶段。 +- 不需要的阶段必须记录明确跳过原因,不能无痕跳过。 + +## 7. 选车、路径与避让 + +- 车辆硬过滤包含在线、自动、健康、已同步、空闲、电量、载货、能力和可达性。 +- 同层和跨层可行车辆进入同一稳定选车策略;历史车辆只用于对比。 +- 候选路径限制在最短物理距离的 `110%` 内,最多 32 条,按对向风险、占用节点、不兼容预约、活动路线重叠、转弯、距离和站点序列作确定性字典序比较。 +- 临时交通冲突只进入 `HOLD`,不能误判为 `NO_ROUTE`。 +- 同一任务、车辆、阻塞资源和原因连续三次交通 `HOLD` 后,才允许从实际位置重规划尚未承诺的剩余路线。 +- 已发送、已接受或结果为 `UNKNOWN` 的动作不能被新路线覆盖。 +- 路线预约必须原子替换;失败时完整保留旧路线和旧预约。 +- 只有拓扑中存在经过验证的安全让行点,才创建 `CLEARANCE` 运行步骤;不能临时选择任意空点。 +- 避让不创建新 WMS 业务任务,不重复归档或回调。 + +## 8. 换层编排 + +提升机不是普通地图边。跨层任务使用幂等换层子作业,并联合保护源入口、提升机本体和目标出口: + +```text +预约源入口 + 提升机 + 目标出口 + -> 创建幂等换层子作业并锁车 + -> 等待旧提升机协议执行事实 + -> 核对车辆、楼层、门、端口和实际出口 + -> 从实际出口重新规划当前业务阶段 +``` + +- 任一资源预约失败时保持 `BLOCKED`,不发送进入提升机动作。 +- 作业提交后断线、故障或重启不释放车辆锁,不创建第二个换层作业。 +- 完成事实必须包含匹配车辆、目标层和实际出口;重复完成只推进一次。 +- 目标出口占用时保持换层完成待提交状态,不能让车辆出现在已占节点。 +- 事实不确定时进入恢复工单,隔离车辆、提升机和相关端口,不盲目释放。 + +## 9. FT546 旧 PLC 与二段任务 + +标准核心生成强类型命令意图,FT546 Adapter 负责映射旧寄存器。核心不伪装旧 PLC 不具备的候选缓冲、Prepare/Commit、命令幂等或有序快照能力。 + +必须保持并逐项回归的写入序列: + +```text +普通主任务:4004 -> 4000 -> 4002 -> 4021 +跨层任务: 普通主任务序列 + 4020=1 -> 4022=8 +二段第一段:4404 -> 4422=1 -> 4402=1 -> 4400 +二段第二段:4404 -> 4422=2 -> 4423=99 -> 4402=9 -> 4400 +``` + +对每次虚拟或真实协议发送,证据至少包含: + +```text +deviceId +parentTaskId +phase +commandKey +routeVersion +taskNo +taskType +pathPoints +registerAddress +dataType +values +writeSequence +triggerValue +``` + +相同车辆、路径、阶段和任务输入下,队列字段、寄存器地址、数据类型、值和顺序必须与旧实现逐项一致。新算法可以选择不同车辆或安全路径,但差异必须归类为算法决策差异,不能混入协议不一致。 + +旧 PLC 不识别 `commandKey`。发送前能证明未发送的失败可以重试;任何写入开始后无法证明结果的命令进入 `UNKNOWN`,保护完整点到点或二段任务路径,不补写单个寄存器、不自动重发。 + +## 10. 设备事实、Outbox 与恢复 + +每个标准调度 tick 的因果顺序为: + +```text +采集不可变设备事实 + -> 校验新鲜度和恢复门禁 + -> 加载候选任务、资源和活动命令 + -> 生成调度授权或 HOLD + -> 同事务持久化绑定、预约、DeviceCommand 和 CommandOutbox + -> 出站分发 + -> 使用新设备事实推进步骤、订单和资源 + -> 独立分发 WMS NotificationOutbox +``` + +- `send` 成功只表示通信层受理,不表示物理完成。 +- 订单、步骤、设备命令、设备事实和资源预约保持独立状态。 +- 设备事实必须校验设备、任务号、阶段、预期端点、载货变化、模式和时间新鲜度。 +- 重复、乱序、倒退、旧阶段或不能关联当前命令的事实只记录审计,不推进状态。 +- `UNKNOWN` 保留车辆和未确认资源,只允许继续只读采集、诊断和受控恢复。 +- WMS 回调失败不回滚物理任务,只重试通知 Outbox。 +- 核心 tick 失败时不创建新设备动作;已存在的 WMS 通知 Outbox 可以独立投递。 +- 重启时先取得租约并保持全局调度暂停,加载活动状态、采集设备快照、完成对账,再局部启用安全设备。 + +## 11. 影子数据与差异报告 + +### 11.1 数据来源顺序 + +1. 已打包的历史队列、地图和通道前端快照,用于可重复离线基线。 +2. 现场稳定程序已经落库的只读事实,用于实时影子比较。 +3. 经过审批的原始报文副本,用于协议和故障审计。 + +影子不直接增加 PLC 连接。实时事实缺失、过期或互相矛盾时,本轮结果标记为 `INSUFFICIENT_FACTS`,不产生误导性可比结论。 + +### 11.2 比较维度 + +协议一致性: + +- 队列业务字段、默认字段和阶段字段; +- PLC 地址、数据类型、值、顺序和触发值; +- 换层、二段任务和旧任务号编码; +- 完成、归档和回调的幂等键。 + +调度合理性: + +- 任务到达与父任务归并; +- 车辆资格、选车和同车绑定; +- 每阶段路径、候选质量和资源保护; +- 换层入口/出口、二段、HOLD、避让和重规划; +- 业务完成与车辆释放。 + +每个差异必须保存相同输入快照、旧结果、新结果、差异类别和可解释原因。协议差异与算法差异分别统计。 + +### 11.3 已建立的离线基线 + +2026-08-12 在提交 `f66585f` 上执行: + +```text +javaTest tests: 549 +failures/errors/skipped: 0/0/0 +raw snapshots / unique GUID / duplicates: 240 / 98 / 142 +business tasks / operational queues: 76 / 17 +completed / waiting / failed: 76 / 0 / 0 +incomplete vehicle releases: 0 +total safety violations / deadlocks: 0 / 0 +archive count / WMS effects / duplicate effects: 76 / 76 / 0 +``` + +本地证据目录:`javaTest/outputs/wcs-history-business-shadow-20260812-f66585f/`。六个报告文件的 `manifest.sha256` 校验结果为零不匹配。该目录是开发机生成证据,不随本设计文档提交。 + +报告明确标注:这是“真实历史负载驱动纯内存仿真”,不是现场 `SHADOW`、HIL 或 `ACTIVE` 验收。 + +## 12. 故障隔离 + +| 故障 | 处理 | 禁止行为 | +| --- | --- | --- | +| 影子快照缺失/过期 | 标记 `INSUFFICIENT_FACTS`,暂停该对象计算 | 猜测设备状态 | +| 影子数据库超时 | 熔断、退避并记录采集缺口 | 加速重试压迫生产库 | +| 命令发送结果不明 | `UNKNOWN`、隔离设备和资源 | 自动重发或释放 | +| 换层事实不一致 | 隔离车辆、提升机和端口,创建 RecoveryCase | 重建换层任务 | +| 二段任务部分写入不明 | 保护整个二段任务范围 | 补写单寄存器 | +| WMS 回调失败 | 重试通知 Outbox | 回滚物理完成 | +| 影子进程崩溃 | 只影响影子报告 | 影响现场调度或告警处置 | +| 输出磁盘满 | 停止新影子计算并告警 | 丢弃差异后继续宣称健康 | + +## 13. STANDARD 切换与回退 + +现场不做热切换。维护窗口内按以下顺序交接: + +```text +LEGACY 正常运行 + -> STANDARD 以控制关闭方式启动并完成恢复对账 + -> 暂停新任务进入 + -> 等待或人工核对活动命令 + -> 停止 LEGACY 调度写入 + -> 获取数据库租约和 fencing token + -> 再次验证旧写者失效 + -> 开放 STANDARD 真实出站网关 + -> 恢复新任务进入 +``` + +回退规则: + +- `STANDARD` 取得写入权之前失败时,`LEGACY` 不停止。 +- `STANDARD` 取得写入权之后失败时,先禁止新命令并对账所有活动命令。 +- 存在 `UNKNOWN` 时禁止自动切回 `LEGACY`,必须先完成现场对账,避免重复动作。 +- 确认无未决物理动作后,释放 `STANDARD` 租约,恢复锁定的稳定制品和 `LEGACY` 配置。 +- 回退不回滚新增数据库结构,只停止使用;避免数据库降级造成更大不可用。 +- 切换和回退演练必须记录制品校验和、配置快照、操作者、时间、活动任务清单和对账证据。 + +## 14. 测试与准入门禁 + +### 14.1 自动化分层 + +1. 领域测试:父任务阶段、同车绑定、业务完成与车辆释放、换层、避让、HOLD、资源容量和死锁。 +2. 协议测试:队列字段、寄存器地址、数据类型、值和顺序逐项对比。 +3. 故障测试:重复/旧/乱序 ACK、断线、超时、部分写入、重启、乱序快照、Outbox 和 `UNKNOWN`。 +4. 模式隔离测试:`SHADOW` 上下文无 PLC/WMS 真实 Bean、无生产写入、无旧调度线程,线程操作接口 fail-closed。 +5. SQL Server 集成测试:事务、唯一键、乐观锁、资源并发、Outbox、租约、fencing token 和重启持久性。 +6. 模拟端到端:入库、出库、移库、同层、跨层、二段、通道前端返回和共享资源避让。 +7. 历史回放:240 条原始记录归并、76 个父任务全阶段和所有不变量。 +8. 实时影子:旧队列与新虚拟队列的连续差异采集,且副作用为零。 +9. HIL:隔离 PLC 或测试设备上的寄存器时序、断线、重启和部分写入。 +10. 切换演练:单写者交接、启动失败不影响 LEGACY、对账后回退。 + +### 14.2 STANDARD 硬门槛 + +- 安全不变量违规数为 0。 +- 现场表写入、PLC 写入和 WMS 回调等影子副作用为 0。 +- 可比较场景的队列业务字段、PLC 地址、数据类型、值和顺序不一致数为 0。 +- 所有算法差异都有输入、旧结果、新结果和决策原因;未经审核的差异数为 0。 +- 所有生产 Adapter、Repository、端点、拓扑、能力证据和恢复门禁均就绪。 +- SQL Server 集成、现场影子和 HIL 任一缺失时,只能声明本地实现或相应层级验收完成,不能声明可投产。 + +## 15. 实施顺序 + +### 阶段 A:离线影子基线 + +- 保持现场稳定程序不变。 +- 固化 `javaTest` 输入清单、测试结果、报告和校验和。 +- 建立队列/PLC 协议字段差异格式。 + +### 阶段 B:实时只读影子硬隔离 + +- 修复 `SHADOW` 条件装配和线程启动边界。 +- 增加只读现场事实 Adapter、记录型命令网关和独立差异仓储。 +- 增加启动自检、数据库权限验证和网络不可达证明。 +- 现场旁路运行并采集差异,不修改调度算法。 + +### 阶段 C:按证据补齐同核语义和 FT546 Adapter + +- 根据差异逐项补齐父任务阶段、换层、二段、避让、设备事实、Outbox 和恢复。 +- 每次修改都用同一采样重新计算,区分协议回归与允许的算法差异。 +- 完成生产 MyBatis Repository 和 SQL Server 集成验证。 + +### 阶段 D:HIL 和受控切换 + +- 在隔离 PLC/测试设备验证真实协议。 +- 完成维护窗口切换和回退演练。 +- 只有全部硬门槛通过后才允许切换现场写入权。 + +## 16. 完成定义 + +本接入只有在以下事实同时成立时才算完成: + +- `SHADOW` 与 `STANDARD` 使用同一核心,`SHADOW` 没有权威写通道。 +- 现场稳定版本在影子阶段保持唯一生产调度者和 PLC 写入者。 +- 父任务、换层、二段、避让、Outbox、事实对账和恢复均有生产实现及自动化证据。 +- 队列业务字段和 PLC 地址、类型、值、顺序与旧逻辑的协议差异为零。 +- 所有允许的车辆或路径差异均可解释、已审核且不违反安全不变量。 +- SQL Server、实时影子、HIL、单写者切换和回退演练证据齐全。 +- `LEGACY` 稳定制品、配置和对账式回退流程仍可执行。