feat(javaTest): map simulation evidence for visualization
This commit is contained in:
@@ -0,0 +1,371 @@
|
||||
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.shadow.history.HistoricalQueueDataset;
|
||||
import com.ft546.wcs.shadow.history.HistoricalQueueRecord;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationResult;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalSimulationInput;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalSimulationTask;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationInvariantSummary;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationMetrics;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationTaskResult;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationTaskStatus;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationTickEvent;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.time.format.DateTimeParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* 将调度仿真证据转换为前端可序列化的数据结构。
|
||||
*
|
||||
* <p>该类不调用规划器,也不修改仿真结果。页面看到的车辆、路径和事件均来自同一次 Java 仿真。
|
||||
*/
|
||||
public final class SimulationVisualizationMapper {
|
||||
private static final String MODE = "OFFLINE_IN_MEMORY_SIMULATION";
|
||||
private static final String BOUNDARY =
|
||||
"纯内存离线仿真,不连接生产 SQL Server、PLC 或 WMS,也不替代现场 HIL/ACTIVE 验收";
|
||||
private static final String PLANNER_VERSION = "GridPathPlanner-BFS/full-grid-v1";
|
||||
private static final DateTimeFormatter TIME =
|
||||
DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
|
||||
|
||||
private final HistoricalQueueDataset dataset;
|
||||
|
||||
public SimulationVisualizationMapper(HistoricalQueueDataset dataset) {
|
||||
if (dataset == null) throw new IllegalArgumentException("dataset is required");
|
||||
this.dataset = dataset;
|
||||
}
|
||||
|
||||
/** 返回页面启动所需的地图、站点和初始车辆元数据。 */
|
||||
public Map<String, Object> bootstrap(HistoricalSimulationInput input) {
|
||||
requireInput(input);
|
||||
Map<String, Object> payload = new LinkedHashMap<String, Object>();
|
||||
payload.put("mode", MODE);
|
||||
payload.put("boundary", BOUNDARY);
|
||||
payload.put("plannerVersion", PLANNER_VERSION);
|
||||
payload.put("mapSha256", dataset.getFullMapSha256());
|
||||
payload.put("fullMapRows", dataset.getFullMapRows());
|
||||
payload.put("passableMapRows", input.getGridMap().getSites().size());
|
||||
payload.put("database", dataset.getDatabase());
|
||||
payload.put("sourceTable", dataset.getSourceTable());
|
||||
payload.put("pathInfoUsedForPlanning", false);
|
||||
|
||||
List<Site> orderedSites = new ArrayList<Site>(input.getGridMap().getSites().values());
|
||||
Collections.sort(orderedSites, Comparator.comparing(Site::getId));
|
||||
List<Map<String, Object>> sites = new ArrayList<Map<String, Object>>();
|
||||
Set<Integer> floors = new TreeSet<Integer>();
|
||||
for (Site site : orderedSites) {
|
||||
sites.add(site(site));
|
||||
floors.add(site.getFloor());
|
||||
}
|
||||
payload.put("floors", new ArrayList<Integer>(floors));
|
||||
payload.put("sites", sites);
|
||||
|
||||
List<VehicleSnapshot> orderedVehicles =
|
||||
new ArrayList<VehicleSnapshot>(input.getInitialVehicles());
|
||||
Collections.sort(orderedVehicles, Comparator.comparing(VehicleSnapshot::getVehicleId));
|
||||
List<Map<String, Object>> vehicles = new ArrayList<Map<String, Object>>();
|
||||
for (VehicleSnapshot vehicle : orderedVehicles) vehicles.add(vehicle(vehicle));
|
||||
payload.put("vehicles", vehicles);
|
||||
return payload;
|
||||
}
|
||||
|
||||
/** 返回一次仿真的任务、事件、指标、归档和安全证据。 */
|
||||
public Map<String, Object> result(HistoricalSimulationInput input,
|
||||
HistoricalDispatchSimulationResult result) {
|
||||
requireInput(input);
|
||||
if (result == null) throw new IllegalArgumentException("result is required");
|
||||
|
||||
Map<String, HistoricalSimulationTask> definitions = taskDefinitions(input);
|
||||
List<SimulationTaskResult> orderedTasks =
|
||||
new ArrayList<SimulationTaskResult>(result.getTaskResults());
|
||||
Collections.sort(orderedTasks,
|
||||
Comparator.comparing(SimulationTaskResult::getSimulationTaskId));
|
||||
|
||||
List<Map<String, Object>> tasks = new ArrayList<Map<String, Object>>();
|
||||
for (SimulationTaskResult task : orderedTasks) {
|
||||
tasks.add(task(task, definitions.get(task.getSimulationTaskId()),
|
||||
result.getAcceleration()));
|
||||
}
|
||||
List<Map<String, Object>> events = new ArrayList<Map<String, Object>>();
|
||||
for (SimulationTickEvent event : result.getEvents()) events.add(event(event));
|
||||
|
||||
Map<String, Object> payload = new LinkedHashMap<String, Object>();
|
||||
payload.put("mode", MODE);
|
||||
payload.put("boundary", BOUNDARY);
|
||||
payload.put("plannerVersion", PLANNER_VERSION);
|
||||
payload.put("mapSha256", dataset.getFullMapSha256());
|
||||
payload.put("pathInfoUsedForPlanning", false);
|
||||
payload.put("summary", summary(result));
|
||||
payload.put("tasks", tasks);
|
||||
payload.put("events", events);
|
||||
payload.put("invariants", invariants(result.getInvariants()));
|
||||
payload.put("archive", archive(result));
|
||||
payload.put("wms", wms(result));
|
||||
return payload;
|
||||
}
|
||||
|
||||
private Map<String, Object> summary(HistoricalDispatchSimulationResult result) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("inputCount", result.getTaskResults().size());
|
||||
value.put("acceleration", result.getAcceleration());
|
||||
value.put("completedCount", result.getCompletedCount());
|
||||
value.put("waitingCount", result.getWaitingCount());
|
||||
value.put("failedCount", result.getFailedCount());
|
||||
value.put("completedByVehicle", result.completedByVehicle());
|
||||
value.put("reassignmentCount", result.getReassignmentCount());
|
||||
value.put("replannedPathDifferenceCount", result.getReplannedCount());
|
||||
value.put("retryCount", result.getRetryCount());
|
||||
value.put("tickCount", result.getTickCount());
|
||||
value.put("waitingTime", timeStats(waitingSamples(result)));
|
||||
value.put("completionTime", timeStats(completionSamples(result)));
|
||||
value.put("historicalDuration", timeStats(historicalSamples(result)));
|
||||
value.put("simulationMinusHistoricalDuration",
|
||||
timeStats(durationDifferenceSamples(result)));
|
||||
value.put("holdReasons", aggregateReasons(result, true));
|
||||
value.put("retryReasons", aggregateReasons(result, false));
|
||||
value.put("failureReasons", failureReasons(result));
|
||||
boolean safe = result.getInvariants().totalSafetyViolations() == 0
|
||||
&& result.getInvariants().getDeadlocks() == 0;
|
||||
value.put("acceptancePassed", safe && result.getWaitingCount() == 0
|
||||
&& result.getFailedCount() == 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> task(SimulationTaskResult task,
|
||||
HistoricalSimulationTask definition,
|
||||
int acceleration) {
|
||||
HistoricalQueueRecord record = task.getRecord();
|
||||
List<String> historicalPath = historicalPath(record.getPathInfo());
|
||||
Long historicalDuration = historicalDuration(task);
|
||||
Long simulatedDuration = task.getCompletionMillis();
|
||||
Long scaledDuration = simulatedDuration == null
|
||||
? null : simulatedDuration * acceleration;
|
||||
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("simulationTaskId", task.getSimulationTaskId());
|
||||
value.put("taskNo", record.getTaskNo());
|
||||
value.put("guid", record.getGuid());
|
||||
value.put("taskType", definition == null
|
||||
? record.getTaskType() : definition.getTaskType().name());
|
||||
value.put("priority", record.getPriority());
|
||||
value.put("businessStartSite", record.getStartSite());
|
||||
value.put("businessEndSite", record.getEndSite());
|
||||
value.put("physicalStartSite", definition == null
|
||||
? record.getStartSite() : definition.getPhysicalSource());
|
||||
value.put("physicalEndSite", definition == null
|
||||
? record.getEndSite() : definition.getPhysicalTarget());
|
||||
value.put("historicalVehicleId", record.getAgvId());
|
||||
value.put("simulatedVehicleId", task.getSimulatedVehicleId());
|
||||
value.put("historicalPath", historicalPath);
|
||||
value.put("plannedPath", task.getPlannedPath());
|
||||
value.put("assignedVehicles", task.getAssignedVehicles());
|
||||
value.put("status", task.getStatus().name());
|
||||
value.put("reason", task.getReason());
|
||||
value.put("arrivalMillis", task.getArrivalMillis());
|
||||
value.put("firstAssignedMillis", task.getFirstAssignedMillis());
|
||||
value.put("completedMillis", task.getCompletedMillis());
|
||||
value.put("waitingMillis", task.getWaitingMillis());
|
||||
value.put("simulationDurationMillis", simulatedDuration);
|
||||
value.put("historicalDurationMillis", historicalDuration);
|
||||
value.put("scaledSourceDurationMillis", scaledDuration);
|
||||
value.put("durationDifferenceMillis", historicalDuration == null || scaledDuration == null
|
||||
? null : scaledDuration - historicalDuration);
|
||||
value.put("holdCount", task.getHoldCount());
|
||||
value.put("protocolAttempts", task.getProtocolAttempts());
|
||||
value.put("retryCount", task.getRetryCount());
|
||||
value.put("holdReasons", task.getHoldReasons());
|
||||
value.put("retryReasons", task.getRetryReasons());
|
||||
value.put("vehicleChanged", task.getSimulatedVehicleId() != null
|
||||
&& !task.getSimulatedVehicleId().equals(record.getAgvId()));
|
||||
value.put("pathChanged", !historicalPath.equals(task.getPlannedPath()));
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> event(SimulationTickEvent event) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("tick", event.getTick());
|
||||
value.put("simulationMillis", event.getSimulationMillis());
|
||||
value.put("eventType", event.getType());
|
||||
value.put("taskId", event.getTaskId());
|
||||
value.put("vehicleId", event.getVehicleId());
|
||||
value.put("reason", event.getReason());
|
||||
value.put("fromSite", event.getFromSite());
|
||||
value.put("toSite", event.getToSite());
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> site(Site site) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("siteId", site.getId());
|
||||
value.put("floor", site.getFloor());
|
||||
value.put("x", site.getX());
|
||||
value.put("y", site.getY());
|
||||
value.put("channel", site.getChannel());
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> vehicle(VehicleSnapshot vehicle) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("vehicleId", vehicle.getVehicleId());
|
||||
value.put("floor", vehicle.getFloor());
|
||||
value.put("currentSite", vehicle.getCurrentSite());
|
||||
value.put("workMode", vehicle.getWorkMode());
|
||||
value.put("status", vehicle.getStatus());
|
||||
value.put("battery", vehicle.getBattery());
|
||||
value.put("connected", vehicle.isConnected());
|
||||
value.put("allowSend", vehicle.isAllowSend());
|
||||
value.put("cargo", vehicle.isCargo());
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> invariants(SimulationInvariantSummary source) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("capacityViolations", source.getCapacityViolations());
|
||||
value.put("nodeDoubleOccupancyViolations", source.getNodeDoubleOccupancyViolations());
|
||||
value.put("oppositeEdgeViolations", source.getOppositeEdgeViolations());
|
||||
value.put("duplicateArchiveAttempts", source.getDuplicateArchiveAttempts());
|
||||
value.put("duplicateWmsBusinessEffects", source.getDuplicateWmsBusinessEffects());
|
||||
value.put("deadlocks", source.getDeadlocks());
|
||||
value.put("totalSafetyViolations", source.totalSafetyViolations());
|
||||
value.put("passed", source.totalSafetyViolations() == 0 && source.getDeadlocks() == 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> archive(HistoricalDispatchSimulationResult result) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("count", result.getArchiveCount());
|
||||
value.put("expected", result.getCompletedCount());
|
||||
value.put("complete", result.getArchiveCount() == result.getCompletedCount()
|
||||
&& result.getInvariants().getDuplicateArchiveAttempts() == 0);
|
||||
value.put("duplicateAttempts", result.getInvariants().getDuplicateArchiveAttempts());
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> wms(HistoricalDispatchSimulationResult result) {
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("deliveryAttempts", result.getWmsDeliveryAttempts());
|
||||
value.put("businessEffects", result.getWmsBusinessEffects());
|
||||
value.put("duplicateDeliveryAttempts", Math.max(0,
|
||||
result.getWmsDeliveryAttempts() - result.getCompletedCount()));
|
||||
value.put("duplicateBusinessEffects",
|
||||
result.getInvariants().getDuplicateWmsBusinessEffects());
|
||||
value.put("idempotent", result.getWmsBusinessEffects() == result.getCompletedCount()
|
||||
&& result.getInvariants().getDuplicateWmsBusinessEffects() == 0);
|
||||
return value;
|
||||
}
|
||||
|
||||
private Map<String, Object> timeStats(List<Long> samples) {
|
||||
SimulationMetrics metrics = new SimulationMetrics();
|
||||
Map<String, Object> value = new LinkedHashMap<String, Object>();
|
||||
value.put("sampleCount", samples.size());
|
||||
value.put("averageMillis", metrics.average(samples));
|
||||
value.put("p95Millis", metrics.percentile(samples, 0.95));
|
||||
value.put("p99Millis", metrics.percentile(samples, 0.99));
|
||||
return value;
|
||||
}
|
||||
|
||||
private List<Long> waitingSamples(HistoricalDispatchSimulationResult result) {
|
||||
List<Long> values = new ArrayList<Long>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
if (task.getWaitingMillis() != null) values.add(task.getWaitingMillis());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private List<Long> completionSamples(HistoricalDispatchSimulationResult result) {
|
||||
List<Long> values = new ArrayList<Long>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
if (task.getCompletionMillis() != null) values.add(task.getCompletionMillis());
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private List<Long> historicalSamples(HistoricalDispatchSimulationResult result) {
|
||||
List<Long> values = new ArrayList<Long>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
Long duration = historicalDuration(task);
|
||||
if (duration != null) values.add(duration);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private List<Long> durationDifferenceSamples(HistoricalDispatchSimulationResult result) {
|
||||
List<Long> values = new ArrayList<Long>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
Long historical = historicalDuration(task);
|
||||
Long simulated = task.getCompletionMillis();
|
||||
if (historical != null && simulated != null) {
|
||||
values.add(simulated * result.getAcceleration() - historical);
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private Map<String, Integer> aggregateReasons(HistoricalDispatchSimulationResult result,
|
||||
boolean holds) {
|
||||
Map<String, Integer> values = new LinkedHashMap<String, Integer>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
Map<String, Integer> source = holds ? task.getHoldReasons() : task.getRetryReasons();
|
||||
for (Map.Entry<String, Integer> entry : source.entrySet()) {
|
||||
Integer previous = values.get(entry.getKey());
|
||||
values.put(entry.getKey(), (previous == null ? 0 : previous) + entry.getValue());
|
||||
}
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private Map<String, Integer> failureReasons(HistoricalDispatchSimulationResult result) {
|
||||
Map<String, Integer> values = new LinkedHashMap<String, Integer>();
|
||||
for (SimulationTaskResult task : result.getTaskResults()) {
|
||||
if (task.getStatus() != SimulationTaskStatus.FAILED) continue;
|
||||
Integer previous = values.get(task.getReason());
|
||||
values.put(task.getReason(), previous == null ? 1 : previous + 1);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private Long historicalDuration(SimulationTaskResult task) {
|
||||
try {
|
||||
LocalDateTime created = LocalDateTime.parse(task.getRecord().getCreateDate(), TIME);
|
||||
LocalDateTime finished = LocalDateTime.parse(task.getRecord().getFinishDate(), TIME);
|
||||
return Duration.between(created, finished).toMillis();
|
||||
} catch (DateTimeParseException | NullPointerException exception) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> historicalPath(String pathInfo) {
|
||||
if (pathInfo == null || pathInfo.trim().isEmpty()) return Collections.emptyList();
|
||||
List<String> values = new ArrayList<String>();
|
||||
for (String node : Arrays.asList(pathInfo.split(","))) {
|
||||
String normalized = node.trim();
|
||||
if (!normalized.isEmpty()) values.add(normalized);
|
||||
}
|
||||
return Collections.unmodifiableList(values);
|
||||
}
|
||||
|
||||
private Map<String, HistoricalSimulationTask> taskDefinitions(HistoricalSimulationInput input) {
|
||||
Map<String, HistoricalSimulationTask> values =
|
||||
new LinkedHashMap<String, HistoricalSimulationTask>();
|
||||
for (HistoricalSimulationTask task : input.getTasks()) {
|
||||
values.put(task.getSimulationTaskId(), task);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
private void requireInput(HistoricalSimulationInput input) {
|
||||
if (input == null) throw new IllegalArgumentException("input is required");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.ft546.wcs.shadow.history.visualizer;
|
||||
|
||||
import com.ft546.wcs.shadow.history.HistoricalQueueDataset;
|
||||
import com.ft546.wcs.shadow.history.HistoricalQueueLoader;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalDispatchSimulationResult;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalSimulationInput;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalSimulationInputFactory;
|
||||
import com.ft546.wcs.shadow.history.simulation.HistoricalSimulationTask;
|
||||
import com.ft546.wcs.shadow.history.simulation.SimulationTaskResult;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class SimulationVisualizationMapperTest {
|
||||
private static HistoricalQueueDataset dataset;
|
||||
private static HistoricalSimulationInput input;
|
||||
private static HistoricalDispatchSimulationResult result;
|
||||
private static HistoricalSimulationTask sampleTask;
|
||||
|
||||
@BeforeAll
|
||||
static void simulateOneTask() {
|
||||
dataset = new HistoricalQueueLoader().load(
|
||||
SimulationVisualizationMapperTest.class.getClassLoader(), "shadow/history");
|
||||
input = new HistoricalSimulationInputFactory().create(dataset);
|
||||
sampleTask = input.getTasks().stream()
|
||||
.filter(task -> !task.getPhysicalSource().equals(task.getPhysicalTarget()))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new AssertionError("a point-to-point sample is required"));
|
||||
PointToPointSimulationRequest request = PointToPointSimulationRequest.valid(
|
||||
sampleTask.getPhysicalSource(), sampleTask.getPhysicalTarget(),
|
||||
"RELOCATION", 2, 100, "HISTORY-CAR",
|
||||
Arrays.asList("WRONG", "REFERENCE"), "PLC_FIRST_SEND_FAILURE");
|
||||
result = new PointToPointSimulationService(input).run(request);
|
||||
}
|
||||
|
||||
@Test
|
||||
void bootstrapReturnsSortedSearchableSitesAndInitialVehicles() {
|
||||
Map<String, Object> payload = mapper().bootstrap(input);
|
||||
List<Map<String, Object>> sites = listOfMaps(payload.get("sites"));
|
||||
List<Map<String, Object>> vehicles = listOfMaps(payload.get("vehicles"));
|
||||
|
||||
assertEquals("OFFLINE_IN_MEMORY_SIMULATION", payload.get("mode"));
|
||||
assertEquals(dataset.getFullMapSha256(), payload.get("mapSha256"));
|
||||
assertEquals(input.getGridMap().getSites().size(), sites.size());
|
||||
assertEquals(input.getInitialVehicles().size(), vehicles.size());
|
||||
assertTrue(String.valueOf(sites.get(0).get("siteId"))
|
||||
.compareTo(String.valueOf(sites.get(sites.size() - 1).get("siteId"))) < 0);
|
||||
assertTrue(sites.get(0).containsKey("x"));
|
||||
assertTrue(sites.get(0).containsKey("y"));
|
||||
assertTrue(sites.get(0).containsKey("floor"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void mapsPlannerOutputEventsAndSafetyEvidenceWithoutReplanning() {
|
||||
Map<String, Object> payload = mapper().result(input, result);
|
||||
List<Map<String, Object>> tasks = listOfMaps(payload.get("tasks"));
|
||||
List<Map<String, Object>> events = listOfMaps(payload.get("events"));
|
||||
Map<String, Object> summary = map(payload.get("summary"));
|
||||
Map<String, Object> invariants = map(payload.get("invariants"));
|
||||
SimulationTaskResult sourceTask = result.getTaskResults().get(0);
|
||||
|
||||
assertEquals(1, tasks.size());
|
||||
assertEquals(sourceTask.getPlannedPath(), tasks.get(0).get("plannedPath"));
|
||||
assertEquals(Arrays.asList("WRONG", "REFERENCE"), tasks.get(0).get("historicalPath"));
|
||||
assertEquals(sampleTask.getPhysicalSource(), tasks.get(0).get("physicalStartSite"));
|
||||
assertEquals(sampleTask.getPhysicalTarget(), tasks.get(0).get("physicalEndSite"));
|
||||
assertEquals(Boolean.TRUE, tasks.get(0).get("vehicleChanged"));
|
||||
assertEquals(Boolean.TRUE, tasks.get(0).get("pathChanged"));
|
||||
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"))));
|
||||
assertEquals(1, summary.get("completedCount"));
|
||||
assertEquals(0, summary.get("waitingCount"));
|
||||
assertEquals(0, summary.get("failedCount"));
|
||||
assertEquals(0, invariants.get("totalSafetyViolations"));
|
||||
assertEquals(Boolean.TRUE, map(payload.get("archive")).get("complete"));
|
||||
assertEquals(Boolean.TRUE, map(payload.get("wms")).get("idempotent"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void timingStatisticsUseOnlyCompletedSamples() {
|
||||
Map<String, Object> summary = map(mapper().result(input, result).get("summary"));
|
||||
Map<String, Object> waiting = map(summary.get("waitingTime"));
|
||||
Map<String, Object> completion = map(summary.get("completionTime"));
|
||||
|
||||
assertEquals(1, waiting.get("sampleCount"));
|
||||
assertEquals(1, completion.get("sampleCount"));
|
||||
assertFalse(Collections.singletonList(waiting.get("averageMillis")).contains(null));
|
||||
assertFalse(Collections.singletonList(completion.get("p95Millis")).contains(null));
|
||||
}
|
||||
|
||||
private SimulationVisualizationMapper mapper() {
|
||||
return new SimulationVisualizationMapper(dataset);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Map<String, Object>> listOfMaps(Object value) {
|
||||
return (List<Map<String, Object>>) value;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> map(Object value) {
|
||||
return (Map<String, Object>) value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user