Files
FT546-WCS/docs/superpowers/specs/2026-08-12-ft546-shadow-first-safe-cutover-design.md

368 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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` 稳定制品、配置和对账式回退流程仍可执行。