docs(javaTest): plan dispatch visualizer implementation
This commit is contained in:
@@ -0,0 +1,450 @@
|
|||||||
|
# javaTest Dispatch Visualizer 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:** Build a localhost-only HTML audit console that runs the existing Java scheduler for arbitrary A-to-B tasks and the complete 240-task historical workload, then renders selected vehicles, planned paths, tick movement, failures, and safety evidence.
|
||||||
|
|
||||||
|
**Architecture:** Add a focused point-to-point input factory beside the simulation domain, a visualizer service that invokes the existing simulator, a deterministic map-to-JSON mapper, and a Java 8 `HttpServer` that serves same-origin static assets and APIs. The browser never replans routes; it draws `plannedPath`, map coordinates, and `MOVE` events returned by Java.
|
||||||
|
|
||||||
|
**Tech Stack:** Java 8, Maven, Jackson 2.17.2, JUnit 5.10.2, JDK `HttpServer`, HTML5, CSS, vanilla JavaScript, SVG, Playwright for browser QA.
|
||||||
|
|
||||||
|
## Global Constraints
|
||||||
|
|
||||||
|
- Work only under `javaTest` plus the approved design/plan documents.
|
||||||
|
- Reuse `HistoricalDispatchSimulator`, `GridPathPlanner`, `VehicleAssignmentService`, traffic arbitration, PLC simulation, archive, and WMS idempotency logic.
|
||||||
|
- Do not add Spring, Node build dependencies, database connections, PLC connections, or WMS connections.
|
||||||
|
- Bind the visualizer to `127.0.0.1`; default port is `18084`.
|
||||||
|
- Do not accept arbitrary filesystem paths or execute user-supplied commands.
|
||||||
|
- Historical `agv_id` and `path_info` are comparison inputs only.
|
||||||
|
- The UI must distinguish approach path, service path, and traversed path.
|
||||||
|
- The UI must remain an offline audit tool and must not claim to guarantee production safety.
|
||||||
|
- Preserve unrelated worktree changes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 1: Point-to-Point Simulation Input and Fault Presets
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/PointToPointSimulationInputFactory.java`
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/PointToPointSimulationRequest.java`
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/PointToPointSimulationService.java`
|
||||||
|
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/PointToPointSimulationServiceTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `HistoricalSimulationInput`, `HistoricalDispatchSimulator`, `SimulationConfig`, and `DeterministicFaultPlan`.
|
||||||
|
- Produces: `PointToPointSimulationInputFactory.create(HistoricalSimulationInput, String, String, DispatchTask.TaskType, int, String, List<String>)` and `PointToPointSimulationService.run(PointToPointSimulationRequest)`.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write the failing service tests**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
void runsOneTaskWithTheRealPlannerAndIgnoresHistoricalPath() {
|
||||||
|
HistoricalSimulationInput base = loadBaseInput();
|
||||||
|
HistoricalSimulationTask sample = base.getTasks().get(0);
|
||||||
|
PointToPointSimulationRequest request = PointToPointSimulationRequest.valid(
|
||||||
|
sample.getPhysicalSource(), sample.getPhysicalTarget(), "INBOUND", 1, 100,
|
||||||
|
"HISTORY-CAR", Arrays.asList("WRONG", "REFERENCE"), "NONE");
|
||||||
|
|
||||||
|
HistoricalDispatchSimulationResult result = service(base).run(request);
|
||||||
|
SimulationTaskResult task = result.getTaskResults().get(0);
|
||||||
|
|
||||||
|
assertEquals(SimulationTaskStatus.COMPLETED, task.getStatus());
|
||||||
|
assertEquals(sample.getPhysicalSource(), task.getPlannedPath().get(0));
|
||||||
|
assertEquals(sample.getPhysicalTarget(),
|
||||||
|
task.getPlannedPath().get(task.getPlannedPath().size() - 1));
|
||||||
|
assertFalse(task.getPlannedPath().equals(Arrays.asList("WRONG", "REFERENCE")));
|
||||||
|
assertEquals(0, result.getInvariants().totalSafetyViolations());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsUnknownAndIdenticalEndpointsBeforeSimulation() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> service(base).run(
|
||||||
|
PointToPointSimulationRequest.valid("UNKNOWN", validSite, "INBOUND", 1,
|
||||||
|
100, null, Collections.emptyList(), "NONE")));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> service(base).run(
|
||||||
|
PointToPointSimulationRequest.valid(validSite, validSite, "INBOUND", 1,
|
||||||
|
100, null, Collections.emptyList(), "NONE")));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run the focused test and confirm it fails**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn "-Dtest=PointToPointSimulationServiceTest" test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: test compilation fails because the visualizer request/service/factory do not exist.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Implement the request and package-local input factory**
|
||||||
|
|
||||||
|
The factory creates one `HistoricalQueueRecord` and one `HistoricalSimulationTask` with ID
|
||||||
|
`VISUALIZER-TASK-1`, current fixed simulation time, request business fields, and the base input's map,
|
||||||
|
traffic map, and initial vehicles. It validates that both sites exist and differ.
|
||||||
|
|
||||||
|
```java
|
||||||
|
public HistoricalSimulationInput create(HistoricalSimulationInput base,
|
||||||
|
String sourceSite,
|
||||||
|
String targetSite,
|
||||||
|
DispatchTask.TaskType taskType,
|
||||||
|
int priority,
|
||||||
|
String historicalVehicleId,
|
||||||
|
List<String> historicalPath) {
|
||||||
|
// Construct the comparison record and one simulation task here, inside the
|
||||||
|
// simulation package where package-private constructors are intentionally visible.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`PointToPointSimulationRequest` normalizes task type, acceleration, empty comparison fields, and
|
||||||
|
fault preset. It rejects priority outside `0..9`, acceleration other than `10/100`, and unknown enums.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Implement fault presets and run the real simulator**
|
||||||
|
|
||||||
|
```java
|
||||||
|
public HistoricalDispatchSimulationResult run(PointToPointSimulationRequest request) {
|
||||||
|
HistoricalSimulationInput input = inputFactory.create(/* normalized request fields */);
|
||||||
|
return simulator.simulate(input,
|
||||||
|
new SimulationConfig(request.getAcceleration(), 30_000L, 3, 200, 5_000),
|
||||||
|
faultPlanFor(request.getFaultPreset()));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`PLC_FIRST_SEND_FAILURE` injects `SEND_FAILURE` on attempt 1 for `VISUALIZER-TASK-1`.
|
||||||
|
Vehicle presets affect all initial vehicles at tick 0 and restore/connect/synchronize at tick 2 so the
|
||||||
|
selected task actually demonstrates the requested recovery path.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Run focused tests and commit**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn "-Dtest=PointToPointSimulationServiceTest" test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all focused tests pass.
|
||||||
|
|
||||||
|
Commit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/simulation/PointToPointSimulationInputFactory.java javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/PointToPointSimulationServiceTest.java
|
||||||
|
git commit -m "feat(javaTest): add point-to-point simulation service"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Visualization DTO Mapping
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java`
|
||||||
|
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `HistoricalSimulationInput` and `HistoricalDispatchSimulationResult`.
|
||||||
|
- Produces: `Map<String, Object> bootstrap(HistoricalSimulationInput)` and `Map<String, Object> result(HistoricalSimulationInput, HistoricalDispatchSimulationResult)`.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write failing mapping tests**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
void mapsSitesTasksEventsAndSafetyEvidenceWithoutReplanning() {
|
||||||
|
Map<String, Object> payload = mapper.result(input, result);
|
||||||
|
List<Map<String, Object>> tasks = castList(payload.get("tasks"));
|
||||||
|
List<Map<String, Object>> events = castList(payload.get("events"));
|
||||||
|
|
||||||
|
assertEquals(result.getTaskResults().get(0).getPlannedPath(), tasks.get(0).get("plannedPath"));
|
||||||
|
assertTrue(events.stream().anyMatch(event -> "ASSIGN".equals(event.get("eventType"))));
|
||||||
|
assertTrue(events.stream().anyMatch(event -> "MOVE".equals(event.get("eventType"))));
|
||||||
|
assertEquals(0, castMap(payload.get("invariants")).get("totalSafetyViolations"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void bootstrapReturnsSortedSearchableSitesAndInitialVehicles() {
|
||||||
|
Map<String, Object> payload = mapper.bootstrap(input);
|
||||||
|
assertFalse(castList(payload.get("sites")).isEmpty());
|
||||||
|
assertEquals("OFFLINE_IN_MEMORY_SIMULATION", payload.get("mode"));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run the mapping tests and confirm failure**
|
||||||
|
|
||||||
|
Run `mvn "-Dtest=SimulationVisualizationMapperTest" test`.
|
||||||
|
|
||||||
|
Expected: compilation failure because `SimulationVisualizationMapper` does not exist.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Implement stable ordered JSON maps**
|
||||||
|
|
||||||
|
Use `LinkedHashMap` and sorted task/site/event lists. Include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bootstrap: mode, boundary, plannerVersion, mapSha256, floors, sites, vehicles
|
||||||
|
result: summary, tasks, events, invariants, archive, wms
|
||||||
|
task: identity, endpoints, vehicles, historicalPath, plannedPath, status, timing, holds, retries
|
||||||
|
event: tick, simulationMillis, eventType, taskId, vehicleId, reason, fromSite, toSite
|
||||||
|
site: siteId, floor, x, y, channel
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not call `GridPathPlanner` from the mapper. The mapper copies the simulator result unchanged.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Run mapper and existing simulator tests**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn "-Dtest=SimulationVisualizationMapperTest,HistoricalDispatchSimulatorTest" test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all selected tests pass.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java
|
||||||
|
git commit -m "feat(javaTest): map simulation evidence for visualization"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Local HTTP Server and APIs
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerApplication.java`
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServer.java`
|
||||||
|
- Create: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/VisualizerHttpException.java`
|
||||||
|
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: Task 1 services and Task 2 mapper.
|
||||||
|
- Produces: `start()`, `stop()`, `getPort()`, `/api/health`, `/api/bootstrap`, `/api/simulations/point-to-point`, `/api/simulations/historical`, and static asset responses.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Write failing HTTP contract tests**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
void servesHealthBootstrapAndPointToPointSimulation() throws Exception {
|
||||||
|
server = fixture.startOnPort(0);
|
||||||
|
assertEquals(200, get("/api/health").status);
|
||||||
|
assertTrue(get("/api/bootstrap").body.contains("OFFLINE_IN_MEMORY_SIMULATION"));
|
||||||
|
|
||||||
|
HttpResult response = postJson("/api/simulations/point-to-point", validRequestJson());
|
||||||
|
assertEquals(200, response.status);
|
||||||
|
assertTrue(response.body.contains("plannedPath"));
|
||||||
|
assertTrue(response.body.contains("MOVE"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsUnknownRoutesMethodsOversizedBodiesAndConcurrentRuns() {
|
||||||
|
assertEquals(404, get("/api/unknown").status);
|
||||||
|
assertEquals(405, postJson("/api/health", "{}").status);
|
||||||
|
assertEquals(413, postJson("/api/simulations/point-to-point", oversizedBody()).status);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run the server tests and confirm failure**
|
||||||
|
|
||||||
|
Run `mvn "-Dtest=HistoricalDispatchVisualizerServerTest" test`.
|
||||||
|
|
||||||
|
Expected: compilation failure because the server classes do not exist.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Implement same-origin JSON handlers**
|
||||||
|
|
||||||
|
Use a fixed thread pool with a single simulation executor and an `AtomicBoolean` busy gate. Parse and
|
||||||
|
write JSON only through the existing Jackson `ObjectMapper`. Limit request bodies before parsing.
|
||||||
|
|
||||||
|
```java
|
||||||
|
private void writeJson(HttpExchange exchange, int status, Object payload) throws IOException {
|
||||||
|
byte[] body = objectMapper.writeValueAsBytes(payload);
|
||||||
|
exchange.getResponseHeaders().set("Content-Type", "application/json; charset=utf-8");
|
||||||
|
exchange.getResponseHeaders().set("Cache-Control", "no-store");
|
||||||
|
exchange.sendResponseHeaders(status, body.length);
|
||||||
|
exchange.getResponseBody().write(body);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Map request failures to stable codes and never return stack traces or absolute paths.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Implement the application bootstrap**
|
||||||
|
|
||||||
|
Load `shadow/history` with `HistoricalQueueLoader`, create the base input once, create services and mapper,
|
||||||
|
bind `127.0.0.1:<port>`, print the exact URL, and add a shutdown hook.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Run HTTP tests and commit**
|
||||||
|
|
||||||
|
Run `mvn "-Dtest=HistoricalDispatchVisualizerServerTest" test`.
|
||||||
|
|
||||||
|
Expected: all HTTP contract tests pass and test server threads terminate.
|
||||||
|
|
||||||
|
Commit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java
|
||||||
|
git commit -m "feat(javaTest): expose local dispatch visualizer API"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 4: Operational HTML Shell and Route Playback
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `javaTest/src/main/resources/visualizer/index.html`
|
||||||
|
- Create: `javaTest/src/main/resources/visualizer/assets/app.css`
|
||||||
|
- Create: `javaTest/src/main/resources/visualizer/assets/app.js`
|
||||||
|
- Modify: `javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServer.java`
|
||||||
|
- Test: `javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java`
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: `/api/bootstrap` and both simulation APIs.
|
||||||
|
- Produces: a responsive first-screen audit console with stable DOM IDs used by browser QA.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Extend static resource tests and confirm failure**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Test
|
||||||
|
void servesTheVisualizerShellAndAssets() throws Exception {
|
||||||
|
assertEquals(200, get("/").status);
|
||||||
|
assertTrue(get("/").body.contains("调度路径审计台"));
|
||||||
|
assertEquals("text/css; charset=utf-8", get("/assets/app.css").contentType);
|
||||||
|
assertEquals("application/javascript; charset=utf-8", get("/assets/app.js").contentType);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `mvn "-Dtest=HistoricalDispatchVisualizerServerTest#servesTheVisualizerShellAndAssets" test`.
|
||||||
|
|
||||||
|
Expected: 404 or missing resource failure.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Build the semantic HTML shell**
|
||||||
|
|
||||||
|
Create fixed sections with IDs:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<header id="app-header">...</header>
|
||||||
|
<nav id="mode-tabs" aria-label="审计模式">...</nav>
|
||||||
|
<section id="point-controls">...</section>
|
||||||
|
<main id="workspace">
|
||||||
|
<section id="route-stage"><svg id="route-map" role="img"></svg></section>
|
||||||
|
<aside id="task-inspector"></aside>
|
||||||
|
</main>
|
||||||
|
<section id="timeline-panel">...</section>
|
||||||
|
<section id="history-view" hidden>...</section>
|
||||||
|
<section id="safety-view" hidden>...</section>
|
||||||
|
```
|
||||||
|
|
||||||
|
The page starts as the usable tool, with no hero, marketing copy, nested cards, or decorative illustration.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Implement responsive operational styling**
|
||||||
|
|
||||||
|
Use stable grid tracks, maximum 8px radius, zero negative letter spacing, and no viewport-scaled font size.
|
||||||
|
Desktop keeps the route stage dominant; under 900px it stacks controls, route, inspector, and timeline.
|
||||||
|
Use neutral surfaces plus green, blue, amber, and red semantics.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Implement API state, route drawing, and tick playback**
|
||||||
|
|
||||||
|
`app.js` must:
|
||||||
|
|
||||||
|
```text
|
||||||
|
bootstrap -> populate searchable A/B datalists and metadata
|
||||||
|
run point task -> store result -> select task -> render path -> reset tick to 0
|
||||||
|
render path -> use returned site x/y/floor; draw approach, planned, and traversed layers
|
||||||
|
advance tick -> update vehicle marker, event focus, traversed edges, inspector, slider
|
||||||
|
play/pause/step -> use fixed timer and never mutate result data
|
||||||
|
```
|
||||||
|
|
||||||
|
Path coordinates use deterministic normalization with fixed SVG padding. Empty/unknown paths render an
|
||||||
|
explicit status panel instead of a blank SVG.
|
||||||
|
|
||||||
|
- [ ] **Step 5: Implement historical and safety modes**
|
||||||
|
|
||||||
|
Historical mode runs the 240-task API on command, renders KPI bands and a searchable/filterable task table,
|
||||||
|
and opens the selected task in the shared path/timeline renderer. Safety mode renders every invariant,
|
||||||
|
HOLD/retry reason distribution, archive count, and WMS idempotency. Any non-zero safety invariant changes the
|
||||||
|
global gate to `验收不通过`.
|
||||||
|
|
||||||
|
- [ ] **Step 6: Run server tests and commit**
|
||||||
|
|
||||||
|
Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn "-Dtest=HistoricalDispatchVisualizerServerTest" test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: all static and API tests pass.
|
||||||
|
|
||||||
|
Commit:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add javaTest/src/main/resources/visualizer javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServer.java javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java
|
||||||
|
git commit -m "feat(javaTest): add interactive dispatch audit console"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 5: Documentation, Full Regression, and Browser Acceptance
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `javaTest/README.md`
|
||||||
|
- Create: `javaTest/src/test/resources/visualizer/point-to-point-request.json`
|
||||||
|
- Create: `javaTest/visualizer-playwright.spec.js` only if the workspace Playwright runner requires a checked-in script; otherwise keep QA scripts in a temporary workspace path.
|
||||||
|
|
||||||
|
**Interfaces:**
|
||||||
|
- Consumes: completed visualizer application.
|
||||||
|
- Produces: reproducible launch instructions, Java regression evidence, and desktop/mobile browser evidence.
|
||||||
|
|
||||||
|
- [ ] **Step 1: Document launch and safety boundary**
|
||||||
|
|
||||||
|
Add the exact Maven command, URL, three modes, route color legend, and statement that this tool is offline
|
||||||
|
evidence rather than production/HIL acceptance.
|
||||||
|
|
||||||
|
- [ ] **Step 2: Run all Java tests**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean test
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: Surefire discovers the existing 630 tests plus all new tests; Failures `0`, Errors `0`.
|
||||||
|
|
||||||
|
- [ ] **Step 3: Start the visualizer server**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn compile exec:java \
|
||||||
|
-Dexec.mainClass=com.ft546.wcs.shadow.history.visualizer.HistoricalDispatchVisualizerApplication \
|
||||||
|
-Dexec.args="18084"
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: process remains running and prints `http://127.0.0.1:18084`.
|
||||||
|
|
||||||
|
- [ ] **Step 4: Run Playwright desktop acceptance**
|
||||||
|
|
||||||
|
At `1440x900`:
|
||||||
|
|
||||||
|
```text
|
||||||
|
open page -> bootstrap succeeds -> select known source/target -> run
|
||||||
|
assert selected vehicle and planned path are visible
|
||||||
|
assert SVG has path segments and a vehicle marker
|
||||||
|
step tick -> assert marker/traversed path changes
|
||||||
|
run 240 history -> assert 240 completed, 0 waiting, 0 failed
|
||||||
|
open first task -> assert its route and events render
|
||||||
|
open safety -> assert totalSafetyViolations is 0 and WMS idempotent is true
|
||||||
|
assert browser console has no errors
|
||||||
|
save screenshot
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: Run Playwright mobile and canvas-pixel acceptance**
|
||||||
|
|
||||||
|
At `390x844`, verify no overlap or horizontal overflow and save a screenshot. Sample pixels in the SVG route
|
||||||
|
area to prove the visualization is nonblank and contains multiple semantic colors.
|
||||||
|
|
||||||
|
- [ ] **Step 6: Run formatting and repository checks**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff --check
|
||||||
|
git status --short
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected: no whitespace errors; only intentional visualizer, test, README, spec, and plan changes are present.
|
||||||
|
|
||||||
|
- [ ] **Step 7: Commit the verified result**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add javaTest/README.md javaTest/src/main javaTest/src/test javaTest/src/main/resources/visualizer
|
||||||
|
git commit -m "docs(javaTest): document dispatch visualizer workflow"
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not stage unrelated `ruoyi-admin` or `docs/*.html` changes.
|
||||||
Reference in New Issue
Block a user