# 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)` 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 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 bootstrap(HistoricalSimulationInput)` and `Map result(HistoricalSimulationInput, HistoricalDispatchSimulationResult)`. - [ ] **Step 1: Write failing mapping tests** ```java @Test void mapsSitesTasksEventsAndSafetyEvidenceWithoutReplanning() { Map payload = mapper.result(input, result); List> tasks = castList(payload.get("tasks")); List> 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 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:`, 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
...
...
...
``` 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.