docs: plan wcs dispatch traffic demo

This commit is contained in:
zgw
2026-07-30 11:14:36 +08:00
parent 7b36dfa2a2
commit 2623935b11

View File

@@ -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<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"
```