Files
FT546-WCS/docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md

18 KiB
Raw Blame History

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<String> 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:

@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
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<String, Site>

  • Produces: VehicleAssignmentDecision select(DispatchTask task, List<VehicleSnapshot> vehicles, Map<String, Site> sites)

  • Produces: DecisionCode.ASSIGNED, HOLD_NO_ELIGIBLE_VEHICLE, REJECT_INVALID_SITE

  • Produces: Optional<VehicleSnapshot> getVehicle(), DecisionCode getCode(), String getReason()

  • Step 1: Write failing assignment tests

Tests must assert:

assertEquals("2", service.select(task, Arrays.asList(crossFloor, sameFloor), sites)
    .getVehicle().get().getVehicleId());
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:

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
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<String> shortestPath(GridMap map, String start, String end, Set<String> blocked)

  • Produces: TrafficDecision plan(VehicleSnapshot vehicle, DispatchQueueEntry queue, Collection<VehicleSnapshot> vehicles, Collection<DispatchQueueEntry> queues)

  • Produces: PATH_READY, HOLD_NO_PATH, HOLD_CHANNEL_BLOCKED, AVOIDANCE_CREATED

  • Step 1: Write failing path and traffic tests

Cover:

assertEquals(Arrays.asList("A", "D", "C"), planner.shortestPath(map, "A", "C",
    Collections.singleton("B")));
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
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<DispatchQueueEntry> createPendingQueue(InMemoryWcsState state)

  • Produces: state methods to add/update vehicles and tasks, query active queues/history/events

  • Produces: SimulatedProtocolAdapter.ProtocolResult send(DispatchQueueEntry queue, List<String> path)

  • Step 1: Write failing coordinator tests

The first test:

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
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

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:

mvn test
mvn exec:java

and explicitly state the Demo never connects to production systems.

  • Step 4: Run focused and full verification

Run:

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
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:

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:

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
git add javaTest docs/superpowers/plans/2026-07-30-wcs-dispatch-traffic-demo.md
git commit -m "docs: finalize wcs demo verification"