From dca8c74ba4bf79ddceecb290ee1834807e8e700c Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Fri, 7 Aug 2026 13:08:10 +0800 Subject: [PATCH] docs(javaTest): design source-aligned two-stage tasks --- ...st-source-aligned-two-stage-task-design.md | 422 ++++++++++++++++++ 1 file changed, 422 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-07-javaTest-source-aligned-two-stage-task-design.md diff --git a/docs/superpowers/specs/2026-08-07-javaTest-source-aligned-two-stage-task-design.md b/docs/superpowers/specs/2026-08-07-javaTest-source-aligned-two-stage-task-design.md new file mode 100644 index 0000000..b7ffc77 --- /dev/null +++ b/docs/superpowers/specs/2026-08-07-javaTest-source-aligned-two-stage-task-design.md @@ -0,0 +1,422 @@ +# javaTest 源码一致二段式业务任务设计 + +> 状态:已确认 +> 确认日期:2026-08-07 +> 实施边界:`javaTest` 离线仿真、历史审计报告与调度可视化 +> 生产依据:`BaseSRgvThread`、`BasCargoSpaceMapper.xml` 与 FT546 历史队列快照 + +## 1. 目标 + +在现有中心选车、路径规划、交通资源裁决、PLC ACK、逐节点移动、换层和故障恢复基础上,增加与生产 FT546 WCS 一致的父任务阶段编排:车辆先空车到取货点,取货后按业务类型执行载货段;货位位于巷道内部时,出库先把货物带回通道前端,入库放货后车辆自动回到通道前端。 + +改造后的仿真必须区分“业务任务”和“历史队列扫描快照”。原始 240 行仍全部进入数据质量审计,但不再被错误地当成 240 个互相独立的 WMS 任务。中心调度以归并后的真实业务父任务为单位选车、锁车、执行、归档和回调。 + +## 2. 生产源码依据 + +### 2.1 `task_type` 是执行阶段,不是入出库业务类型 + +`BaseSRgvThread.createCarOutQueue/createCarInQueue` 创建队列时把 `task_type` 设置为 `1`,表示先执行空车取货阶段。车辆到达取货点或多段任务前端后,`finishTaskByQueue` 把同一队列更新为 `task_type=2`,再执行载货阶段。 + +真正的业务类型保存在 `default02`: + +- `1`:入库; +- `2`:出库; +- `7`:移库; +- `8`:换层执行阶段; +- `4`:普通空车取货阶段; +- `3`:车辆运行或避让阶段。 + +因此当前 `HistoricalSimulationInputFactory` 将历史 `task_type=1/2/3` 直接映射成 `INBOUND/OUTBOUND/RELOCATION` 不符合生产语义。本次设计保留原始字段,但新增源码一致的业务类型解析,不能再用队列阶段冒充业务类型。 + +### 2.2 出库的通道前端阶段 + +生产出库建队时,先由取货站点查询 `group_coding_no`,再查询该组 `group_coding` 最大的 `cargo_space_logo`。当取货点不等于该站点时: + +- `default08=1`:先执行车辆当前位置到取货点; +- 首次下发成功后变为 `default08=2`; +- PLC 条件允许时下发取货点到 `default06`,任务类型使用 `9`; +- 到达 `default06` 后,队列转为 `task_type=2`,继续执行载货到业务放货点。 + +在仿真中对应: + +```text +EMPTY_TO_PICKUP + -> PICKUP_CONFIRMED + -> LOADED_RETURN_TO_SOURCE_FRONT(源点在巷道内部时) + -> LOADED_TO_PLACE + -> BUSINESS_COMPLETED +``` + +### 2.3 入库完成和自动回前端 + +生产入库在载货阶段到达 `cargoPlaceSite`,或者设备上报该货位所属通道的最大站点时,更新业务完成状态、写回调表并归档队列。该规则说明货物入库和车辆回到通道前端属于同一个设备作业的两个事实。 + +为了在离线仿真中看清资源占用和车辆位置,本设计把它显式拆成两个阶段,但仍属于同一父任务、同一车辆: + +```text +LOADED_TO_PLACE + -> CARGO_DEPOSITED + -> WMS_CALLBACK(业务完成,只允许一次) + -> EMPTY_RETURN_TO_TARGET_FRONT(目标在巷道内部时) + -> VEHICLE_RELEASED +``` + +业务完成时间记录在 `CARGO_DEPOSITED`;车辆释放时间记录在回到通道前端之后。故障导致自动返回变慢时,不修改已经完成的业务事实,也不重复回调。 + +### 2.4 通道前端的权威规则 + +`BasCargoSpaceMapper.xml` 的规则为: + +```sql +select group_coding_no +from bas_cargo_space +where cargo_space_logo = :site + +select top 1 cargo_space_logo +from bas_cargo_space +where group_coding_no = :group +order by group_coding desc +``` + +`javaTest` 不通过站点编号、坐标或历史路径猜测通道前端。历史测试资源增加一个由上述只读查询生成的通道前端快照,至少包含: + +```text +siteId +groupCodingNo +groupCoding +frontSiteId +``` + +生产数据库只用于生成和核验离线快照;仿真运行时不连接 SQL Server。 + +## 3. 当前 240 行数据的真实结构 + +对现有 `queue-history-200.json` 的源码一致分析结果: + +| 口径 | 数量 | 含义 | +|---|---:|---| +| 原始扫描快照 | 240 | 每次扫描保存的队列状态,包含重复行 | +| 唯一队列 GUID | 98 | 去除同一 GUID 重复扫描后的队列实例 | +| 业务阶段 GUID | 81 | `task_type=1/2` 的空车或载货阶段实例 | +| 车辆运行作业 GUID | 17 | `task_type=3` 的换层、运行或避让作业 | +| 真实业务父任务 | 76 | 按业务任务号和一致业务字段归并后的任务 | +| 入库 | 38 | 从 `default02=1` 识别 | +| 出库 | 33 | 从 `default02=2` 识别 | +| 移库 | 5 | 从 `default02=7` 识别 | +| 重复扫描快照 | 142 | `240 - 98`,只审计,不重复调度 | + +最终报告必须同时展示原始口径和业务口径。旧报告的“240 个任务全部完成”保留为兼容对照,但明确标记为 `RAW_SNAPSHOT`,不能继续用它证明 240 个真实 WMS 业务任务完成。 + +## 4. 方案选择 + +### 4.1 采用:显式父任务阶段模型 + +一个业务任务只创建一个父任务执行对象。阶段变化不重新进入全局业务队列,也不创建第二个 WMS 任务;车辆、载货状态、资源预约、PLC 命令、换层作业、归档键和回调键都挂在同一父任务下。 + +优点:能够保证取货后不换车、业务完成与车辆释放分离、故障恢复不丢失货物状态、WMS 回调幂等,并能逐阶段展示真实规划路径。 + +### 4.2 不采用:给现有单段路线简单追加返回路径 + +单一节点列表无法表达取货确认、载货状态、业务完成时刻和自动回前端边界。PLC 重试或重启时也无法判断应该重发哪一阶段。 + +### 4.3 不采用:把各阶段拆成独立任务 + +独立任务会重新参与选车,可能出现货物已经在车辆上却换车;还会生成多次归档和 WMS 回调,不能代表生产队列的阶段更新语义。 + +## 5. 输入归并 + +新增 `HistoricalBusinessTaskAssembler`,职责仅是把历史扫描快照归并成可调度业务输入。 + +归并规则: + +1. 原始行按 `create_date/snapshot_row/guid` 稳定排序; +2. 相同 GUID 的重复扫描只保留状态演进证据,不重复创建任务; +3. `task_type=3` 归类为车辆运行证据,不创建 WMS 业务父任务; +4. `task_type=1/2` 按 `task_no`、`cargo_pick_site`、`cargo_place_site` 合并; +5. 从组内 `default02=1/2/7` 解析唯一业务类型; +6. 组内端点、业务类型或优先级出现互相冲突时,关闭为 `CONFLICTING_HISTORY_SNAPSHOTS`,不得任意选一条; +7. 最早 `create_date` 控制业务到达,最晚历史 `finish_date` 只用于耗时比较; +8. 历史 `agv_id/path_info/task_type/default06/default08` 作为阶段对照证据,不强制本次选车和路径。 + +输出同时包含: + +- `rawSnapshotCount`; +- `uniqueQueueCount`; +- `duplicateSnapshotCount`; +- `businessTaskCount`; +- `operationalQueueCount`; +- 每个父任务关联的原始 GUID 和扫描行。 + +## 6. 领域模型 + +### 6.1 执行模式 + +仿真输入增加明确模式: + +- `RAW_SNAPSHOT`:兼容旧的 240 行单段回放,只用于结果对照; +- `SOURCE_ALIGNED_BUSINESS`:默认模式,执行归并后的二段式父任务。 + +两个模式不得混用终端计数。 + +### 6.2 父任务阶段 + +新增 `BusinessTaskPhase`: + +```text +WAITING_ASSIGNMENT +FLOOR_TRANSFER_TO_PICKUP +EMPTY_TO_PICKUP +PICKUP_CONFIRMED +LOADED_RETURN_TO_SOURCE_FRONT +LOADED_TO_PLACE +CARGO_DEPOSITED +EMPTY_RETURN_TO_TARGET_FRONT +BUSINESS_COMPLETED +VEHICLE_RELEASED +FAILED +``` + +不需要的阶段直接跳过,但必须记录跳过原因,例如 `SOURCE_IS_FRONT`、`TARGET_IS_FRONT` 或 `SITE_NOT_IN_CARGO_GROUP`。 + +### 6.3 载货状态 + +新增 `CargoState`: + +```text +EMPTY -> LOADED -> DEPOSITED +``` + +- `PICKUP_CONFIRMED` 后从 `EMPTY` 变为 `LOADED`; +- `CARGO_DEPOSITED` 后从 `LOADED` 变为 `DEPOSITED`; +- 载货后车辆必须与父任务保持绑定; +- `LOADED` 状态下不允许因普通调度等待而换车。 + +### 6.4 阶段证据 + +每个阶段保存: + +```text +phase +fromSite +toSite +plannedPath +startedMillis +completedMillis +holdCount +retryCount +completionReason +``` + +父任务的 `plannedPath` 不再是一个模糊的总列表;报告可以拼接完整移动轨迹,但必须保留阶段边界。 + +## 7. 调度与执行流程 + +### 7.1 选车 + +父任务到达后,中心调度以业务取货点作为源点统一评估车队: + +1. 当前车辆能否直接到取货点; +2. 不同楼层时能否通过现有 `FloorTransferGateway` 到取货层; +3. 车辆是否在线、同步、无故障、非低电量且未绑定其他任务; +4. 后续业务路径和需要的通道前端是否存在。 + +选车仍使用 `VehicleAssignmentService`。历史 `agv_id` 只用于比较。选定后车辆锁定到父任务;换层完成后从实际出口重规划。 + +### 7.2 阶段路径 + +中心调度为每个需要移动的阶段独立调用 `GridPathPlanner`: + +- `EMPTY_TO_PICKUP`:车辆实际位置到业务取货点; +- `LOADED_RETURN_TO_SOURCE_FRONT`:出库/移库取货点到源通道前端; +- `LOADED_TO_PLACE`:当前实际站点到业务放货点; +- `EMPTY_RETURN_TO_TARGET_FRONT`:入库/移库放货点到目标通道前端。 + +阶段转换后必须基于车辆实际位置重新规划,不能复用历史 `path_info`,也不能把多个阶段一次性乐观预约。 + +### 7.3 资源和 PLC + +每个移动阶段复用现有机制: + +```text +规划阶段路径 + -> 申请走廊方向预约 + -> 节点/边/对向冲突裁决 + -> PLC 发送 + -> ACK + -> 车辆移动一条边 + -> 释放上一节点和边 +``` + +PLC 幂等键至少包含 `parentTaskId + phase + routeVersion + edgeIndex`。同一阶段同一路段失败重试不能推进位置;阶段切换后旧 ACK 不能完成新阶段。 + +### 7.4 阶段完成边界 + +- 到取货点并收到最后 ACK:记录 `PICKUP_CONFIRMED`,载货状态变为 `LOADED`; +- 到放货点并收到最后 ACK:记录 `CARGO_DEPOSITED`,产生一次业务归档和一次 WMS 回调; +- 入库目标存在不同前端:继续执行空车自动返回,业务完成事实保持不变; +- 自动返回完成:释放车辆并完成车务阶段; +- 出库送达业务出口:业务完成、归档、回调并释放车辆; +- 移库同时应用源端取货前端和目标端放货后返回规则。 + +## 8. HOLD、重派和故障 + +### 8.1 取货前 + +车辆尚未取货时: + +- 资源冲突产生 HOLD,保留阶段等待; +- 车辆永久故障可以释放绑定并重新选车; +- 断线、重启未同步先等待恢复;超过明确恢复策略后才重派; +- 低电量车辆不参与首次分配。 + +### 8.2 取货后 + +载货状态为 `LOADED` 时: + +- 车辆故障、断线、重启未同步均保持同车和父任务绑定; +- 不允许中心调度自动把货物改派给另一辆车; +- 恢复后从车辆真实位置重规划当前阶段; +- 需要人工倒货的场景不在离线算法中伪造,关闭为 `MANUAL_CARGO_RECOVERY_REQUIRED`; +- PLC UNKNOWN 保留原命令和资源所有权,不能盲目创建新命令。 + +### 8.3 通道前端和无路 + +- 需要前端阶段但映射不存在:`UNKNOWN_CHANNEL_FRONT`; +- 前端不在完整地图:`UNKNOWN_MAP_SITE`; +- 当前阶段永久无路:`NO_ROUTE_`; +- 前端暂时被占用:HOLD,不能判失败; +- 对向走廊被占用:HOLD,等待方向所有权释放。 + +## 9. 归档与回调幂等 + +父任务只有一个业务完成键,即 `businessTaskId`。不论阶段重试、重复 ACK、重启恢复或自动返回状态重复上报: + +- `SimulationArchive.completeBusiness` 只能产生一条业务归档; +- `IdempotentWmsCompletionReceiver.receive` 只能产生一次业务效果; +- 自动返回事件不能再次归档或回调; +- 车辆释放可以晚于业务完成,但不能提前于资源释放; +- 结果同时保存 `businessCompletedMillis` 和 `vehicleReleasedMillis`。 + +报告分别计算: + +- 业务等待时间:到达至首次选车; +- 业务完成时间:到达至 `CARGO_DEPOSITED`/业务出口送达; +- 车辆占用时间:首次绑定至 `VEHICLE_RELEASED`; +- 自动返回耗时:`CARGO_DEPOSITED` 至 `VEHICLE_RELEASED`。 + +历史 `finish_date` 只与业务完成时间比较,不能驱动阶段结束。 + +## 10. 输出与可视化 + +### 10.1 汇总 + +审计台顶部同时显示: + +- 原始 240 行、唯一 98 GUID、142 重复快照; +- 76 个业务任务及入库/出库/移库分布; +- 17 个车辆运行作业; +- 业务完成/等待/失败; +- 业务回调数、重复回调效果数; +- 自动返回完成/等待/失败; +- 资源双占、对向换边和死锁。 + +### 10.2 任务检查器 + +每个业务任务显示: + +- 关联的历史扫描行和 GUID; +- 历史阶段与本次阶段的对应关系; +- 历史车辆与本次选择车辆; +- 业务源点、目标点、源前端、目标前端; +- 当前载货状态和当前阶段; +- 每个阶段的规划路径、HOLD、重试和耗时; +- 业务完成时间与车辆释放时间; +- 历史耗时与本次业务耗时差异。 + +### 10.3 地图和事件 + +地图按阶段分别绘制路径,不把源点到目标点画成一条无法解释的总线。事件至少包括: + +```text +BUSINESS_TASK_ARRIVED +PHASE_ASSIGNED +PICKUP_CONFIRMED +SOURCE_FRONT_RETURN_STARTED +SOURCE_FRONT_REACHED +CARGO_DEPOSITED +WMS_CALLBACK +TARGET_FRONT_RETURN_STARTED +TARGET_FRONT_REACHED +BUSINESS_ARCHIVED +VEHICLE_RELEASED +PHASE_HOLD +PHASE_RETRY +PHASE_REPLAN +``` + +现有换层事件继续保留,并标明它属于哪个业务阶段。 + +## 11. 兼容性 + +- 现有 `RAW_SNAPSHOT` 240 行回放及其报告保留,作为旧算法对照; +- 新审计台默认使用 `SOURCE_ALIGNED_BUSINESS`; +- 现有换层领域模型和网关不修改设备语义,只增加父任务阶段关联; +- 现有节点、边、走廊、PLC 故障计划和车辆故障事件继续复用; +- 未提供通道前端快照的自定义点对点请求按普通点对点任务执行,并明确显示 `NO_CHANNEL_OPERATION`,不能猜测前端; +- 本次只修改 `javaTest`,不修改生产数据库、PLC 地址或 `BaseSRgvThread`。 + +## 12. 测试与验收 + +所有行为按测试驱动实现,必须先观察测试因缺少功能而失败。 + +### 12.1 输入归并 + +1. 240 行得到 98 个唯一 GUID、142 个重复快照; +2. 得到 76 个业务父任务:38 入库、33 出库、5 移库; +3. 17 个 `task_type=3` GUID 不进入 WMS 业务计数; +4. 同一任务多个历史阶段只创建一个父任务; +5. 冲突字段形成明确输入失败。 + +### 12.2 阶段执行 + +1. 入库目标在巷道内部时,货物完成后同车返回目标前端; +2. 出库源点在巷道内部时,同车先取货、返回源前端,再送业务目标; +3. 前端等于货位或站点不属于货位组时跳过多余阶段; +4. 每个阶段重新规划并逐段释放资源; +5. 跨层阶段继续通过 `FloorTransferGateway`,不生成普通跨层边。 + +### 12.3 故障和幂等 + +1. 第二阶段 PLC 首发失败只重试当前阶段; +2. 载货后断线、重启、故障不换车; +3. 重复 ACK 不重复推进阶段; +4. 入库业务回调后自动返回失败不重复回调; +5. 一个父任务只有一条业务归档和一次 WMS 业务效果; +6. 未知前端和永久无路按阶段原因关闭; +7. 全流程无资源双占、对向换边和死锁。 + +### 12.4 完整回放 + +完整验收至少输出: + +- 240 行原始快照的识别、去重和归并统计; +- 76 个业务任务的完成、等待、失败数量; +- 每辆车完成的业务任务数量; +- 每个任务的重新选车、换层和各阶段规划路径; +- HOLD 与 PLC 重试的阶段和原因; +- 业务完成时间与车辆释放时间; +- 历史耗时与仿真业务耗时差异; +- 归档遗漏数、重复归档数和重复 WMS 业务效果数; +- 自动返回未完成数; +- 资源双占、对向换边和死锁数量。 + +验收目标是 76 个可执行业务任务全部正确完成,原始 240 行全部被识别且没有被静默丢弃。若真实拓扑或输入冲突导致关闭,报告必须保留明确任务和原因,不能为了满足完成数伪造路线或完成事实。 + +## 13. 非目标 + +- 不在 `javaTest` 中连接现场 PLC、生产 WMS 或写入生产 SQL Server; +- 不修改 `BaseSRgvThread` 已有现场下发逻辑; +- 不把模拟时间当作现场 ETA 承诺; +- 不模拟人工倒货或故障车货物转移; +- 不用历史完成状态直接完成本次任务; +- 不用历史 `path_info` 替代中心算法重新规划。