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

429 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:
```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<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:
```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<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:
```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<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:
```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"
```