From 2623935b115f7070bff1f0f9d5a9e7ccbed958c1 Mon Sep 17 00:00:00 2001 From: zgw <13623675548@163.com> Date: Thu, 30 Jul 2026 11:14:36 +0800 Subject: [PATCH] docs: plan wcs dispatch traffic demo --- .../2026-07-30-wcs-dispatch-traffic-demo.md | 428 ++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md diff --git a/docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md b/docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md new file mode 100644 index 0000000..b259a3b --- /dev/null +++ b/docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md @@ -0,0 +1,428 @@ +# WCS Dispatch and Traffic Demo Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** 在工作树根目录创建独立的 Java 8 Maven Demo,用真实 JUnit 5 测试模拟 LEGACY WCS 的中心调度、确定性选车、路径交通管制、避让和队列生命周期。 + +**Architecture:** `LegacyDispatchCenter` 是单周期入口,只调用一次任务准入,再稳定处理车辆和活动队列。`VehicleAssignmentService`、`TrafficController`、`GridPathPlanner`、`SimulatedProtocolAdapter` 均为纯 Java 边界;`InMemoryWcsState` 保存模拟任务、队列、车辆和历史,不连接生产数据库或 PLC。 + +**Tech Stack:** Java 8、Maven 3.6、JUnit Jupiter 5.10.2、Maven Surefire 3.2.5、Exec Maven Plugin 3.1.0。 + +## Global Constraints + +- 生产控制模式保持 LEGACY;Demo 不修改生产 WCS 源码。 +- 不连接 SQL Server、PLC、WMS 或 Spring,不产生真实设备写入。 +- 不建立生产中央路权服务,不写交通运行表,不启用 SHADOW/ACTIVE。 +- 不使用 `default02` 至 `default08` 保存新增交通状态。 +- 所有源码使用 Java 8 可用语法与 API。 +- `mvn test` 的真实测试数必须大于 0,failures 和 errors 必须为 0。 +- 车辆资格固定为 connected、`workMode=5`、`status=1`、`allowSend=true`、`battery>30`。 +- 选择顺序固定为同层、绝对楼层差、Y 距离、X 距离、vehicleId。 +- 队列状态保持旧语义:新建 `issued=0/executionStatus=1`,下发 `1/3`,完成 `1/4` 后归档删除。 + +--- + +## File Map + +### Build and documentation + +- `javaTest/pom.xml`:独立 Maven 构建、JUnit 5、Surefire、Exec 插件。 +- `javaTest/README.md`:运行方式、场景、LEGACY 安全边界。 + +### Domain + +- `javaTest/src/main/java/com/ft546/wcs/demo/model/Site.java`:站点、楼层和坐标值对象。 +- `javaTest/src/main/java/com/ft546/wcs/demo/model/VehicleSnapshot.java`:不可变车辆执行快照。 +- `javaTest/src/main/java/com/ft546/wcs/demo/model/DispatchTask.java`:业务任务及准入状态。 +- `javaTest/src/main/java/com/ft546/wcs/demo/model/DispatchQueueEntry.java`:活动队列及旧状态码流转。 +- `javaTest/src/main/java/com/ft546/wcs/demo/model/DecisionCode.java`:调度和交通决策码。 + +### State and scheduling + +- `javaTest/src/main/java/com/ft546/wcs/demo/repository/InMemoryWcsState.java`:车辆、任务、队列、历史和每周期事件。 +- `javaTest/src/main/java/com/ft546/wcs/demo/center/VehicleAssignmentDecision.java`:确定性选车结果和理由。 +- `javaTest/src/main/java/com/ft546/wcs/demo/center/VehicleAssignmentService.java`:车辆资格过滤和稳定比较器。 +- `javaTest/src/main/java/com/ft546/wcs/demo/center/QueueAdmissionService.java`:每周期把一个待处理任务转为活动队列。 +- `javaTest/src/main/java/com/ft546/wcs/demo/center/CycleResult.java`:单周期已处理车辆和事件快照。 +- `javaTest/src/main/java/com/ft546/wcs/demo/center/LegacyDispatchCenter.java`:单周期中心调度入口。 + +### Traffic and protocol + +- `javaTest/src/main/java/com/ft546/wcs/demo/traffic/GridMap.java`:站点、边和静态障碍。 +- `javaTest/src/main/java/com/ft546/wcs/demo/traffic/GridPathPlanner.java`:确定性广度优先最短路。 +- `javaTest/src/main/java/com/ft546/wcs/demo/traffic/TrafficDecision.java`:路径、HOLD 或避让结果。 +- `javaTest/src/main/java/com/ft546/wcs/demo/traffic/TrafficController.java`:剩余路径锁、离线点、通道冲突和避让去重。 +- `javaTest/src/main/java/com/ft546/wcs/demo/simulation/SimulatedProtocolAdapter.java`:可注入失败的无 I/O 下发适配器。 +- `javaTest/src/main/java/com/ft546/wcs/demo/DemoApplication.java`:构造场景、推进周期和打印结果。 + +### Tests + +- `javaTest/src/test/java/com/ft546/wcs/demo/VehicleAssignmentServiceTest.java` +- `javaTest/src/test/java/com/ft546/wcs/demo/TrafficControllerTest.java` +- `javaTest/src/test/java/com/ft546/wcs/demo/LegacyDispatchCenterTest.java` +- `javaTest/src/test/java/com/ft546/wcs/demo/QueueLifecycleTest.java` +- `javaTest/src/test/java/com/ft546/wcs/demo/DemoScenarioTest.java` + +--- + +## Task 1: Bootstrap Real JUnit 5 and Domain Models + +**Files:** +- Create: `javaTest/pom.xml` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/model/Site.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/model/VehicleSnapshot.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/model/DispatchTask.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/model/DispatchQueueEntry.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/model/DecisionCode.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/demo/QueueLifecycleTest.java` + +**Interfaces:** +- Produces: `Site(String id, int floor, int x, int y, String channel)` +- Produces: `VehicleSnapshot(String vehicleId, int floor, String currentSite, int workMode, int status, int battery, boolean connected, boolean allowSend, boolean cargo)` +- Produces: `DispatchTask(String taskId, TaskType type, String sourceSite, String targetSite, int floor, int priority)` +- Produces: `DispatchQueueEntry.business(DispatchTask task, String vehicleId)` +- Produces: `markIssued(List path)`, `markCompleted()`, `isIssued()`, `getExecutionStatus()` + +- [ ] **Step 1: Add Maven build and write the failing lifecycle test** + +`pom.xml` must set `maven.compiler.source` and `target` to `1.8`, add `org.junit.jupiter:junit-jupiter:5.10.2`, configure `maven-surefire-plugin:3.2.5`, and configure `exec-maven-plugin:3.1.0` with main class `com.ft546.wcs.demo.DemoApplication`. + +The first test: + +```java +@Test +void queueUsesLegacyLifecycleCodes() { + DispatchTask task = new DispatchTask("T-1", TaskType.OUTBOUND, "A", "B", 1, 2); + DispatchQueueEntry queue = DispatchQueueEntry.business(task, "1"); + assertEquals(0, queue.getIssued()); + assertEquals(1, queue.getExecutionStatus()); + queue.markIssued(Arrays.asList("A", "B")); + assertEquals(1, queue.getIssued()); + assertEquals(3, queue.getExecutionStatus()); + queue.markCompleted(); + assertEquals(4, queue.getExecutionStatus()); +} +``` + +- [ ] **Step 2: Run the focused test and verify RED** + +Run: `cd javaTest && mvn -Dtest=QueueLifecycleTest test` + +Expected: FAIL during test compilation because domain classes do not exist. + +- [ ] **Step 3: Implement immutable inputs and guarded queue transitions** + +Implement nested enum `DispatchTask.TaskType { INBOUND, OUTBOUND, RELOCATION }`. Reject blank IDs, battery outside 0–100, non-positive floors and `markCompleted()` before issue with `IllegalStateException`. Copy path input into an unmodifiable list. + +- [ ] **Step 4: Run the focused test and verify GREEN** + +Run: `cd javaTest && mvn -Dtest=QueueLifecycleTest test` + +Expected: tests=1, failures=0, errors=0. + +- [ ] **Step 5: Commit** + +```bash +git add javaTest/pom.xml javaTest/src/main/java/com/ft546/wcs/demo/model javaTest/src/test/java/com/ft546/wcs/demo/QueueLifecycleTest.java +git commit -m "test: bootstrap wcs demo domain" +``` + +## Task 2: Deterministic Vehicle Assignment + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/center/VehicleAssignmentDecision.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/center/VehicleAssignmentService.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/demo/VehicleAssignmentServiceTest.java` + +**Interfaces:** +- Consumes: `DispatchTask`, `VehicleSnapshot`, `Map` +- Produces: `VehicleAssignmentDecision select(DispatchTask task, List vehicles, Map sites)` +- Produces: `DecisionCode.ASSIGNED`, `HOLD_NO_ELIGIBLE_VEHICLE`, `REJECT_INVALID_SITE` +- Produces: `Optional getVehicle()`, `DecisionCode getCode()`, `String getReason()` + +- [ ] **Step 1: Write failing assignment tests** + +Tests must assert: + +```java +assertEquals("2", service.select(task, Arrays.asList(crossFloor, sameFloor), sites) + .getVehicle().get().getVehicleId()); +``` + +```java +assertEquals("1", service.select(task, Arrays.asList(vehicle2, vehicle1), sites) + .getVehicle().get().getVehicleId()); +``` + +Also assert ineligible vehicles are excluded and a missing source site returns `REJECT_INVALID_SITE`. + +- [ ] **Step 2: Run and verify RED** + +Run: `cd javaTest && mvn -Dtest=VehicleAssignmentServiceTest test` + +Expected: FAIL because assignment types do not exist. + +- [ ] **Step 3: Implement stable assignment** + +Eligibility is exactly `connected && workMode == 5 && status == 1 && allowSend && battery > 30`. Sort by: + +```java +Comparator.comparingInt(candidate -> candidate.sameFloorPenalty) + .thenComparingInt(candidate -> candidate.floorDistance) + .thenComparingInt(candidate -> candidate.yDistance) + .thenComparingInt(candidate -> candidate.xDistance) + .thenComparing(candidate -> candidate.vehicle.getVehicleId()); +``` + +Distances use `Site` coordinates; never parse malformed numeric substrings from site IDs. Return a reason string containing the chosen comparison values. + +- [ ] **Step 4: Run and verify GREEN** + +Run: `cd javaTest && mvn -Dtest=VehicleAssignmentServiceTest test` + +Expected: all assignment tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/demo/center javaTest/src/test/java/com/ft546/wcs/demo/VehicleAssignmentServiceTest.java +git commit -m "feat: add deterministic vehicle assignment demo" +``` + +## Task 3: Grid Path Planning and Traffic Control + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/traffic/GridMap.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/traffic/GridPathPlanner.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/traffic/TrafficDecision.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/traffic/TrafficController.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/demo/TrafficControllerTest.java` + +**Interfaces:** +- Consumes: sites, adjacency, static blocks, vehicles and active queues +- Produces: `List shortestPath(GridMap map, String start, String end, Set blocked)` +- Produces: `TrafficDecision plan(VehicleSnapshot vehicle, DispatchQueueEntry queue, Collection vehicles, Collection queues)` +- Produces: `PATH_READY`, `HOLD_NO_PATH`, `HOLD_CHANNEL_BLOCKED`, `AVOIDANCE_CREATED` + +- [ ] **Step 1: Write failing path and traffic tests** + +Cover: + +```java +assertEquals(Arrays.asList("A", "D", "C"), planner.shortestPath(map, "A", "C", + Collections.singleton("B"))); +``` + +```java +TrafficDecision decision = controller.plan(car1, car1Queue, vehicles, queues); +assertEquals(DecisionCode.AVOIDANCE_CREATED, decision.getCode()); +assertEquals(9, decision.getAvoidanceQueue().get().getPriority()); +``` + +Also test offline current-site locking, no route, avoidance de-duplication, and successful re-plan after avoidance queue deletion. + +- [ ] **Step 2: Run and verify RED** + +Run: `cd javaTest && mvn -Dtest=TrafficControllerTest test` + +Expected: FAIL because traffic types do not exist. + +- [ ] **Step 3: Implement deterministic BFS and occupancy view** + +`GridPathPlanner` visits neighbors sorted by site ID. `TrafficController` blocks: + +1. static map obstacles, +2. disconnected vehicle current sites, +3. every other active queue path from that vehicle's current-site index onward. + +Before planning a business queue, detect a same-floor vehicle at a different site with the same target channel. If it has no active queue, create `DispatchQueueEntry.avoidance(...)` to a configured cache site, priority 9. If it already has an avoidance queue, return `HOLD_CHANNEL_BLOCKED` without creating another. + +- [ ] **Step 4: Run and verify GREEN** + +Run: `cd javaTest && mvn -Dtest=TrafficControllerTest test` + +Expected: all traffic tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/demo/traffic javaTest/src/test/java/com/ft546/wcs/demo/TrafficControllerTest.java +git commit -m "feat: simulate wcs traffic control" +``` + +## Task 4: In-Memory State, Admission, and Cycle Coordinator + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/repository/InMemoryWcsState.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/center/QueueAdmissionService.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/center/CycleResult.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/center/LegacyDispatchCenter.java` +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/simulation/SimulatedProtocolAdapter.java` +- Test: `javaTest/src/test/java/com/ft546/wcs/demo/LegacyDispatchCenterTest.java` + +**Interfaces:** +- Produces: `CycleResult runCycle()` +- Produces: `int getAdmissionInvocationCount()` +- Produces: `Optional createPendingQueue(InMemoryWcsState state)` +- Produces: state methods to add/update vehicles and tasks, query active queues/history/events +- Produces: `SimulatedProtocolAdapter.ProtocolResult send(DispatchQueueEntry queue, List path)` + +- [ ] **Step 1: Write failing coordinator tests** + +The first test: + +```java +CycleResult result = center.runCycle(); +assertEquals(1, admission.getInvocationCount()); +assertEquals(Arrays.asList("1", "2", "3"), result.getProcessedVehicleIds()); +assertEquals(1, state.getActiveQueues().size()); +``` + +Also test protocol failure leaves `issued=0/executionStatus=1`, the next cycle retries successfully, and reaching the destination archives and deletes the queue exactly once. + +- [ ] **Step 2: Run and verify RED** + +Run: `cd javaTest && mvn -Dtest=LegacyDispatchCenterTest test` + +Expected: FAIL because coordinator types do not exist. + +- [ ] **Step 3: Implement one-admission-per-cycle coordinator** + +`runCycle()` must: + +1. call admission exactly once, +2. freeze and sort vehicle IDs, +3. add any avoidance queue returned by traffic control, +4. only send a queue when `PATH_READY`, +5. leave HOLD queues unchanged, +6. call protocol once per unissued ready queue, +7. complete and archive an issued queue only when vehicle current site equals queue end site. + +`SimulatedProtocolAdapter.failNextSend()` injects one failure and records every attempted queue ID without I/O. + +- [ ] **Step 4: Run and verify GREEN** + +Run: `cd javaTest && mvn -Dtest=LegacyDispatchCenterTest,QueueLifecycleTest test` + +Expected: coordinator and lifecycle tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/demo/repository javaTest/src/main/java/com/ft546/wcs/demo/center javaTest/src/main/java/com/ft546/wcs/demo/simulation javaTest/src/test/java/com/ft546/wcs/demo/LegacyDispatchCenterTest.java +git commit -m "feat: add legacy dispatch cycle demo" +``` + +## Task 5: Executable Scenarios and End-to-End Test + +**Files:** +- Create: `javaTest/src/main/java/com/ft546/wcs/demo/DemoApplication.java` +- Create: `javaTest/src/test/java/com/ft546/wcs/demo/DemoScenarioTest.java` +- Create: `javaTest/README.md` + +**Interfaces:** +- Produces: `DemoApplication.runScenario(PrintStream out)` +- Produces: console lines beginning `CYCLE`, `ASSIGN`, `PATH`, `HOLD`, `AVOID`, `SEND`, `COMPLETE`, `SUMMARY` + +- [ ] **Step 1: Write failing end-to-end test** + +```java +ByteArrayOutputStream output = new ByteArrayOutputStream(); +DemoApplication.runScenario(new PrintStream(output)); +String text = new String(output.toByteArray(), StandardCharsets.UTF_8); +assertTrue(text.contains("ASSIGN")); +assertTrue(text.contains("AVOID")); +assertTrue(text.contains("HOLD")); +assertTrue(text.contains("SEND")); +assertTrue(text.contains("COMPLETE")); +assertTrue(text.contains("SUMMARY")); +``` + +- [ ] **Step 2: Run and verify RED** + +Run: `cd javaTest && mvn -Dtest=DemoScenarioTest test` + +Expected: FAIL because `DemoApplication` does not exist. + +- [ ] **Step 3: Implement the complete console scenario** + +Create a two-lane map with a cache point and three vehicles. Run sequential phases for deterministic assignment, same-channel avoidance, avoidance completion, protocol failure/retry, business completion and no-path HOLD. Print every event and a final summary containing cycle count, active queue count and history count. + +`README.md` must document: + +```bash +mvn test +mvn exec:java +``` + +and explicitly state the Demo never connects to production systems. + +- [ ] **Step 4: Run focused and full verification** + +Run: + +```bash +cd javaTest +mvn -Dtest=DemoScenarioTest test +mvn test +mvn exec:java +``` + +Expected: focused test passes; full Surefire summary has tests > 0, failures=0, errors=0; console output contains all required event types and exits 0. + +- [ ] **Step 5: Commit** + +```bash +git add javaTest/src/main/java/com/ft546/wcs/demo/DemoApplication.java javaTest/src/test/java/com/ft546/wcs/demo/DemoScenarioTest.java javaTest/README.md +git commit -m "feat: add executable wcs dispatch simulation" +``` + +## Task 6: Completion Audit + +**Files:** +- Verify: `javaTest/**` +- Verify: `docs/superpowers/specs/2026-07-30-wcs-dispatch-traffic-demo-design.md` + +**Interfaces:** +- Consumes: all earlier tasks +- Produces: objective-by-objective evidence + +- [ ] **Step 1: Verify repository and Java compatibility** + +Run: + +```bash +git rev-parse --is-inside-work-tree +mvn -f javaTest/pom.xml clean test +``` + +Expected: Git prints `true`; Maven exits 0 and compiles with source/target 1.8. + +- [ ] **Step 2: Verify real test counts** + +Inspect `javaTest/target/surefire-reports/TEST-*.xml`; sum `tests`, `failures`, `errors`, and `skipped`. + +Expected: tests > 0, failures=0, errors=0. + +- [ ] **Step 3: Verify executable behavior** + +Run: `mvn -f javaTest/pom.xml exec:java` + +Expected: output includes `CYCLE`, `ASSIGN`, `PATH`, `HOLD`, `AVOID`, `SEND`, `COMPLETE`, and `SUMMARY`, with process exit 0. + +- [ ] **Step 4: Verify production isolation** + +Run: + +```bash +git diff 7b36dfa --name-only +``` + +Expected: only `javaTest/**` and the implementation plan are added or modified; no production `ruoyi-*` code changes. + +- [ ] **Step 5: Final commit if verification updates documentation** + +```bash +git add javaTest docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md +git commit -m "docs: finalize wcs demo verification" +```