18 KiB
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.javajavaTest/src/test/java/com/ft546/wcs/demo/TrafficControllerTest.javajavaTest/src/test/java/com/ft546/wcs/demo/LegacyDispatchCenterTest.javajavaTest/src/test/java/com/ft546/wcs/demo/QueueLifecycleTest.javajavaTest/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:
- static map obstacles,
- disconnected vehicle current sites,
- 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:
- call admission exactly once,
- freeze and sort vehicle IDs,
- add any avoidance queue returned by traffic control,
- only send a queue when
PATH_READY, - leave HOLD queues unchanged,
- call protocol once per unissued ready queue,
- 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"