From 97de1f9f26867d2bd064f63bdbedb88ed7a30a62 Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Thu, 6 Aug 2026 16:47:01 +0800 Subject: [PATCH] docs(javaTest): design bounded conflict-aware routing --- ...t-bounded-conflict-aware-routing-design.md | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-06-javaTest-bounded-conflict-aware-routing-design.md diff --git a/docs/superpowers/specs/2026-08-06-javaTest-bounded-conflict-aware-routing-design.md b/docs/superpowers/specs/2026-08-06-javaTest-bounded-conflict-aware-routing-design.md new file mode 100644 index 0000000..2cd771a --- /dev/null +++ b/docs/superpowers/specs/2026-08-06-javaTest-bounded-conflict-aware-routing-design.md @@ -0,0 +1,346 @@ +# javaTest 有界绕行与冲突感知路径规划设计 + +**日期:** 2026-08-06 +**状态:** 已完成方案讨论,等待书面确认 +**适用范围:** `javaTest` 历史任务中心调度仿真与调度路径审计页面 + +## 1. 背景 + +当前 `javaTest` 使用 `GridPathPlanner` 在完整地图上执行 BFS,得到边数最少且结果确定的路径。 +历史 `path_info` 只用于结果对照,不参与重新规划。240 条历史任务的初步结果为 137 条路径序列 +发生变化,但该数字不能直接证明路径优化有效: + +- 130 条历史路径的首尾与解析后的物理业务端点不一致; +- 2 条历史路径含未知或非邻接段; +- 剩余 108 条可公平比较的任务中,103 条完全相同,4 条路线不同但边数相同,只有 1 条更短; +- 可比较任务的平均边数仅从 10.14 降到 10.12。 + +因此,本设计不再用“路径是否不同”或受故障影响的实际完成时间证明优化效果,而是在最短物理 +距离附近选择交通冲突更少、路线重叠更少、转弯更少的候选路线,并提供完整、可解释的决策证据。 + +## 2. 目标与非目标 + +### 2.1 目标 + +1. 接近路径和服务路径都允许在最短物理距离 110% 范围内绕行。 +2. 只使用当前活动任务、节点占用、边预约和走廊方向判断交通风险,不使用历史热度。 +3. 按固定业务优先级比较候选路线,不使用难以解释和调参的加权总分。 +4. 同一交通资源连续造成 3 个调度周期 HOLD 后,从车辆当前位置重新规划剩余路线。 +5. 路线切换必须保持当前占用和预约安全,不能因重规划产生资源空窗。 +6. 将路线质量、交通执行和故障恢复分开统计。 +7. 相同地图、输入和交通快照必须产生相同结果。 + +### 2.2 非目标 + +1. 不用实际行驶时间或最终任务完成时间给路径质量打分。 +2. 不使用历史 `path_info` 约束新路线。 +3. 不在本次改造中改变现有车辆资格、低电量和车辆选择业务规则。 +4. 不把 PLC 失败、断线、低电量或车辆故障当成交通 HOLD。 +5. 不接入生产数据库、真实 PLC、真实 WMS 或实时交通服务。 + +## 3. 业务术语 + +- **接近路径:** 重新选中的车辆当前位置到任务物理起点 A 的路线。 +- **服务路径:** 任务物理起点 A 到物理终点 B 的路线。 +- **基准最短距离 D:** 在永久禁行条件下,起终点间可达路线的最小坐标距离。 +- **有界候选路线:** 物理距离不超过 `D * 1.10` 的无环路线。 +- **交通快照:** 某次调度决策时的车辆占用节点、边预约、走廊方向和其他活动路线。 +- **交通 HOLD:** 下一步因节点、边、对向或走廊方向冲突而暂时不能安全推进。 +- **相同阻塞:** 任务、车辆、阻塞资源标识和交通 HOLD 原因均相同。 + +## 4. 总体架构 + +### 4.1 `RouteCandidatePlanner` + +职责:根据完整 `GridMap`、起点、终点和永久禁行资源生成确定性的有界候选路线。 + +输入: + +- `GridMap` +- 起点、终点 +- 永久禁行节点或边 +- `RoutePlanningPolicy` + +输出: + +- 基准最短距离 +- 最短路线 +- 不超过 110% 的候选路线 +- 是否命中候选数量上限 + +该模块不读取车辆状态、交通预约、历史路径或任务优先级,只负责生成地图上合法的路线候选。 + +### 4.2 `RouteQualityEvaluator` + +职责:使用同一时刻的 `TrafficRouteSnapshot` 对候选路线进行可解释的业务排序。 + +输入:候选路线、交通快照、当前任务和车辆。 +输出:每条路线的 `RouteQuality` 以及最终 `RoutePlanDecision`。 + +### 4.3 `TrafficRouteSnapshot` + +职责:为一次规划决策提供不可变交通证据,避免候选路线使用不同时间点的数据。 + +包含: + +- 车辆当前占用节点及归属车辆; +- 已预约的有向边、方向和归属任务; +- 走廊方向预约及归属任务; +- 其他活动任务尚未完成的剩余路线; +- 当前任务和车辆自身已经合法持有的资源。 + +### 4.4 `HoldReroutePolicy` + +职责:只累计交通 HOLD,并在同一阻塞连续出现 3 个调度周期时触发重规划。 + +### 4.5 `RouteDecisionEvidence` + +职责:保存候选生成、路线评分、HOLD 触发、资源替换和最终选择的可审计证据,供 JSON 报告和 +HTML 页面展示。 + +## 5. 物理距离与候选路线 + +### 5.1 边距离 + +当前完整地图共有 8 层、1239 条边,不存在跨层边。相邻站点边距离按地图坐标的欧氏距离计算: + +```text +edgeDistance = sqrt((to.x - from.x)^2 + (to.y - from.y)^2) +pathDistance = sum(edgeDistance) +``` + +距离单位是地图坐标单位,不宣称为米。未来如增加跨层边,必须由地图元数据显式提供该边距离; +不得根据楼层编号虚构垂直距离。 + +### 5.2 候选生成算法 + +采用确定性的 K 最短无环路径方案: + +1. 使用 Dijkstra 计算基准最短路线和距离 `D`; +2. 按距离从小到大生成无环候选路线; +3. 下一候选距离超过 `D * 1.10` 时停止; +4. 最多保留 32 条候选; +5. 距离相同时按完整站点序列排序; +6. 未知节点、永久封闭道路和静态禁行资源不进入候选; +7. 当前临时占用和预约不从地图删除,只在质量评价阶段计入风险。 + +候选上限 32 是性能边界,不是业务评分。达到上限时在证据中写入 +`candidateLimitReached=true`,但仍从已生成候选中作出确定性选择。 + +### 5.3 接近路径与服务路径 + +现有 `VehicleAssignmentService` 继续负责车辆资格和选车,不在本设计中改变选车规则。选车完成后: + +1. 为选中车辆计算“当前位置到任务起点”的有界接近路径; +2. 独立计算“任务起点到任务终点”的有界服务路径; +3. 分别保存两段路线的最短距离、候选数量和选择原因; +4. 交通预约仍对拼接后的完整执行路线申请。 + +这样可以优化两段路线,同时避免本次改造无意改变已经验证过的自动选车行为。 + +## 6. 路线质量排序 + +每条候选路线生成以下固定顺序的比较元组: + +```text +( + oppositeDirectionConflictCount, + occupiedNodeCount, + incompatibleReservedEdgeCount, + activeRouteOverlapEdgeCount, + turnCount, + physicalDistance, + canonicalPath +) +``` + +排序为字典序,前一项不同时不再比较后一项。 + +### 6.1 指标定义 + +- `oppositeDirectionConflictCount`:候选有向边与其他任务已预约方向相反的数量。 +- `occupiedNodeCount`:候选未来节点中被其他车辆占用或独占预约的数量;当前车辆自身起点不计。 +- `incompatibleReservedEdgeCount`:候选边与现有容量或方向规则不兼容的预约数量;已计为对向冲突 + 的边不重复计数。 +- `activeRouteOverlapEdgeCount`:候选无向边与其他活动任务剩余路线重合的数量;这是未来竞争风险, + 不等同于已经发生的资源冲突。 +- `turnCount`:相邻两条同层边的方向向量发生变化的次数。 +- `physicalDistance`:第 5 节定义的坐标距离。 +- `canonicalPath`:用完整站点序列作最终稳定决胜。 + +安全规则仍由交通资源裁决执行。质量评价只决定优先尝试哪条路线,不能绕过节点容量、边方向或 +走廊预约。 + +## 7. HOLD 后重规划 + +### 7.1 触发条件 + +仅以下原因进入连续 HOLD 计数: + +- 下一节点被其他车辆占用或独占预约; +- 下一条边不可申请; +- 存在对向换边风险; +- 走廊方向预约冲突。 + +键为: + +```text +(simulationTaskId, vehicleId, blockingResourceId, holdReason) +``` + +连续第 1、2 次只保持 HOLD;连续第 3 次产生 `REROUTE_TRIGGER`。车辆发生移动、阻塞资源或原因 +改变、HOLD 消失、车辆被重新分配时清零。 + +PLC 发送失败或未 ACK、低电量、车辆故障和断线不进入该计数。 + +### 7.2 重规划范围 + +- 尚未到达任务起点:重新计算“当前位置到起点”的接近路径,同时基于同一交通快照重新评价 + “起点到终点”的服务路径。 +- 已进入服务路径:只计算“当前位置到任务终点”的剩余服务路径。 + +### 7.3 防止路线抖动 + +满足以下条件才允许切换: + +1. 新剩余路线与当前剩余路线不同; +2. 新路线不超过对应最短距离 110%; +3. 新路线的质量元组严格优于当前路线; +4. 新路线的路线级走廊预约能够安全替换; +5. 上次成功换路后车辆已经至少移动一个节点,或当前路线出现永久失效。 + +找不到严格更优路线时产生 `REROUTE_REJECTED` 并继续 HOLD,不关闭任务。 + +## 8. 预约替换与资源释放 + +新增原子语义的路线级走廊预约替换操作: + +```text +保留车辆当前占用节点和已进入边 +→ 以当前任务旧预约为可替换资源,试算新剩余路线的走廊方向 +→ 在不暴露中间状态的前提下提交走廊预约替换 +→ 成功后提交新 remainingPath +→ 释放旧路线不再使用的走廊预约 +``` + +如果试算或替换失败,旧路线和现有合法资源保持不变。不得先释放旧预约再申请新路线。 + +节点和边不因重规划而整条路线预占,仍由现有交通控制器在车辆移动前逐段申请,并在车辆通过后 +逐段释放。重规划不能释放车辆当前节点、正在通过的边或其他任务资源。该边界既保留现有安全 +语义,也避免因整条路线预占扩大资源等待和死锁风险。 + +## 9. 失败与降级规则 + +- 完整地图永久不可达:任务关闭为 `NO_ROUTE`。 +- 存在地图路线但所有候选都有临时冲突:选择风险最小候选,交给交通裁决决定 GRANT/HOLD。 +- 候选达到 32 条上限:从已有候选选择并记录上限证据。 +- HOLD 后无更优路线:保持当前路线和 HOLD。 +- PLC 失败或未 ACK:沿用原有协议重试,不触发路径重规划。 +- 车辆故障或断线:沿用重新选车流程;新车辆确定后重新规划接近路径和服务路径。 +- 低电量:车辆不再接收新任务;不把低电量伪装成交通冲突。 + +## 10. 事件与审计证据 + +保留现有事件,并新增: + +- `REROUTE_TRIGGER`:包含连续 HOLD 次数、阻塞资源和原因; +- `REROUTE`:包含旧剩余路线、新剩余路线、两者质量元组和选择原因; +- `REROUTE_REJECTED`:包含没有更优候选、超出 110% 或预约替换失败原因。 + +每次初始规划和重规划输出: + +- `shortestPhysicalDistance` +- `selectedPhysicalDistance` +- `detourRatio` +- `candidateCount` +- `candidateLimitReached` +- 质量元组各项指标 +- `decisionReason` +- `pathVersion` +- `planningStage`:`APPROACH` 或 `SERVICE` + +浏览器继续显示完整地图、历史路径、新规划路径和实际轨迹,并在检查器中分别显示接近路径和 +服务路径的选择证据。实际时间、完成时间和 PLC 重试放在“运行与故障恢复”区域,不进入路线评分。 + +## 11. 历史路线比较口径 + +每条历史任务先分类: + +- `COMPARABLE`:历史路径首尾与物理业务端点一致,且所有连续边都存在于权威地图; +- `ENDPOINT_MISMATCH`:历史首尾与物理业务端点不一致; +- `INVALID_HISTORY_PATH`:包含未知节点或非邻接段。 + +只有 `COMPARABLE` 任务进入路线质量统计。统计分类为: + +- 完全相同; +- 物理距离更短; +- 距离相同但冲突或转弯更少; +- 距离在 110% 内略长但冲突更少; +- 新路线质量更差。 + +`pathChanged` 继续保留节点序列语义以兼容现有报告,但不能再单独作为“优化成功”指标。 + +## 12. 验收门禁 + +### 12.1 路线算法门禁 + +1. 所有选中路线距离不超过对应最短物理距离 110%。 +2. 最终路线必须是已生成候选中的字典序最优质量元组。 +3. 临时冲突不能导致错误的 `NO_ROUTE`。 +4. 同一输入、地图和交通快照重复运行必须得到相同路线和原因。 +5. 历史端点不一致或存在无效段时不得计入优化比例。 + +### 12.2 HOLD 与资源门禁 + +1. 同一交通冲突连续 1、2 次不重规划,第 3 次触发。 +2. PLC 失败、低电量、故障和断线不能误触发交通重规划。 +3. 找不到严格更优路线时保持原路线。 +4. 路线切换失败不能丢失旧预约。 +5. 车辆移动前后节点、边和走廊资源均保持单一合法归属。 +6. 节点双占、资源超容量、对向换边和死锁为 0。 + +### 12.3 业务闭环门禁 + +1. 基准 240 条历史任务全部进入终态,不遗留活动或等待任务。 +2. 所有完成任务归档一次且仅一次。 +3. WMS 重复投递不产生重复业务效果。 +4. 失败任务必须有明确、可审计的失败原因。 + +## 13. 测试策略 + +### 13.1 单元测试 + +- 坐标距离和路线距离; +- 110% 边界内外候选过滤; +- 无环候选和 32 条数量上限; +- 固定质量元组排序; +- 转弯、重叠、占用和对向冲突计数; +- 相同输入的确定性; +- 历史路线三类可比性判定。 + +### 13.2 集成测试 + +- 最短路线有冲突,10% 内存在无冲突绕行,必须选择绕行; +- 无冲突绕行超过 10%,必须保留允许范围内的最优路线; +- 所有候选有临时冲突时保持可调度而非 `NO_ROUTE`; +- 连续第 3 次相同交通 HOLD 重规划; +- 阻塞资源变化后计数重新开始; +- 成功换路后移动一个节点前不再次普通重规划; +- 新预约失败时旧预约保持完整; +- PLC 重试和车辆故障不污染交通 HOLD 计数。 + +### 13.3 历史负载和浏览器验收 + +- 重新执行 240 条历史任务并输出 `COMPARABLE/ENDPOINT_MISMATCH/INVALID_HISTORY_PATH`; +- 报告接近路径和服务路径的候选、质量、绕行比例和重规划证据; +- 在完整地图中显示初始路线、HOLD 点和重规划后的剩余路线; +- 验证所有路线切换均能从事件和资源证据追溯; +- 验证归档、WMS 幂等和所有安全门禁。 + +## 14. 性能与兼容性 + +- 不新增生产依赖,保持 Java 8 编译目标。 +- 候选数量固定上限为 32,避免历史批量回放搜索量失控。 +- 现有 `plannedPath`、`pathChanged` 和历史证据字段保持兼容;新增字段只扩展 JSON。 +- 当前 BFS 行为保留为基准测试和最短边数对照,不再承担最终冲突感知选路职责。 +- 本设计只修改 `javaTest`,不直接替换生产 WCS 调度实现。