diff --git a/javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java b/javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java index f7a1432..4eddc6d 100644 --- a/javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java +++ b/javaTest/src/main/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapper.java @@ -2,6 +2,7 @@ package com.ft546.wcs.shadow.history.visualizer; import com.ft546.wcs.demo.model.Site; import com.ft546.wcs.demo.model.VehicleSnapshot; +import com.ft546.wcs.demo.traffic.GridMap; import com.ft546.wcs.shadow.history.HistoricalQueueDataset; import com.ft546.wcs.shadow.history.HistoricalQueueRecord; import com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationResult; @@ -41,6 +42,8 @@ public final class SimulationVisualizationMapper { DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss"); private final HistoricalQueueDataset dataset; + private final RouteTopologyEvidenceMapper routeTopology = + new RouteTopologyEvidenceMapper(); public SimulationVisualizationMapper(HistoricalQueueDataset dataset) { if (dataset == null) throw new IllegalArgumentException("dataset is required"); @@ -71,6 +74,8 @@ public final class SimulationVisualizationMapper { } payload.put("floors", new ArrayList(floors)); payload.put("sites", sites); + // 页面底图必须使用当前 Java 地图的真实邻接边,不能在浏览器中按坐标猜路。 + payload.put("edges", routeTopology.fullMapEdges(input.getGridMap())); List orderedVehicles = new ArrayList(input.getInitialVehicles()); @@ -96,7 +101,7 @@ public final class SimulationVisualizationMapper { List> tasks = new ArrayList>(); for (SimulationTaskResult task : orderedTasks) { tasks.add(task(task, definitions.get(task.getSimulationTaskId()), - result.getAcceleration())); + result.getAcceleration(), input.getGridMap())); } List> events = new ArrayList>(); for (SimulationTickEvent event : result.getEvents()) events.add(event(event)); @@ -145,7 +150,8 @@ public final class SimulationVisualizationMapper { private Map task(SimulationTaskResult task, HistoricalSimulationTask definition, - int acceleration) { + int acceleration, + GridMap gridMap) { HistoricalQueueRecord record = task.getRecord(); List historicalPath = historicalPath(record.getPathInfo()); Long historicalDuration = historicalDuration(task); @@ -170,6 +176,9 @@ public final class SimulationVisualizationMapper { value.put("simulatedVehicleId", task.getSimulatedVehicleId()); value.put("historicalPath", historicalPath); value.put("plannedPath", task.getPlannedPath()); + // 边差证据解释新旧路线在哪里重合或分叉,但不参与调度中心的规划结果。 + value.put("pathComparison", routeTopology.compare( + gridMap, historicalPath, task.getPlannedPath())); value.put("assignedVehicles", task.getAssignedVehicles()); value.put("status", task.getStatus().name()); value.put("reason", task.getReason()); diff --git a/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java b/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java index 3ec4620..a20f593 100644 --- a/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java +++ b/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/HistoricalDispatchVisualizerServerTest.java @@ -71,9 +71,13 @@ class HistoricalDispatchVisualizerServerTest { assertEquals(Boolean.FALSE, map(health.body).get("simulationBusy")); assertEquals(200, bootstrap.status); assertEquals("OFFLINE_IN_MEMORY_SIMULATION", map(bootstrap.body).get("mode")); + assertFalse(list(map(bootstrap.body).get("edges")).isEmpty()); assertEquals(200, simulation.status); assertEquals(1, map(map(simulation.body).get("summary")).get("completedCount")); assertFalse(list(map(simulation.body).get("events")).isEmpty()); + Map taskEvidence = map( + list(map(simulation.body).get("tasks")).get(0)); + assertTrue(taskEvidence.containsKey("pathComparison")); assertTrue(simulation.body.contains("plannedPath")); assertTrue(simulation.body.contains("MOVE")); } diff --git a/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java b/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java index 40e5563..bc7c900 100644 --- a/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java +++ b/javaTest/src/test/java/com/ft546/wcs/shadow/history/visualizer/SimulationVisualizationMapperTest.java @@ -12,8 +12,10 @@ import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -45,6 +47,7 @@ class SimulationVisualizationMapperTest { void bootstrapReturnsSortedSearchableSitesAndInitialVehicles() { Map payload = mapper().bootstrap(input); List> sites = listOfMaps(payload.get("sites")); + List> edges = listOfMaps(payload.get("edges")); List> vehicles = listOfMaps(payload.get("vehicles")); assertEquals("OFFLINE_IN_MEMORY_SIMULATION", payload.get("mode")); @@ -56,6 +59,21 @@ class SimulationVisualizationMapperTest { assertTrue(sites.get(0).containsKey("x")); assertTrue(sites.get(0).containsKey("y")); assertTrue(sites.get(0).containsKey("floor")); + + int neighborReferences = input.getGridMap().getSites().keySet().stream() + .mapToInt(siteId -> input.getGridMap().getNeighbors(siteId).size()) + .sum(); + assertEquals(neighborReferences / 2, edges.size()); + Set edgeKeys = new HashSet(); + for (Map edge : edges) { + String from = String.valueOf(edge.get("fromSite")); + String to = String.valueOf(edge.get("toSite")); + assertTrue(input.getGridMap().getSites().containsKey(from)); + assertTrue(input.getGridMap().getSites().containsKey(to)); + assertTrue(from.compareTo(to) < 0); + assertTrue(input.getGridMap().getNeighbors(from).contains(to)); + assertTrue(edgeKeys.add(from + "|" + to)); + } } @Test @@ -74,6 +92,15 @@ class SimulationVisualizationMapperTest { assertEquals(sampleTask.getPhysicalTarget(), tasks.get(0).get("physicalEndSite")); assertEquals(Boolean.TRUE, tasks.get(0).get("vehicleChanged")); assertEquals(Boolean.TRUE, tasks.get(0).get("pathChanged")); + Map comparison = map(tasks.get(0).get("pathComparison")); + assertEquals(0, comparison.get("sharedEdgeCount")); + assertEquals(sourceTask.getPlannedPath().size() - 1, + comparison.get("plannedOnlyEdgeCount")); + assertEquals(0, comparison.get("historicalOnlyEdgeCount")); + List> invalidHistorical = + listOfMaps(comparison.get("invalidHistoricalSegments")); + assertEquals(1, invalidHistorical.size()); + assertEquals("UNKNOWN_SITE", invalidHistorical.get(0).get("reason")); assertTrue(events.stream().anyMatch(event -> "ASSIGN".equals(event.get("eventType")))); assertTrue(events.stream().anyMatch(event -> "MOVE".equals(event.get("eventType")))); assertTrue(events.stream().anyMatch(event -> "PLC_RETRY".equals(event.get("eventType"))));