feat(javaTest): plan traffic-aware approach and service routes

This commit is contained in:
zgw
2026-08-06 17:29:00 +08:00
parent 5719fe4a5f
commit 87129e39b4
4 changed files with 211 additions and 23 deletions

View File

@@ -6,6 +6,12 @@ import com.ft546.wcs.demo.model.DispatchTask;
import com.ft546.wcs.demo.model.Site; import com.ft546.wcs.demo.model.Site;
import com.ft546.wcs.demo.model.VehicleSnapshot; import com.ft546.wcs.demo.model.VehicleSnapshot;
import com.ft546.wcs.demo.traffic.GridPathPlanner; import com.ft546.wcs.demo.traffic.GridPathPlanner;
import com.ft546.wcs.demo.traffic.RouteCandidatePlanner;
import com.ft546.wcs.demo.traffic.RouteCandidateSet;
import com.ft546.wcs.demo.traffic.RoutePlanDecision;
import com.ft546.wcs.demo.traffic.RoutePlanningPolicy;
import com.ft546.wcs.demo.traffic.RouteQualityEvaluator;
import com.ft546.wcs.demo.traffic.TrafficRouteSnapshot;
import com.ft546.wcs.shadow.application.ShadowTrafficCoordinator; import com.ft546.wcs.shadow.application.ShadowTrafficCoordinator;
import com.ft546.wcs.shadow.decision.DecisionType; import com.ft546.wcs.shadow.decision.DecisionType;
import com.ft546.wcs.shadow.decision.RollingDispatchPolicy; import com.ft546.wcs.shadow.decision.RollingDispatchPolicy;
@@ -77,7 +83,8 @@ public final class HistoricalDispatchSimulator {
SimulationTaskStatus.FAILED, failure.getReason(), null, SimulationTaskStatus.FAILED, failure.getReason(), null,
Collections.emptyList(), Collections.emptyList(), 0L, Collections.emptyList(), Collections.emptyList(), 0L,
null, null, 0, 0, 0, null, null, 0, 0, 0,
Collections.emptyMap(), Collections.emptyMap())); Collections.emptyMap(), Collections.emptyMap(),
Collections.emptyList()));
} }
Map<String, SimulatedVehicleRuntime> vehicles = createRuntimes(input); Map<String, SimulatedVehicleRuntime> vehicles = createRuntimes(input);
@@ -91,6 +98,8 @@ public final class HistoricalDispatchSimulator {
SimulationArchive archive = new SimulationArchive(); SimulationArchive archive = new SimulationArchive();
IdempotentWmsCompletionReceiver wms = new IdempotentWmsCompletionReceiver(); IdempotentWmsCompletionReceiver wms = new IdempotentWmsCompletionReceiver();
GridPathPlanner planner = new GridPathPlanner(); GridPathPlanner planner = new GridPathPlanner();
RouteCandidatePlanner routePlanner = new RouteCandidatePlanner();
RouteQualityEvaluator routeEvaluator = new RouteQualityEvaluator();
VehicleAssignmentService assignment = new VehicleAssignmentService(); VehicleAssignmentService assignment = new VehicleAssignmentService();
CorridorReservationManager reservations = new CorridorReservationManager(); CorridorReservationManager reservations = new CorridorReservationManager();
ClearanceMovePlanner clearancePlanner = new ClearanceMovePlanner(); ClearanceMovePlanner clearancePlanner = new ClearanceMovePlanner();
@@ -105,8 +114,8 @@ public final class HistoricalDispatchSimulator {
applyVehicleEvents(tick, now, faults, vehicles, active, applyVehicleEvents(tick, now, faults, vehicles, active,
clearanceMoves, queue, executions, reservations, events); clearanceMoves, queue, executions, reservations, events);
assignReadyTasks(tick, now, input, queue, vehicles, active, assignReadyTasks(tick, now, input, queue, vehicles, active,
clearanceMoves, executions, terminal, planner, assignment, plc, clearanceMoves, executions, terminal, routePlanner, routeEvaluator,
reservations, events); assignment, plc, ledger, reservations, events);
// 本 tick 已通过的边被共享给两类移动,禁止另一车辆同 tick 重复或反向穿越。 // 本 tick 已通过的边被共享给两类移动,禁止另一车辆同 tick 重复或反向穿越。
Set<String> traversedEdges = new LinkedHashSet<String>(); Set<String> traversedEdges = new LinkedHashSet<String>();
ClearanceProgress clearanceProgress = advanceClearanceMoves( ClearanceProgress clearanceProgress = advanceClearanceMoves(
@@ -269,9 +278,11 @@ public final class HistoricalDispatchSimulator {
* @param clearanceMoves 执行该处理所需的 clearance moves 输入。 * @param clearanceMoves 执行该处理所需的 clearance moves 输入。
* @param executions 执行该处理所需的 executions 输入。 * @param executions 执行该处理所需的 executions 输入。
* @param terminal 执行该处理所需的 terminal 输入。 * @param terminal 执行该处理所需的 terminal 输入。
* @param planner 执行该处理所需的 planner 输入。 * @param routePlanner 生成 110% 物理距离内的确定性无环候选。
* @param routeEvaluator 使用同一交通快照按固定业务元组选择候选。
* @param assignment 执行该处理所需的 assignment 输入。 * @param assignment 执行该处理所需的 assignment 输入。
* @param plc 执行该处理所需的 plc 输入。 * @param plc 执行该处理所需的 plc 输入。
* @param ledger 当前节点和边资源的权威台账。
* @param reservations 执行该处理所需的 reservations 输入。 * @param reservations 执行该处理所需的 reservations 输入。
* @param events 执行该处理所需的 events 输入。 * @param events 执行该处理所需的 events 输入。
* @return 本 tick 是否至少有一个任务被分配或因永久不可达关闭。 * @return 本 tick 是否至少有一个任务被分配或因永久不可达关闭。
@@ -283,9 +294,11 @@ public final class HistoricalDispatchSimulator {
Map<String, ClearanceMove> clearanceMoves, Map<String, ClearanceMove> clearanceMoves,
Map<String, Execution> executions, Map<String, Execution> executions,
Map<String, SimulationTaskResult> terminal, Map<String, SimulationTaskResult> terminal,
GridPathPlanner planner, RouteCandidatePlanner routePlanner,
RouteQualityEvaluator routeEvaluator,
VehicleAssignmentService assignment, VehicleAssignmentService assignment,
SimulatedPlcGateway plc, SimulatedPlcGateway plc,
ResourceLedger ledger,
CorridorReservationManager reservations, CorridorReservationManager reservations,
List<SimulationTickEvent> events) { List<SimulationTickEvent> events) {
boolean progress = false; boolean progress = false;
@@ -295,10 +308,11 @@ public final class HistoricalDispatchSimulator {
for (HistoricalSimulationTask task : ready) { for (HistoricalSimulationTask task : ready) {
if (terminal.containsKey(task.getSimulationTaskId())) continue; if (terminal.containsKey(task.getSimulationTaskId())) continue;
// 服务路径由完整地图重新计算。历史 path_info 不参与规划,空路径表示业务端点永久不可达。 // 先生成服务段物理候选。这里只使用永久地图,临时占用不能把任务误关为 NO_ROUTE。
List<String> servicePath = planner.shortestPath(input.getGridMap(), RouteCandidateSet serviceCandidates = routePlanner.plan(input.getGridMap(),
task.getPhysicalSource(), task.getPhysicalTarget(), Collections.emptySet()); task.getPhysicalSource(), task.getPhysicalTarget(),
if (servicePath.isEmpty()) { Collections.<String>emptySet(), RoutePlanningPolicy.defaultPolicy());
if (serviceCandidates.getCandidates().isEmpty()) {
queue.remove(task.getSimulationTaskId()); queue.remove(task.getSimulationTaskId());
terminal.put(task.getSimulationTaskId(), result(executions.get( terminal.put(task.getSimulationTaskId(), result(executions.get(
task.getSimulationTaskId()), SimulationTaskStatus.FAILED, task.getSimulationTaskId()), SimulationTaskStatus.FAILED,
@@ -308,22 +322,24 @@ public final class HistoricalDispatchSimulator {
// 为每台空闲且健康的车计算到任务源点的接近路径,无法接近源点的车不进入本轮选车。 // 为每台空闲且健康的车计算到任务源点的接近路径,无法接近源点的车不进入本轮选车。
List<VehicleSnapshot> candidates = new ArrayList<VehicleSnapshot>(); List<VehicleSnapshot> candidates = new ArrayList<VehicleSnapshot>();
Map<String, List<String>> approaches = new LinkedHashMap<String, List<String>>(); Map<String, RouteCandidateSet> approaches =
new LinkedHashMap<String, RouteCandidateSet>();
for (SimulatedVehicleRuntime vehicle : vehicles.values()) { for (SimulatedVehicleRuntime vehicle : vehicles.values()) {
if (!vehicle.isAssignmentEligible() || active.containsKey(vehicle.getVehicleId()) if (!vehicle.isAssignmentEligible() || active.containsKey(vehicle.getVehicleId())
|| clearanceMoves.containsKey(vehicle.getVehicleId())) { || clearanceMoves.containsKey(vehicle.getVehicleId())) {
continue; continue;
} }
List<String> approach = planner.shortestPath(input.getGridMap(), RouteCandidateSet approach = routePlanner.plan(input.getGridMap(),
vehicle.getCurrentSite(), task.getPhysicalSource(), Collections.emptySet()); vehicle.getCurrentSite(), task.getPhysicalSource(),
if (!approach.isEmpty()) { Collections.<String>emptySet(), RoutePlanningPolicy.defaultPolicy());
if (!approach.getCandidates().isEmpty()) {
candidates.add(vehicle.snapshot()); candidates.add(vehicle.snapshot());
approaches.put(vehicle.getVehicleId(), approach); approaches.put(vehicle.getVehicleId(), approach);
} }
} }
if (candidates.isEmpty()) { if (candidates.isEmpty()) {
// 暂无可分配车不等于失败;只有全车队都无法抵达源点时才关闭为 NO_ROUTE。 // 暂无可分配车不等于失败;只有全车队都无法抵达源点时才关闭为 NO_ROUTE。
if (!fleetCanReach(task, vehicles, input, planner)) { if (!fleetCanReach(task, vehicles, input, routePlanner)) {
queue.remove(task.getSimulationTaskId()); queue.remove(task.getSimulationTaskId());
terminal.put(task.getSimulationTaskId(), result(executions.get( terminal.put(task.getSimulationTaskId(), result(executions.get(
task.getSimulationTaskId()), SimulationTaskStatus.FAILED, task.getSimulationTaskId()), SimulationTaskStatus.FAILED,
@@ -343,8 +359,17 @@ public final class HistoricalDispatchSimulator {
if (!decision.getVehicle().isPresent()) continue; if (!decision.getVehicle().isPresent()) continue;
String vehicleId = decision.getVehicle().get().getVehicleId(); String vehicleId = decision.getVehicle().get().getVehicleId();
// 车辆先走到取货/起始点,再沿新规划的服务路径到达任务终点,形成实际执行路线。 // 所有候选共用这一刻的占点、边预约、走廊方向和活动路线,保证比较口径一致。
List<String> route = new ArrayList<String>(approaches.get(vehicleId)); TrafficRouteSnapshot traffic = trafficSnapshot(
vehicles, active, ledger, reservations);
RoutePlanDecision approachDecision = routeEvaluator.select(input.getGridMap(),
approaches.get(vehicleId), traffic, task.getSimulationTaskId(), vehicleId);
RoutePlanDecision serviceDecision = routeEvaluator.select(input.getGridMap(),
serviceCandidates, traffic, task.getSimulationTaskId(), vehicleId);
// 车辆先走到业务源点,再执行服务段;拼接时去掉重复源点。
List<String> route = new ArrayList<String>(approachDecision.getSelectedPath());
List<String> servicePath = serviceDecision.getSelectedPath();
for (int i = 1; i < servicePath.size(); i++) route.add(servicePath.get(i)); for (int i = 1; i < servicePath.size(); i++) route.add(servicePath.get(i));
Execution execution = executions.get(task.getSimulationTaskId()); Execution execution = executions.get(task.getSimulationTaskId());
@@ -365,6 +390,13 @@ public final class HistoricalDispatchSimulator {
execution.lastVehicleId = vehicleId; execution.lastVehicleId = vehicleId;
execution.plannedPath = new ArrayList<String>(servicePath); execution.plannedPath = new ArrayList<String>(servicePath);
execution.assignedVehicles.add(vehicleId); execution.assignedVehicles.add(vehicleId);
execution.pathVersion++;
execution.planningEvidence.add(new RoutePlanningEvidence(
RoutePlanningEvidence.PlanningStage.APPROACH,
execution.pathVersion, now, false, approachDecision));
execution.planningEvidence.add(new RoutePlanningEvidence(
RoutePlanningEvidence.PlanningStage.SERVICE,
execution.pathVersion, now, false, serviceDecision));
active.put(vehicleId, new ActiveTask(execution, route)); active.put(vehicleId, new ActiveTask(execution, route));
queue.remove(task.getSimulationTaskId()); queue.remove(task.getSimulationTaskId());
events.add(event(tick, now, "ASSIGN", task.getSimulationTaskId(), vehicleId, events.add(event(tick, now, "ASSIGN", task.getSimulationTaskId(), vehicleId,
@@ -387,14 +419,52 @@ public final class HistoricalDispatchSimulator {
private boolean fleetCanReach(HistoricalSimulationTask task, private boolean fleetCanReach(HistoricalSimulationTask task,
Map<String, SimulatedVehicleRuntime> vehicles, Map<String, SimulatedVehicleRuntime> vehicles,
HistoricalSimulationInput input, HistoricalSimulationInput input,
GridPathPlanner planner) { RouteCandidatePlanner planner) {
for (SimulatedVehicleRuntime vehicle : vehicles.values()) { for (SimulatedVehicleRuntime vehicle : vehicles.values()) {
if (!planner.shortestPath(input.getGridMap(), vehicle.getCurrentSite(), if (!planner.plan(input.getGridMap(), vehicle.getCurrentSite(),
task.getPhysicalSource(), Collections.emptySet()).isEmpty()) return true; task.getPhysicalSource(), Collections.<String>emptySet(),
RoutePlanningPolicy.defaultPolicy()).getCandidates().isEmpty()) return true;
} }
return false; return false;
} }
/**
* 冻结本次规划看到的车辆占点、已预约方向、权威边资源和其他活动任务剩余路线。
*/
private TrafficRouteSnapshot trafficSnapshot(
Map<String, SimulatedVehicleRuntime> vehicles,
Map<String, ActiveTask> active,
ResourceLedger ledger,
CorridorReservationManager reservations) {
TrafficRouteSnapshot.Builder snapshot = TrafficRouteSnapshot.builder();
for (SimulatedVehicleRuntime vehicle : vehicles.values()) {
snapshot.occupyNode(vehicle.getCurrentSite(), vehicle.getVehicleId());
}
for (Map.Entry<String, ActiveTask> entry : active.entrySet()) {
ActiveTask task = entry.getValue();
List<String> remaining = task.route.subList(task.index, task.route.size());
snapshot.activeRoute(task.execution.task.getSimulationTaskId(), remaining);
// 只有台账中确实由该活动车辆持有的边才属于当前瞬时独占资源。
for (int index = 1; index < remaining.size(); index++) {
String from = remaining.get(index - 1);
String to = remaining.get(index);
if (ledger.holdersFor(ResourceKey.edge(from, to)).contains(entry.getKey())) {
snapshot.reserveEdge(from, to,
task.execution.task.getSimulationTaskId());
}
}
}
for (CorridorReservationManager.CorridorReservationSnapshot reservation
: reservations.snapshot()) {
for (String taskId : reservation.getTaskIds()) {
snapshot.reserveDirection(reservation.getFromSite(),
reservation.getToSite(), taskId);
}
}
return snapshot.build();
}
/** /**
* 推进全部已分配任务的下一条边,并在移动前依次执行方向冲突、清障、交通决策、资源台账和 PLC 确认。 * 推进全部已分配任务的下一条边,并在移动前依次执行方向冲突、清障、交通决策、资源台账和 PLC 确认。
* *
@@ -920,7 +990,8 @@ public final class HistoricalDispatchSimulator {
status == SimulationTaskStatus.COMPLETED ? now : null, status == SimulationTaskStatus.COMPLETED ? now : null,
execution.holdCount, execution.holdCount,
plc.attemptsFor(execution.task.getSimulationTaskId()), plc.attemptsFor(execution.task.getSimulationTaskId()),
execution.retryCount, execution.holdReasons, execution.retryReasons); execution.retryCount, execution.holdReasons, execution.retryReasons,
execution.planningEvidence);
} }
/** /**
@@ -1004,6 +1075,9 @@ public final class HistoricalDispatchSimulator {
new LinkedHashMap<String, Integer>(); new LinkedHashMap<String, Integer>();
private final Map<String, Integer> retryReasons = private final Map<String, Integer> retryReasons =
new LinkedHashMap<String, Integer>(); new LinkedHashMap<String, Integer>();
private final List<RoutePlanningEvidence> planningEvidence =
new ArrayList<RoutePlanningEvidence>();
private int pathVersion;
/** /**
* 创建 {@code Execution} 实例并初始化其内部状态。 * 创建 {@code Execution} 实例并初始化其内部状态。

View File

@@ -0,0 +1,51 @@
package com.ft546.wcs.shadow.history.simulation;
import com.ft546.wcs.demo.traffic.RoutePlanDecision;
/** 保存一次接近段或服务段路线选择的不可变业务证据。 */
public final class RoutePlanningEvidence {
public enum PlanningStage { APPROACH, SERVICE }
private final PlanningStage stage;
private final int pathVersion;
private final long simulationMillis;
private final boolean reroute;
private final RoutePlanDecision decision;
/**
* 记录一次已实际采用的中心规划决定。
*
* @param stage 车辆到源点的接近段或源点到目标的服务段
* @param pathVersion 任务执行路线版本,从 1 开始递增
* @param simulationMillis 作出决定的仿真时间
* @param reroute 是否由连续交通 HOLD 触发
* @param decision 候选集合、质量元组和最终选择
*/
public RoutePlanningEvidence(PlanningStage stage, int pathVersion,
long simulationMillis, boolean reroute,
RoutePlanDecision decision) {
if (stage == null || pathVersion <= 0 || simulationMillis < 0L || decision == null) {
throw new IllegalArgumentException("valid route planning evidence is required");
}
this.stage = stage;
this.pathVersion = pathVersion;
this.simulationMillis = simulationMillis;
this.reroute = reroute;
this.decision = decision;
}
/** 返回本次决定对应接近段还是服务段。 */
public PlanningStage getStage() { return stage; }
/** 返回采用该决定后的完整执行路线版本。 */
public int getPathVersion() { return pathVersion; }
/** 返回本次决定发生的仿真时间。 */
public long getSimulationMillis() { return simulationMillis; }
/** 返回该决定是否来自运行中的交通重规划。 */
public boolean isReroute() { return reroute; }
/** 返回候选、质量和选择原因。 */
public RoutePlanDecision getDecision() { return decision; }
}

View File

@@ -24,6 +24,7 @@ public final class SimulationTaskResult {
private final int retryCount; private final int retryCount;
private final Map<String, Integer> holdReasons; private final Map<String, Integer> holdReasons;
private final Map<String, Integer> retryReasons; private final Map<String, Integer> retryReasons;
private final List<RoutePlanningEvidence> planningEvidence;
/** /**
* 创建 {@code SimulationTaskResult} 实例并初始化其内部状态。 * 创建 {@code SimulationTaskResult} 实例并初始化其内部状态。
@@ -44,6 +45,7 @@ public final class SimulationTaskResult {
* @param retryCount 执行该处理所需的 retry count 输入。 * @param retryCount 执行该处理所需的 retry count 输入。
* @param holdReasons 执行该处理所需的 hold reasons 输入。 * @param holdReasons 执行该处理所需的 hold reasons 输入。
* @param retryReasons 执行该处理所需的 retry reasons 输入。 * @param retryReasons 执行该处理所需的 retry reasons 输入。
* @param planningEvidence 每个实际采用路径版本的接近段和服务段决策证据。
*/ */
SimulationTaskResult(String simulationTaskId, HistoricalQueueRecord record, SimulationTaskResult(String simulationTaskId, HistoricalQueueRecord record,
SimulationTaskStatus status, String reason, SimulationTaskStatus status, String reason,
@@ -52,7 +54,8 @@ public final class SimulationTaskResult {
Long firstAssignedMillis, Long completedMillis, Long firstAssignedMillis, Long completedMillis,
int holdCount, int protocolAttempts, int retryCount, int holdCount, int protocolAttempts, int retryCount,
Map<String, Integer> holdReasons, Map<String, Integer> holdReasons,
Map<String, Integer> retryReasons) { Map<String, Integer> retryReasons,
List<RoutePlanningEvidence> planningEvidence) {
this.simulationTaskId = simulationTaskId; this.simulationTaskId = simulationTaskId;
this.record = record; this.record = record;
this.status = status; this.status = status;
@@ -68,6 +71,7 @@ public final class SimulationTaskResult {
this.retryCount = retryCount; this.retryCount = retryCount;
this.holdReasons = immutableMap(holdReasons); this.holdReasons = immutableMap(holdReasons);
this.retryReasons = immutableMap(retryReasons); this.retryReasons = immutableMap(retryReasons);
this.planningEvidence = immutable(planningEvidence);
} }
/** /**
@@ -175,6 +179,9 @@ public final class SimulationTaskResult {
* @return 本次处理产生的结果;调用方据此继续后续流程。 * @return 本次处理产生的结果;调用方据此继续后续流程。
*/ */
public Map<String, Integer> getRetryReasons() { return retryReasons; } public Map<String, Integer> getRetryReasons() { return retryReasons; }
/** 返回任务每个路径版本的接近段和服务段中心规划证据。 */
public List<RoutePlanningEvidence> getPlanningEvidence() { return planningEvidence; }
/** /**
* 返回当前对象保存的“waiting millis”信息。 * 返回当前对象保存的“waiting millis”信息。
* *

View File

@@ -78,11 +78,34 @@ class HistoricalDispatchSimulatorTest {
alternateRouteInput(), SimulationConfig.defaults(), alternateRouteInput(), SimulationConfig.defaults(),
DeterministicFaultPlan.none()); DeterministicFaultPlan.none());
assertEquals(Arrays.asList("A", "C"), assertEquals(Arrays.asList("A", "B", "C"),
result.getTaskResults().get(0).getPlannedPath()); result.getTaskResults().get(0).getPlannedPath());
assertEquals(1, result.getReplannedCount()); assertEquals(1, result.getReplannedCount());
} }
@Test
void selectsBoundedConflictFreeServiceRouteAndStoresBothPlanningStages() {
HistoricalDispatchSimulationResult result = new HistoricalDispatchSimulator().simulate(
conflictAwareRoutingInput(), new SimulationConfig(100, 30_000L, 3, 100, 300),
DeterministicFaultPlan.none());
SimulationTaskResult second = result.getTaskResults().stream()
.filter(task -> "SAFE-2".equals(task.getSimulationTaskId()))
.findFirst().orElseThrow(AssertionError::new);
assertEquals(Arrays.asList("D", "C", "A"), second.getPlannedPath());
assertEquals(2, second.getPlanningEvidence().size());
RoutePlanningEvidence approach = second.getPlanningEvidence().get(0);
RoutePlanningEvidence service = second.getPlanningEvidence().get(1);
assertEquals(RoutePlanningEvidence.PlanningStage.APPROACH, approach.getStage());
assertEquals(RoutePlanningEvidence.PlanningStage.SERVICE, service.getStage());
assertEquals(1, service.getPathVersion());
assertEquals("FEWER_OPPOSITE_DIRECTION_CONFLICTS",
service.getDecision().getDecisionReason());
assertTrue(service.getDecision().getDetourRatio() <= 1.10d);
assertEquals(2, service.getDecision().getCandidateSet().getCandidates().size());
assertEquals(0, result.getFailedCount());
}
/** /**
* 验证“moves idle blockers node by node and completes both business directions”场景,确保相关业务规则和断言成立。 * 验证“moves idle blockers node by node and completes both business directions”场景,确保相关业务规则和断言成立。
* *
@@ -211,13 +234,46 @@ class HistoricalDispatchSimulatorTest {
"test", "test", nodes, edges, Collections.emptyMap(), "test", "test", nodes, edges, Collections.emptyMap(),
Collections.emptySet(), Collections.emptySet())); Collections.emptySet(), Collections.emptySet()));
HistoricalSimulationTask task = customTask( HistoricalSimulationTask task = customTask(
"R1", "A", "C", 1, 0, Arrays.asList("A", "B", "C")); "R1", "A", "C", 1, 0, Arrays.asList("A", "C"));
return new HistoricalSimulationInput(Collections.singletonList(task), return new HistoricalSimulationInput(Collections.singletonList(task),
Collections.emptyList(), grid, traffic, Collections.emptyList(), grid, traffic,
Collections.singletonList(new VehicleSnapshot( Collections.singletonList(new VehicleSnapshot(
"1", 1, "A", 5, 1, 100, true, true, false))); "1", 1, "A", 5, 1, 100, true, true, false)));
} }
private HistoricalSimulationInput conflictAwareRoutingInput() {
GridMap grid = new GridMap();
grid.addSite(new Site("A", 1, 0, 0, "TEST"));
grid.addSite(new Site("B", 1, 5, 0, "TEST"));
grid.addSite(new Site("C", 1, 5, 1, "TEST"));
grid.addSite(new Site("D", 1, 10, 0, "TEST"));
grid.connect("A", "B");
grid.connect("B", "D");
grid.connect("A", "C");
grid.connect("C", "D");
LinkedHashSet<String> nodes = new LinkedHashSet<String>(
Arrays.asList("A", "B", "C", "D"));
LinkedHashSet<TrafficMapCompiler.EdgeDefinition> edges =
new LinkedHashSet<TrafficMapCompiler.EdgeDefinition>();
edges.add(new TrafficMapCompiler.EdgeDefinition("A", "B"));
edges.add(new TrafficMapCompiler.EdgeDefinition("B", "D"));
edges.add(new TrafficMapCompiler.EdgeDefinition("A", "C"));
edges.add(new TrafficMapCompiler.EdgeDefinition("C", "D"));
EffectiveTrafficMap traffic = new TrafficMapCompiler().compile(
new TrafficMapCompiler.MapDefinition("CONFLICT-AWARE", 1L, "TEST", true,
"test", "test", nodes, edges, Collections.emptyMap(),
Collections.emptySet(), Collections.emptySet()));
List<HistoricalSimulationTask> tasks = Arrays.asList(
customTask("SAFE-1", "A", "D", 1, 0, Arrays.asList("A", "B", "D")),
customTask("SAFE-2", "D", "A", 2, 0, Arrays.asList("D", "B", "A")));
List<VehicleSnapshot> vehicles = Arrays.asList(
new VehicleSnapshot("1", 1, "A", 5, 1, 100, true, true, false),
new VehicleSnapshot("2", 1, "D", 5, 1, 100, true, true, false));
return new HistoricalSimulationInput(tasks, Collections.emptyList(), grid,
traffic, vehicles);
}
/** /**
* 验证“blocked three vehicle corridor input”场景,确保相关业务规则和断言成立。 * 验证“blocked three vehicle corridor input”场景,确保相关业务规则和断言成立。
* *