feat(javaTest): plan traffic-aware approach and service routes
This commit is contained in:
@@ -6,6 +6,12 @@ import com.ft546.wcs.demo.model.DispatchTask;
|
||||
import com.ft546.wcs.demo.model.Site;
|
||||
import com.ft546.wcs.demo.model.VehicleSnapshot;
|
||||
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.decision.DecisionType;
|
||||
import com.ft546.wcs.shadow.decision.RollingDispatchPolicy;
|
||||
@@ -77,7 +83,8 @@ public final class HistoricalDispatchSimulator {
|
||||
SimulationTaskStatus.FAILED, failure.getReason(), null,
|
||||
Collections.emptyList(), Collections.emptyList(), 0L,
|
||||
null, null, 0, 0, 0,
|
||||
Collections.emptyMap(), Collections.emptyMap()));
|
||||
Collections.emptyMap(), Collections.emptyMap(),
|
||||
Collections.emptyList()));
|
||||
}
|
||||
|
||||
Map<String, SimulatedVehicleRuntime> vehicles = createRuntimes(input);
|
||||
@@ -91,6 +98,8 @@ public final class HistoricalDispatchSimulator {
|
||||
SimulationArchive archive = new SimulationArchive();
|
||||
IdempotentWmsCompletionReceiver wms = new IdempotentWmsCompletionReceiver();
|
||||
GridPathPlanner planner = new GridPathPlanner();
|
||||
RouteCandidatePlanner routePlanner = new RouteCandidatePlanner();
|
||||
RouteQualityEvaluator routeEvaluator = new RouteQualityEvaluator();
|
||||
VehicleAssignmentService assignment = new VehicleAssignmentService();
|
||||
CorridorReservationManager reservations = new CorridorReservationManager();
|
||||
ClearanceMovePlanner clearancePlanner = new ClearanceMovePlanner();
|
||||
@@ -105,8 +114,8 @@ public final class HistoricalDispatchSimulator {
|
||||
applyVehicleEvents(tick, now, faults, vehicles, active,
|
||||
clearanceMoves, queue, executions, reservations, events);
|
||||
assignReadyTasks(tick, now, input, queue, vehicles, active,
|
||||
clearanceMoves, executions, terminal, planner, assignment, plc,
|
||||
reservations, events);
|
||||
clearanceMoves, executions, terminal, routePlanner, routeEvaluator,
|
||||
assignment, plc, ledger, reservations, events);
|
||||
// 本 tick 已通过的边被共享给两类移动,禁止另一车辆同 tick 重复或反向穿越。
|
||||
Set<String> traversedEdges = new LinkedHashSet<String>();
|
||||
ClearanceProgress clearanceProgress = advanceClearanceMoves(
|
||||
@@ -269,9 +278,11 @@ public final class HistoricalDispatchSimulator {
|
||||
* @param clearanceMoves 执行该处理所需的 clearance moves 输入。
|
||||
* @param executions 执行该处理所需的 executions 输入。
|
||||
* @param terminal 执行该处理所需的 terminal 输入。
|
||||
* @param planner 执行该处理所需的 planner 输入。
|
||||
* @param routePlanner 生成 110% 物理距离内的确定性无环候选。
|
||||
* @param routeEvaluator 使用同一交通快照按固定业务元组选择候选。
|
||||
* @param assignment 执行该处理所需的 assignment 输入。
|
||||
* @param plc 执行该处理所需的 plc 输入。
|
||||
* @param ledger 当前节点和边资源的权威台账。
|
||||
* @param reservations 执行该处理所需的 reservations 输入。
|
||||
* @param events 执行该处理所需的 events 输入。
|
||||
* @return 本 tick 是否至少有一个任务被分配或因永久不可达关闭。
|
||||
@@ -283,9 +294,11 @@ public final class HistoricalDispatchSimulator {
|
||||
Map<String, ClearanceMove> clearanceMoves,
|
||||
Map<String, Execution> executions,
|
||||
Map<String, SimulationTaskResult> terminal,
|
||||
GridPathPlanner planner,
|
||||
RouteCandidatePlanner routePlanner,
|
||||
RouteQualityEvaluator routeEvaluator,
|
||||
VehicleAssignmentService assignment,
|
||||
SimulatedPlcGateway plc,
|
||||
ResourceLedger ledger,
|
||||
CorridorReservationManager reservations,
|
||||
List<SimulationTickEvent> events) {
|
||||
boolean progress = false;
|
||||
@@ -295,10 +308,11 @@ public final class HistoricalDispatchSimulator {
|
||||
for (HistoricalSimulationTask task : ready) {
|
||||
if (terminal.containsKey(task.getSimulationTaskId())) continue;
|
||||
|
||||
// 服务路径由完整地图重新计算。历史 path_info 不参与规划,空路径表示业务端点永久不可达。
|
||||
List<String> servicePath = planner.shortestPath(input.getGridMap(),
|
||||
task.getPhysicalSource(), task.getPhysicalTarget(), Collections.emptySet());
|
||||
if (servicePath.isEmpty()) {
|
||||
// 先生成服务段物理候选。这里只使用永久地图,临时占用不能把任务误关为 NO_ROUTE。
|
||||
RouteCandidateSet serviceCandidates = routePlanner.plan(input.getGridMap(),
|
||||
task.getPhysicalSource(), task.getPhysicalTarget(),
|
||||
Collections.<String>emptySet(), RoutePlanningPolicy.defaultPolicy());
|
||||
if (serviceCandidates.getCandidates().isEmpty()) {
|
||||
queue.remove(task.getSimulationTaskId());
|
||||
terminal.put(task.getSimulationTaskId(), result(executions.get(
|
||||
task.getSimulationTaskId()), SimulationTaskStatus.FAILED,
|
||||
@@ -308,22 +322,24 @@ public final class HistoricalDispatchSimulator {
|
||||
|
||||
// 为每台空闲且健康的车计算到任务源点的接近路径,无法接近源点的车不进入本轮选车。
|
||||
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()) {
|
||||
if (!vehicle.isAssignmentEligible() || active.containsKey(vehicle.getVehicleId())
|
||||
|| clearanceMoves.containsKey(vehicle.getVehicleId())) {
|
||||
continue;
|
||||
}
|
||||
List<String> approach = planner.shortestPath(input.getGridMap(),
|
||||
vehicle.getCurrentSite(), task.getPhysicalSource(), Collections.emptySet());
|
||||
if (!approach.isEmpty()) {
|
||||
RouteCandidateSet approach = routePlanner.plan(input.getGridMap(),
|
||||
vehicle.getCurrentSite(), task.getPhysicalSource(),
|
||||
Collections.<String>emptySet(), RoutePlanningPolicy.defaultPolicy());
|
||||
if (!approach.getCandidates().isEmpty()) {
|
||||
candidates.add(vehicle.snapshot());
|
||||
approaches.put(vehicle.getVehicleId(), approach);
|
||||
}
|
||||
}
|
||||
if (candidates.isEmpty()) {
|
||||
// 暂无可分配车不等于失败;只有全车队都无法抵达源点时才关闭为 NO_ROUTE。
|
||||
if (!fleetCanReach(task, vehicles, input, planner)) {
|
||||
if (!fleetCanReach(task, vehicles, input, routePlanner)) {
|
||||
queue.remove(task.getSimulationTaskId());
|
||||
terminal.put(task.getSimulationTaskId(), result(executions.get(
|
||||
task.getSimulationTaskId()), SimulationTaskStatus.FAILED,
|
||||
@@ -343,8 +359,17 @@ public final class HistoricalDispatchSimulator {
|
||||
if (!decision.getVehicle().isPresent()) continue;
|
||||
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));
|
||||
Execution execution = executions.get(task.getSimulationTaskId());
|
||||
|
||||
@@ -365,6 +390,13 @@ public final class HistoricalDispatchSimulator {
|
||||
execution.lastVehicleId = vehicleId;
|
||||
execution.plannedPath = new ArrayList<String>(servicePath);
|
||||
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));
|
||||
queue.remove(task.getSimulationTaskId());
|
||||
events.add(event(tick, now, "ASSIGN", task.getSimulationTaskId(), vehicleId,
|
||||
@@ -387,14 +419,52 @@ public final class HistoricalDispatchSimulator {
|
||||
private boolean fleetCanReach(HistoricalSimulationTask task,
|
||||
Map<String, SimulatedVehicleRuntime> vehicles,
|
||||
HistoricalSimulationInput input,
|
||||
GridPathPlanner planner) {
|
||||
RouteCandidatePlanner planner) {
|
||||
for (SimulatedVehicleRuntime vehicle : vehicles.values()) {
|
||||
if (!planner.shortestPath(input.getGridMap(), vehicle.getCurrentSite(),
|
||||
task.getPhysicalSource(), Collections.emptySet()).isEmpty()) return true;
|
||||
if (!planner.plan(input.getGridMap(), vehicle.getCurrentSite(),
|
||||
task.getPhysicalSource(), Collections.<String>emptySet(),
|
||||
RoutePlanningPolicy.defaultPolicy()).getCandidates().isEmpty()) return true;
|
||||
}
|
||||
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 确认。
|
||||
*
|
||||
@@ -920,7 +990,8 @@ public final class HistoricalDispatchSimulator {
|
||||
status == SimulationTaskStatus.COMPLETED ? now : null,
|
||||
execution.holdCount,
|
||||
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>();
|
||||
private final Map<String, Integer> retryReasons =
|
||||
new LinkedHashMap<String, Integer>();
|
||||
private final List<RoutePlanningEvidence> planningEvidence =
|
||||
new ArrayList<RoutePlanningEvidence>();
|
||||
private int pathVersion;
|
||||
|
||||
/**
|
||||
* 创建 {@code Execution} 实例并初始化其内部状态。
|
||||
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public final class SimulationTaskResult {
|
||||
private final int retryCount;
|
||||
private final Map<String, Integer> holdReasons;
|
||||
private final Map<String, Integer> retryReasons;
|
||||
private final List<RoutePlanningEvidence> planningEvidence;
|
||||
|
||||
/**
|
||||
* 创建 {@code SimulationTaskResult} 实例并初始化其内部状态。
|
||||
@@ -44,6 +45,7 @@ public final class SimulationTaskResult {
|
||||
* @param retryCount 执行该处理所需的 retry count 输入。
|
||||
* @param holdReasons 执行该处理所需的 hold reasons 输入。
|
||||
* @param retryReasons 执行该处理所需的 retry reasons 输入。
|
||||
* @param planningEvidence 每个实际采用路径版本的接近段和服务段决策证据。
|
||||
*/
|
||||
SimulationTaskResult(String simulationTaskId, HistoricalQueueRecord record,
|
||||
SimulationTaskStatus status, String reason,
|
||||
@@ -52,7 +54,8 @@ public final class SimulationTaskResult {
|
||||
Long firstAssignedMillis, Long completedMillis,
|
||||
int holdCount, int protocolAttempts, int retryCount,
|
||||
Map<String, Integer> holdReasons,
|
||||
Map<String, Integer> retryReasons) {
|
||||
Map<String, Integer> retryReasons,
|
||||
List<RoutePlanningEvidence> planningEvidence) {
|
||||
this.simulationTaskId = simulationTaskId;
|
||||
this.record = record;
|
||||
this.status = status;
|
||||
@@ -68,6 +71,7 @@ public final class SimulationTaskResult {
|
||||
this.retryCount = retryCount;
|
||||
this.holdReasons = immutableMap(holdReasons);
|
||||
this.retryReasons = immutableMap(retryReasons);
|
||||
this.planningEvidence = immutable(planningEvidence);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,6 +179,9 @@ public final class SimulationTaskResult {
|
||||
* @return 本次处理产生的结果;调用方据此继续后续流程。
|
||||
*/
|
||||
public Map<String, Integer> getRetryReasons() { return retryReasons; }
|
||||
|
||||
/** 返回任务每个路径版本的接近段和服务段中心规划证据。 */
|
||||
public List<RoutePlanningEvidence> getPlanningEvidence() { return planningEvidence; }
|
||||
/**
|
||||
* 返回当前对象保存的“waiting millis”信息。
|
||||
*
|
||||
|
||||
@@ -78,11 +78,34 @@ class HistoricalDispatchSimulatorTest {
|
||||
alternateRouteInput(), SimulationConfig.defaults(),
|
||||
DeterministicFaultPlan.none());
|
||||
|
||||
assertEquals(Arrays.asList("A", "C"),
|
||||
assertEquals(Arrays.asList("A", "B", "C"),
|
||||
result.getTaskResults().get(0).getPlannedPath());
|
||||
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”场景,确保相关业务规则和断言成立。
|
||||
*
|
||||
@@ -211,13 +234,46 @@ class HistoricalDispatchSimulatorTest {
|
||||
"test", "test", nodes, edges, Collections.emptyMap(),
|
||||
Collections.emptySet(), Collections.emptySet()));
|
||||
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),
|
||||
Collections.emptyList(), grid, traffic,
|
||||
Collections.singletonList(new VehicleSnapshot(
|
||||
"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”场景,确保相关业务规则和断言成立。
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user