feat(javaTest): execute source-aligned business phases
This commit is contained in:
@@ -92,7 +92,8 @@ public final class HistoricalDispatchSimulator {
|
||||
for (HistoricalSimulationTask task : input.getTasks()) {
|
||||
long arrival = clock.toSimulationMillis(task.getCreatedAt());
|
||||
queue.add(task, arrival);
|
||||
executions.put(task.getSimulationTaskId(), new Execution(task, arrival));
|
||||
executions.put(task.getSimulationTaskId(), new Execution(
|
||||
task, arrival, input.getExecutionMode()));
|
||||
}
|
||||
|
||||
List<SimulationTickEvent> events = new ArrayList<SimulationTickEvent>();
|
||||
@@ -450,9 +451,11 @@ public final class HistoricalDispatchSimulator {
|
||||
task.getSimulationTaskId(), vehicleId);
|
||||
List<String> postTransferRoute = new ArrayList<String>(
|
||||
postTransferApproach.getSelectedPath());
|
||||
for (int index = 1;
|
||||
index < postTransferService.getSelectedPath().size(); index++) {
|
||||
postTransferRoute.add(postTransferService.getSelectedPath().get(index));
|
||||
if (!execution.isBusinessMode()) {
|
||||
for (int index = 1;
|
||||
index < postTransferService.getSelectedPath().size(); index++) {
|
||||
postTransferRoute.add(postTransferService.getSelectedPath().get(index));
|
||||
}
|
||||
}
|
||||
String destinationHold = destinationCommitmentReason(
|
||||
vehicleId, postTransferRoute.get(postTransferRoute.size() - 1),
|
||||
@@ -547,10 +550,12 @@ public final class HistoricalDispatchSimulator {
|
||||
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));
|
||||
if (!execution.isBusinessMode()) {
|
||||
for (int i = 1; i < servicePath.size(); i++) route.add(servicePath.get(i));
|
||||
}
|
||||
String destinationHold = destinationCommitmentReason(
|
||||
vehicleId, route.get(route.size() - 1), vehicles, active,
|
||||
floorTransfers, input.getGridMap());
|
||||
@@ -582,18 +587,27 @@ public final class HistoricalDispatchSimulator {
|
||||
// 预约成功才正式交接给活动任务;这些字段同时构成“历史方案与新方案”的可审计差异。
|
||||
recordAssignment(execution, vehicleId, now);
|
||||
if (execution.assignmentMode == null) execution.assignmentMode = AssignmentMode.DIRECT;
|
||||
execution.plannedPath = new ArrayList<String>(servicePath);
|
||||
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,
|
||||
historicalReferenceQuality(task, input, traffic, routePlanner,
|
||||
routeEvaluator, vehicleId)));
|
||||
active.put(vehicleId, new ActiveTask(execution, route,
|
||||
approachDecision.getSelectedPath().size() - 1));
|
||||
if (execution.isBusinessMode()) {
|
||||
// 第一阶段路径就是所选车辆从当前位置到真实取货点的接近路径。
|
||||
execution.plannedPath = new ArrayList<String>();
|
||||
BusinessTaskLeg leg = execution.currentLeg();
|
||||
execution.beginPhase(leg, route.get(0), route, now);
|
||||
active.put(vehicleId, new ActiveTask(execution, route, 0,
|
||||
leg.getPhase(), execution.pathVersion));
|
||||
} else {
|
||||
execution.plannedPath = new ArrayList<String>(servicePath);
|
||||
execution.planningEvidence.add(new RoutePlanningEvidence(
|
||||
RoutePlanningEvidence.PlanningStage.SERVICE,
|
||||
execution.pathVersion, now, false, serviceDecision,
|
||||
historicalReferenceQuality(task, input, traffic, routePlanner,
|
||||
routeEvaluator, vehicleId)));
|
||||
active.put(vehicleId, new ActiveTask(execution, route,
|
||||
approachDecision.getSelectedPath().size() - 1));
|
||||
}
|
||||
queue.remove(task.getSimulationTaskId());
|
||||
events.add(event(tick, now, "ASSIGN", task.getSimulationTaskId(), vehicleId,
|
||||
decision.getReason(), route.get(0), route.get(route.size() - 1)));
|
||||
@@ -1017,8 +1031,10 @@ public final class HistoricalDispatchSimulator {
|
||||
serviceCandidates, traffic, task.getSimulationTaskId(),
|
||||
vehicle.getVehicleId());
|
||||
List<String> route = new ArrayList<String>(approachDecision.getSelectedPath());
|
||||
for (int index = 1; index < serviceDecision.getSelectedPath().size(); index++) {
|
||||
route.add(serviceDecision.getSelectedPath().get(index));
|
||||
if (!execution.isBusinessMode()) {
|
||||
for (int index = 1; index < serviceDecision.getSelectedPath().size(); index++) {
|
||||
route.add(serviceDecision.getSelectedPath().get(index));
|
||||
}
|
||||
}
|
||||
|
||||
CorridorReservationManager.ReservationDecision reservation = reservations.replaceRoute(
|
||||
@@ -1033,18 +1049,26 @@ public final class HistoricalDispatchSimulator {
|
||||
}
|
||||
|
||||
// 预约成功后才提交新路径版本;证据中的接近段必定从设备完成事实返回的实际出口开始。
|
||||
execution.plannedPath = new ArrayList<String>(serviceDecision.getSelectedPath());
|
||||
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,
|
||||
historicalReferenceQuality(task, input, traffic, routePlanner,
|
||||
routeEvaluator, vehicle.getVehicleId())));
|
||||
active.put(vehicle.getVehicleId(), new ActiveTask(execution, route,
|
||||
approachDecision.getSelectedPath().size() - 1));
|
||||
if (execution.isBusinessMode()) {
|
||||
execution.plannedPath = new ArrayList<String>();
|
||||
BusinessTaskLeg leg = execution.currentLeg();
|
||||
execution.beginPhase(leg, route.get(0), route, now);
|
||||
active.put(vehicle.getVehicleId(), new ActiveTask(execution, route, 0,
|
||||
leg.getPhase(), execution.pathVersion));
|
||||
} else {
|
||||
execution.plannedPath = new ArrayList<String>(serviceDecision.getSelectedPath());
|
||||
execution.planningEvidence.add(new RoutePlanningEvidence(
|
||||
RoutePlanningEvidence.PlanningStage.SERVICE,
|
||||
execution.pathVersion, now, false, serviceDecision,
|
||||
historicalReferenceQuality(task, input, traffic, routePlanner,
|
||||
routeEvaluator, vehicle.getVehicleId())));
|
||||
active.put(vehicle.getVehicleId(), new ActiveTask(execution, route,
|
||||
approachDecision.getSelectedPath().size() - 1));
|
||||
}
|
||||
floorTransfers.remove(vehicle.getVehicleId());
|
||||
events.add(event(tick, now, "POST_TRANSFER_REPLAN",
|
||||
task.getSimulationTaskId(), vehicle.getVehicleId(),
|
||||
@@ -1311,9 +1335,11 @@ public final class HistoricalDispatchSimulator {
|
||||
SimulatedVehicleRuntime vehicle = vehicles.get(vehicleId);
|
||||
if (!vehicle.isAssignmentEligible()) continue;
|
||||
if (current.index >= current.route.size() - 1) {
|
||||
// 路线已经走完时不再申请资源,直接进入归档和 WMS 回调闭环。
|
||||
complete(tick, now, current, vehicle, active, terminal, plc,
|
||||
archive, wms, invariants, reservations, events);
|
||||
// 原始路线直接归档;业务路线只关闭当前阶段,并从车辆事实位置规划下一阶段。
|
||||
finishReachedRoute(tick, now, current, vehicle, input, config, queue,
|
||||
vehicles, active, terminal, routePlanner, routeEvaluator,
|
||||
ledger, plc, archive, wms, invariants, reservations,
|
||||
clearanceMoves, floorTransfers, clearancePlanner, events);
|
||||
progress = true;
|
||||
continue;
|
||||
}
|
||||
@@ -1392,8 +1418,11 @@ public final class HistoricalDispatchSimulator {
|
||||
continue;
|
||||
}
|
||||
// 资源已预占后才下发 PLC;只有确认 ACK 才能提交车辆位置变化。
|
||||
SimulatedPlcGateway.PlcResponse sent = plc.send(
|
||||
current.execution.task.getSimulationTaskId(), vehicle, from, to);
|
||||
String parentTaskId = current.execution.task.getSimulationTaskId();
|
||||
String commandId = current.phase == null ? parentTaskId : current.commandId();
|
||||
SimulatedPlcGateway.PlcResponse sent = current.phase == null
|
||||
? plc.send(parentTaskId, vehicle, from, to)
|
||||
: plc.send(parentTaskId, commandId, vehicle, from, to);
|
||||
SimulatedPlcGateway.PlcResponse accepted = plc.accept(sent, vehicle);
|
||||
if (accepted.getReason() != SimulatedPlcGateway.PlcReason.ACK) {
|
||||
// 命令未被确认时撤销本步新增占用,但保留当前位置和路线索引以便安全重试。
|
||||
@@ -1402,8 +1431,10 @@ public final class HistoricalDispatchSimulator {
|
||||
events.add(event(tick, now, "PLC_RETRY",
|
||||
current.execution.task.getSimulationTaskId(), vehicleId,
|
||||
accepted.getReason().name(), from, to));
|
||||
if (plc.attemptsFor(current.execution.task.getSimulationTaskId())
|
||||
% config.getRetryLimit() == 0) {
|
||||
int commandAttempts = current.phase == null
|
||||
? plc.attemptsForTask(parentTaskId)
|
||||
: plc.attemptsForCommand(commandId);
|
||||
if (commandAttempts % config.getRetryLimit() == 0) {
|
||||
// 重试额度耗尽后释放未来走廊并回队,避免单车协议异常无限阻塞全局交通。
|
||||
active.remove(vehicleId);
|
||||
reservations.releaseTask(current.execution.task.getSimulationTaskId());
|
||||
@@ -1429,14 +1460,235 @@ public final class HistoricalDispatchSimulator {
|
||||
"ACK", from, to));
|
||||
progress = true;
|
||||
if (current.index >= current.route.size() - 1) {
|
||||
// 最后一条边确认后立即关闭任务,避免下一个 tick 才释放剩余走廊和归档。
|
||||
complete(tick, now, current, vehicle, active, terminal, plc,
|
||||
archive, wms, invariants, reservations, events);
|
||||
// 最后一条边 ACK 后立即提交当前阶段,业务下一段从这个真实位置重新规划。
|
||||
finishReachedRoute(tick, now, current, vehicle, input, config, queue,
|
||||
vehicles, active, terminal, routePlanner, routeEvaluator,
|
||||
ledger, plc, archive, wms, invariants, reservations,
|
||||
clearanceMoves, floorTransfers, clearancePlanner, events);
|
||||
}
|
||||
}
|
||||
return progress;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理一条活动路线的终点:旧回放直接结束任务,业务回放只提交当前阶段并继续下一段。
|
||||
*/
|
||||
private void finishReachedRoute(
|
||||
long tick, long now, ActiveTask current, SimulatedVehicleRuntime vehicle,
|
||||
HistoricalSimulationInput input, SimulationConfig config,
|
||||
SimulationTaskQueue queue,
|
||||
Map<String, SimulatedVehicleRuntime> vehicles,
|
||||
Map<String, ActiveTask> active,
|
||||
Map<String, SimulationTaskResult> terminal,
|
||||
RouteCandidatePlanner routePlanner, RouteQualityEvaluator routeEvaluator,
|
||||
ResourceLedger ledger, SimulatedPlcGateway plc,
|
||||
SimulationArchive archive, IdempotentWmsCompletionReceiver wms,
|
||||
SimulationInvariantSummary invariants,
|
||||
CorridorReservationManager reservations,
|
||||
Map<String, ClearanceMove> clearanceMoves,
|
||||
Map<String, FloorTransferExecution> floorTransfers,
|
||||
ClearanceMovePlanner clearancePlanner,
|
||||
List<SimulationTickEvent> events) {
|
||||
if (!current.execution.isBusinessMode()) {
|
||||
complete(tick, now, current, vehicle, active, terminal, plc,
|
||||
archive, wms, invariants, reservations, events);
|
||||
return;
|
||||
}
|
||||
completeBusinessPhase(tick, now, current, vehicle, input, config, queue,
|
||||
vehicles, active, terminal, routePlanner, routeEvaluator,
|
||||
ledger, plc, archive, wms, invariants, reservations,
|
||||
clearanceMoves, floorTransfers, clearancePlanner, events);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在路线终点 ACK 后提交业务阶段;放货只归档一次,最后一段才解除车辆与父任务的绑定。
|
||||
*/
|
||||
private void completeBusinessPhase(
|
||||
long tick, long now, ActiveTask current, SimulatedVehicleRuntime vehicle,
|
||||
HistoricalSimulationInput input, SimulationConfig config,
|
||||
SimulationTaskQueue queue,
|
||||
Map<String, SimulatedVehicleRuntime> vehicles,
|
||||
Map<String, ActiveTask> active,
|
||||
Map<String, SimulationTaskResult> terminal,
|
||||
RouteCandidatePlanner routePlanner, RouteQualityEvaluator routeEvaluator,
|
||||
ResourceLedger ledger, SimulatedPlcGateway plc,
|
||||
SimulationArchive archive, IdempotentWmsCompletionReceiver wms,
|
||||
SimulationInvariantSummary invariants,
|
||||
CorridorReservationManager reservations,
|
||||
Map<String, ClearanceMove> clearanceMoves,
|
||||
Map<String, FloorTransferExecution> floorTransfers,
|
||||
ClearanceMovePlanner clearancePlanner,
|
||||
List<SimulationTickEvent> events) {
|
||||
Execution execution = current.execution;
|
||||
String taskId = execution.task.getSimulationTaskId();
|
||||
|
||||
if (!current.phaseCompletionApplied) {
|
||||
BusinessTaskLeg completedLeg = execution.currentLeg();
|
||||
reservations.releaseTask(taskId);
|
||||
PhaseProgress phase = execution.currentPhaseProgress();
|
||||
if (phase == null || phase.phase != completedLeg.getPhase()) {
|
||||
throw new IllegalStateException("missing active phase evidence for " + taskId);
|
||||
}
|
||||
String completionReason = phaseCompletionReason(completedLeg.getPhase());
|
||||
phase.complete(now, completionReason);
|
||||
execution.cargoState = completedLeg.getCargoStateOnArrival();
|
||||
current.phaseCompletionApplied = true;
|
||||
events.add(event(tick, now, completionReason, taskId,
|
||||
vehicle.getVehicleId(), "ACK", phase.fromSite, phase.toSite));
|
||||
|
||||
// 货物送达业务目标是唯一归档和 WMS 回调点;后续空车回前端不会重复业务副作用。
|
||||
if (completedLeg.completesBusiness()
|
||||
&& execution.businessCompletedMillis == null) {
|
||||
if (!archive.complete(taskId, vehicle.getVehicleId(), now)) {
|
||||
invariants.duplicateArchiveAttempt();
|
||||
}
|
||||
boolean first = wms.receive(taskId);
|
||||
boolean duplicate = wms.receive(taskId);
|
||||
if (!first || duplicate) invariants.duplicateWmsBusinessEffect();
|
||||
execution.businessCompletedMillis = now;
|
||||
events.add(event(tick, now, "BUSINESS_ARCHIVED", taskId,
|
||||
vehicle.getVehicleId(), "CARGO_DEPOSITED",
|
||||
vehicle.getCurrentSite(), vehicle.getCurrentSite()));
|
||||
events.add(event(tick, now, "WMS_CALLBACK", taskId,
|
||||
vehicle.getVehicleId(), "IDEMPOTENT_EFFECT=1;DELIVERIES=2",
|
||||
vehicle.getCurrentSite(), vehicle.getCurrentSite()));
|
||||
}
|
||||
|
||||
if (completedLeg.releasesVehicle()) {
|
||||
execution.vehicleReleasedMillis = now;
|
||||
execution.vehicleReleaseStatus = VehicleReleaseStatus.COMPLETED;
|
||||
terminal.put(taskId, result(execution,
|
||||
SimulationTaskStatus.COMPLETED, "COMPLETED", plc, now));
|
||||
active.remove(vehicle.getVehicleId());
|
||||
events.add(event(tick, now, "VEHICLE_RELEASED", taskId,
|
||||
vehicle.getVehicleId(), "BUSINESS_PHASES_COMPLETED",
|
||||
vehicle.getCurrentSite(), vehicle.getCurrentSite()));
|
||||
events.add(event(tick, now, "COMPLETE", taskId,
|
||||
vehicle.getVehicleId(), "ARCHIVED_WMS_ACK",
|
||||
vehicle.getCurrentSite(), vehicle.getCurrentSite()));
|
||||
return;
|
||||
}
|
||||
execution.phaseIndex++;
|
||||
}
|
||||
|
||||
// 阶段切换不复用上一段路径,从车辆已确认的实际站点重新执行候选规划和走廊预约。
|
||||
activateNextBusinessPhase(tick, now, current, vehicle, input, config, queue,
|
||||
vehicles, active, terminal, routePlanner, routeEvaluator,
|
||||
ledger, plc, reservations, clearanceMoves, floorTransfers,
|
||||
clearancePlanner, events);
|
||||
}
|
||||
|
||||
/** 从车辆当前位置规划父任务下一业务阶段,并保持同一车辆绑定。 */
|
||||
private boolean activateNextBusinessPhase(
|
||||
long tick, long now, ActiveTask completedRoute,
|
||||
SimulatedVehicleRuntime vehicle, HistoricalSimulationInput input,
|
||||
SimulationConfig config, SimulationTaskQueue queue,
|
||||
Map<String, SimulatedVehicleRuntime> vehicles,
|
||||
Map<String, ActiveTask> active,
|
||||
Map<String, SimulationTaskResult> terminal,
|
||||
RouteCandidatePlanner routePlanner, RouteQualityEvaluator routeEvaluator,
|
||||
ResourceLedger ledger, SimulatedPlcGateway plc,
|
||||
CorridorReservationManager reservations,
|
||||
Map<String, ClearanceMove> clearanceMoves,
|
||||
Map<String, FloorTransferExecution> floorTransfers,
|
||||
ClearanceMovePlanner clearancePlanner,
|
||||
List<SimulationTickEvent> events) {
|
||||
Execution execution = completedRoute.execution;
|
||||
BusinessTaskLeg leg = execution.currentLeg();
|
||||
String taskId = execution.task.getSimulationTaskId();
|
||||
String fromSite = vehicle.getCurrentSite();
|
||||
|
||||
// 先建立阶段槽位,预约或终点承诺产生的 HOLD 才能准确归入即将执行的阶段。
|
||||
PhaseProgress phase = execution.beginPhase(leg, fromSite,
|
||||
Collections.singletonList(fromSite), now);
|
||||
RouteCandidateSet candidates = routePlanner.plan(input.getGridMap(),
|
||||
fromSite, leg.getTargetSite(), Collections.<String>emptySet(),
|
||||
RoutePlanningPolicy.defaultPolicy());
|
||||
if (candidates.getCandidates().isEmpty()) {
|
||||
String reason = "NO_ROUTE_" + leg.getPhase();
|
||||
phase.complete(now, reason);
|
||||
reservations.releaseTask(taskId);
|
||||
active.remove(vehicle.getVehicleId());
|
||||
execution.vehicleReleaseStatus = execution.businessCompletedMillis == null
|
||||
? VehicleReleaseStatus.PENDING : VehicleReleaseStatus.FAILED;
|
||||
terminal.put(taskId, result(execution,
|
||||
SimulationTaskStatus.FAILED, reason, plc, now));
|
||||
events.add(event(tick, now, "PHASE_FAILED", taskId,
|
||||
vehicle.getVehicleId(), reason, fromSite, leg.getTargetSite()));
|
||||
return true;
|
||||
}
|
||||
|
||||
TrafficRouteSnapshot traffic = trafficSnapshot(vehicles, active, ledger, reservations);
|
||||
RoutePlanDecision decision = routeEvaluator.select(input.getGridMap(), candidates,
|
||||
traffic, taskId, vehicle.getVehicleId());
|
||||
List<String> route = new ArrayList<String>(decision.getSelectedPath());
|
||||
String destinationHold = destinationCommitmentReason(
|
||||
vehicle.getVehicleId(), leg.getTargetSite(), vehicles,
|
||||
active, floorTransfers, input.getGridMap());
|
||||
if (destinationHold != null) {
|
||||
if ("DESTINATION_OCCUPIED".equals(destinationHold)) {
|
||||
planDestinationClearance(tick, now, taskId, vehicle.getVehicleId(),
|
||||
route, vehicles, active, clearanceMoves, floorTransfers,
|
||||
clearancePlanner, ledger, input.getGridMap(), events);
|
||||
}
|
||||
hold(execution, destinationHold);
|
||||
events.add(event(tick, now, "PHASE_HOLD", taskId,
|
||||
vehicle.getVehicleId(), destinationHold,
|
||||
fromSite, leg.getTargetSite()));
|
||||
return false;
|
||||
}
|
||||
|
||||
CorridorReservationManager.ReservationDecision reservation = reservations.reserve(
|
||||
taskId, route, queuePriority(execution, now, config),
|
||||
execution.arrivalMillis);
|
||||
if (!reservation.isGranted()) {
|
||||
hold(execution, reservation.getReason());
|
||||
events.add(event(tick, now, "PHASE_HOLD", taskId,
|
||||
vehicle.getVehicleId(), reservation.getReason(),
|
||||
fromSite, leg.getTargetSite()));
|
||||
return false;
|
||||
}
|
||||
|
||||
execution.pathVersion++;
|
||||
execution.planningEvidence.add(new RoutePlanningEvidence(
|
||||
RoutePlanningEvidence.PlanningStage.SERVICE,
|
||||
execution.pathVersion, now, false, decision));
|
||||
phase.plannedPath = new ArrayList<String>(route);
|
||||
if (leg.getPhase() == BusinessTaskPhase.LOADED_RETURN_TO_SOURCE_FRONT
|
||||
|| leg.getPhase() == BusinessTaskPhase.LOADED_TO_PLACE) {
|
||||
appendBusinessPath(execution.plannedPath, route);
|
||||
}
|
||||
active.put(vehicle.getVehicleId(), new ActiveTask(execution, route, 0,
|
||||
leg.getPhase(), execution.pathVersion));
|
||||
events.add(event(tick, now, "PHASE_ASSIGN", taskId,
|
||||
vehicle.getVehicleId(), leg.getPhase().name(),
|
||||
fromSite, leg.getTargetSite()));
|
||||
return true;
|
||||
}
|
||||
|
||||
/** 把阶段路径拼成兼容旧报告的总路线,同时保留 TaskPhaseEvidence 中的边界。 */
|
||||
private void appendBusinessPath(List<String> completePath, List<String> phasePath) {
|
||||
if (phasePath.isEmpty()) return;
|
||||
int start = !completePath.isEmpty()
|
||||
&& completePath.get(completePath.size() - 1).equals(phasePath.get(0)) ? 1 : 0;
|
||||
for (int index = start; index < phasePath.size(); index++) {
|
||||
completePath.add(phasePath.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
/** 将移动阶段转换成用户可识别的业务事实事件。 */
|
||||
private String phaseCompletionReason(BusinessTaskPhase phase) {
|
||||
if (phase == BusinessTaskPhase.EMPTY_TO_PICKUP) return "PICKUP_CONFIRMED";
|
||||
if (phase == BusinessTaskPhase.LOADED_RETURN_TO_SOURCE_FRONT) {
|
||||
return "SOURCE_FRONT_REACHED";
|
||||
}
|
||||
if (phase == BusinessTaskPhase.LOADED_TO_PLACE) return "CARGO_DEPOSITED";
|
||||
if (phase == BusinessTaskPhase.EMPTY_RETURN_TO_TARGET_FRONT) {
|
||||
return "TARGET_FRONT_REACHED";
|
||||
}
|
||||
return "PHASE_COMPLETED";
|
||||
}
|
||||
|
||||
/**
|
||||
* 逐段执行为主任务让路的清障移动,并施加与正常任务相同的方向、交通、资源和 PLC 约束。
|
||||
*
|
||||
@@ -1946,7 +2198,10 @@ public final class HistoricalDispatchSimulator {
|
||||
plc.attemptsFor(execution.task.getSimulationTaskId()),
|
||||
execution.retryCount, execution.holdReasons, execution.retryReasons,
|
||||
execution.planningEvidence, execution.rerouteCount,
|
||||
execution.assignmentMode, execution.floorTransferJobs);
|
||||
execution.assignmentMode, execution.floorTransferJobs,
|
||||
execution.phaseEvidence(), execution.cargoState,
|
||||
execution.businessCompletedMillis, execution.vehicleReleasedMillis,
|
||||
execution.vehicleReleaseStatus, execution.skippedStageReasons());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1976,6 +2231,8 @@ public final class HistoricalDispatchSimulator {
|
||||
private static void hold(Execution execution, String reason) {
|
||||
execution.holdCount++;
|
||||
increment(execution.holdReasons, reason);
|
||||
PhaseProgress phase = execution.currentPhaseProgress();
|
||||
if (phase != null) phase.holdCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1986,6 +2243,8 @@ public final class HistoricalDispatchSimulator {
|
||||
private static void retry(Execution execution, String reason) {
|
||||
execution.retryCount++;
|
||||
increment(execution.retryReasons, reason);
|
||||
PhaseProgress phase = execution.currentPhaseProgress();
|
||||
if (phase != null) phase.retryCount++;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2020,6 +2279,15 @@ public final class HistoricalDispatchSimulator {
|
||||
private static final class Execution {
|
||||
private final HistoricalSimulationTask task;
|
||||
private final long arrivalMillis;
|
||||
private final SimulationExecutionMode executionMode;
|
||||
private final BusinessPhasePlan phasePlan;
|
||||
private int phaseIndex;
|
||||
private CargoState cargoState = CargoState.EMPTY;
|
||||
private Long businessCompletedMillis;
|
||||
private Long vehicleReleasedMillis;
|
||||
private VehicleReleaseStatus vehicleReleaseStatus;
|
||||
private final List<PhaseProgress> phaseProgress =
|
||||
new ArrayList<PhaseProgress>();
|
||||
private Long firstAssignedMillis;
|
||||
private String lastVehicleId;
|
||||
private List<String> plannedPath = new ArrayList<String>();
|
||||
@@ -2045,9 +2313,90 @@ public final class HistoricalDispatchSimulator {
|
||||
* @param task 执行该处理所需的 task 输入。
|
||||
* @param arrivalMillis 执行该处理所需的 arrival millis 输入。
|
||||
*/
|
||||
private Execution(HistoricalSimulationTask task, long arrivalMillis) {
|
||||
private Execution(HistoricalSimulationTask task, long arrivalMillis,
|
||||
SimulationExecutionMode executionMode) {
|
||||
this.task = task;
|
||||
this.arrivalMillis = arrivalMillis;
|
||||
this.executionMode = executionMode;
|
||||
if (executionMode == SimulationExecutionMode.SOURCE_ALIGNED_BUSINESS) {
|
||||
this.phasePlan = new BusinessPhasePlanner().plan(task);
|
||||
this.vehicleReleaseStatus = VehicleReleaseStatus.PENDING;
|
||||
} else {
|
||||
this.phasePlan = null;
|
||||
this.vehicleReleaseStatus = VehicleReleaseStatus.NOT_REQUIRED;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isBusinessMode() {
|
||||
return executionMode == SimulationExecutionMode.SOURCE_ALIGNED_BUSINESS;
|
||||
}
|
||||
|
||||
private BusinessTaskLeg currentLeg() {
|
||||
return phasePlan.getLegs().get(phaseIndex);
|
||||
}
|
||||
|
||||
private PhaseProgress beginPhase(BusinessTaskLeg leg, String fromSite,
|
||||
List<String> path, long now) {
|
||||
PhaseProgress existing = currentPhaseProgress();
|
||||
if (existing != null && existing.phase == leg.getPhase()) {
|
||||
existing.plannedPath = new ArrayList<String>(path);
|
||||
return existing;
|
||||
}
|
||||
PhaseProgress created = new PhaseProgress(
|
||||
leg.getPhase(), fromSite, leg.getTargetSite(), path, now);
|
||||
phaseProgress.add(created);
|
||||
return created;
|
||||
}
|
||||
|
||||
private PhaseProgress currentPhaseProgress() {
|
||||
if (phaseProgress.isEmpty()) return null;
|
||||
PhaseProgress current = phaseProgress.get(phaseProgress.size() - 1);
|
||||
return current.completedMillis == null ? current : null;
|
||||
}
|
||||
|
||||
private List<TaskPhaseEvidence> phaseEvidence() {
|
||||
List<TaskPhaseEvidence> evidence = new ArrayList<TaskPhaseEvidence>();
|
||||
for (PhaseProgress phase : phaseProgress) evidence.add(phase.freeze());
|
||||
return evidence;
|
||||
}
|
||||
|
||||
private List<String> skippedStageReasons() {
|
||||
return phasePlan == null ? Collections.<String>emptyList()
|
||||
: phasePlan.getSkippedStageReasons();
|
||||
}
|
||||
}
|
||||
|
||||
/** 模拟器内部的可变阶段记录,只在生成结果时冻结为 TaskPhaseEvidence。 */
|
||||
private static final class PhaseProgress {
|
||||
private final BusinessTaskPhase phase;
|
||||
private final String fromSite;
|
||||
private final String toSite;
|
||||
private List<String> plannedPath;
|
||||
private final long startedMillis;
|
||||
private Long completedMillis;
|
||||
private int holdCount;
|
||||
private int retryCount;
|
||||
private String completionReason;
|
||||
|
||||
private PhaseProgress(BusinessTaskPhase phase, String fromSite, String toSite,
|
||||
List<String> plannedPath, long startedMillis) {
|
||||
this.phase = phase;
|
||||
this.fromSite = fromSite;
|
||||
this.toSite = toSite;
|
||||
this.plannedPath = new ArrayList<String>(plannedPath);
|
||||
this.startedMillis = startedMillis;
|
||||
}
|
||||
|
||||
private void complete(long now, String reason) {
|
||||
if (completedMillis != null) return;
|
||||
completedMillis = now;
|
||||
completionReason = reason;
|
||||
}
|
||||
|
||||
private TaskPhaseEvidence freeze() {
|
||||
return new TaskPhaseEvidence(phase, fromSite, toSite, plannedPath,
|
||||
startedMillis, completedMillis, holdCount, retryCount,
|
||||
completionReason);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2093,6 +2442,8 @@ public final class HistoricalDispatchSimulator {
|
||||
|
||||
private static final class ActiveTask {
|
||||
private final Execution execution;
|
||||
private final BusinessTaskPhase phase;
|
||||
private final int routeVersion;
|
||||
private List<String> route;
|
||||
private int index;
|
||||
private int serviceStartIndex;
|
||||
@@ -2100,6 +2451,7 @@ public final class HistoricalDispatchSimulator {
|
||||
private final TrafficHoldTracker trafficHolds =
|
||||
new TrafficHoldTracker(TRAFFIC_HOLD_REROUTE_THRESHOLD);
|
||||
private boolean movedSinceReroute = true;
|
||||
private boolean phaseCompletionApplied;
|
||||
|
||||
/**
|
||||
* 创建 {@code ActiveTask} 实例并初始化其内部状态。
|
||||
@@ -2110,12 +2462,26 @@ public final class HistoricalDispatchSimulator {
|
||||
* @param serviceStartIndex 服务段源点在完整执行路线中的下标。
|
||||
*/
|
||||
private ActiveTask(Execution execution, List<String> route, int serviceStartIndex) {
|
||||
this(execution, route, serviceStartIndex, null,
|
||||
execution.pathVersion);
|
||||
}
|
||||
|
||||
/** 创建一段绑定父任务和业务阶段的活动路线,供 PLC 命令生成稳定幂等键。 */
|
||||
private ActiveTask(Execution execution, List<String> route, int serviceStartIndex,
|
||||
BusinessTaskPhase phase, int routeVersion) {
|
||||
this.execution = execution;
|
||||
this.phase = phase;
|
||||
this.routeVersion = routeVersion;
|
||||
this.route = Collections.unmodifiableList(new ArrayList<String>(route));
|
||||
this.serviceStartIndex = serviceStartIndex;
|
||||
if (serviceStartIndex == 0) traversedServicePath.add(route.get(0));
|
||||
}
|
||||
|
||||
private String commandId() {
|
||||
return execution.task.getSimulationTaskId() + ":" + phase
|
||||
+ ":v" + routeVersion + ":e" + index;
|
||||
}
|
||||
|
||||
/** 返回车辆是否已经到达业务源点并开始执行服务段。 */
|
||||
private boolean hasReachedServiceSource() {
|
||||
return index >= serviceStartIndex || !traversedServicePath.isEmpty();
|
||||
|
||||
@@ -28,6 +28,12 @@ public final class SimulationTaskResult {
|
||||
private final int rerouteCount;
|
||||
private final AssignmentMode assignmentMode;
|
||||
private final List<FloorTransferJob> floorTransferJobs;
|
||||
private final List<TaskPhaseEvidence> phaseEvidence;
|
||||
private final CargoState cargoState;
|
||||
private final Long businessCompletedMillis;
|
||||
private final Long vehicleReleasedMillis;
|
||||
private final VehicleReleaseStatus vehicleReleaseStatus;
|
||||
private final List<String> skippedStageReasons;
|
||||
|
||||
/**
|
||||
* 创建 {@code SimulationTaskResult} 实例并初始化其内部状态。
|
||||
@@ -64,6 +70,33 @@ public final class SimulationTaskResult {
|
||||
List<RoutePlanningEvidence> planningEvidence,
|
||||
int rerouteCount, AssignmentMode assignmentMode,
|
||||
List<FloorTransferJob> floorTransferJobs) {
|
||||
this(simulationTaskId, record, status, reason, simulatedVehicleId,
|
||||
plannedPath, assignedVehicles, arrivalMillis, firstAssignedMillis,
|
||||
completedMillis, holdCount, protocolAttempts, retryCount,
|
||||
holdReasons, retryReasons, planningEvidence, rerouteCount,
|
||||
assignmentMode, floorTransferJobs,
|
||||
Collections.<TaskPhaseEvidence>emptyList(), CargoState.EMPTY,
|
||||
null, null, VehicleReleaseStatus.NOT_REQUIRED,
|
||||
Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
/** 创建同时表达业务完成、车辆释放和逐阶段路径证据的完整结果。 */
|
||||
SimulationTaskResult(String simulationTaskId, HistoricalQueueRecord record,
|
||||
SimulationTaskStatus status, String reason,
|
||||
String simulatedVehicleId, List<String> plannedPath,
|
||||
List<String> assignedVehicles, long arrivalMillis,
|
||||
Long firstAssignedMillis, Long completedMillis,
|
||||
int holdCount, int protocolAttempts, int retryCount,
|
||||
Map<String, Integer> holdReasons,
|
||||
Map<String, Integer> retryReasons,
|
||||
List<RoutePlanningEvidence> planningEvidence,
|
||||
int rerouteCount, AssignmentMode assignmentMode,
|
||||
List<FloorTransferJob> floorTransferJobs,
|
||||
List<TaskPhaseEvidence> phaseEvidence,
|
||||
CargoState cargoState, Long businessCompletedMillis,
|
||||
Long vehicleReleasedMillis,
|
||||
VehicleReleaseStatus vehicleReleaseStatus,
|
||||
List<String> skippedStageReasons) {
|
||||
this.simulationTaskId = simulationTaskId;
|
||||
this.record = record;
|
||||
this.status = status;
|
||||
@@ -83,6 +116,12 @@ public final class SimulationTaskResult {
|
||||
this.rerouteCount = rerouteCount;
|
||||
this.assignmentMode = assignmentMode;
|
||||
this.floorTransferJobs = immutable(floorTransferJobs);
|
||||
this.phaseEvidence = immutable(phaseEvidence);
|
||||
this.cargoState = cargoState;
|
||||
this.businessCompletedMillis = businessCompletedMillis;
|
||||
this.vehicleReleasedMillis = vehicleReleasedMillis;
|
||||
this.vehicleReleaseStatus = vehicleReleaseStatus;
|
||||
this.skippedStageReasons = immutable(skippedStageReasons);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -205,6 +244,24 @@ public final class SimulationTaskResult {
|
||||
|
||||
/** 返回该父任务产生的换层业务效果数;重复提交不会增加该计数。 */
|
||||
public int getFloorTransferCount() { return floorTransferJobs.size(); }
|
||||
|
||||
/** 返回按真实执行顺序冻结的每阶段路线、时间、HOLD 和重试证据。 */
|
||||
public List<TaskPhaseEvidence> getPhaseEvidence() { return phaseEvidence; }
|
||||
|
||||
/** 返回父任务货物的最终状态;业务模式完成时应为 {@code DEPOSITED}。 */
|
||||
public CargoState getCargoState() { return cargoState; }
|
||||
|
||||
/** 返回货物入位并完成归档/WMS 回调的仿真时间。 */
|
||||
public Long getBusinessCompletedMillis() { return businessCompletedMillis; }
|
||||
|
||||
/** 返回同一车辆完成必要回前端动作并解除占车关系的仿真时间。 */
|
||||
public Long getVehicleReleasedMillis() { return vehicleReleasedMillis; }
|
||||
|
||||
/** 返回业务完成后的车辆清理动作是否仍待执行、已完成或失败。 */
|
||||
public VehicleReleaseStatus getVehicleReleaseStatus() { return vehicleReleaseStatus; }
|
||||
|
||||
/** 返回未生成通道阶段的明确原因,不允许前端自行猜测货位分组。 */
|
||||
public List<String> getSkippedStageReasons() { return skippedStageReasons; }
|
||||
/**
|
||||
* 返回当前对象保存的“waiting millis”信息。
|
||||
*
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.ft546.wcs.shadow.history.simulation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 一次业务移动阶段实际采用的路径和执行结果。
|
||||
*
|
||||
* <p>每个阶段单独保存起终点、规划路径、时间、HOLD 和 PLC 重试数,页面无需把多段路线猜成一条;
|
||||
* 未完成阶段允许 {@code completedMillis} 和 {@code completionReason} 为空,供失败/等待快照诊断。
|
||||
*/
|
||||
public final class TaskPhaseEvidence {
|
||||
private final BusinessTaskPhase phase;
|
||||
private final String fromSite;
|
||||
private final String toSite;
|
||||
private final List<String> plannedPath;
|
||||
private final long startedMillis;
|
||||
private final Long completedMillis;
|
||||
private final int holdCount;
|
||||
private final int retryCount;
|
||||
private final String completionReason;
|
||||
|
||||
public TaskPhaseEvidence(BusinessTaskPhase phase, String fromSite, String toSite,
|
||||
List<String> plannedPath, long startedMillis,
|
||||
Long completedMillis, int holdCount, int retryCount,
|
||||
String completionReason) {
|
||||
if (phase == null || blank(fromSite) || blank(toSite) || plannedPath == null
|
||||
|| plannedPath.isEmpty() || startedMillis < 0L
|
||||
|| (completedMillis != null && completedMillis < startedMillis)
|
||||
|| holdCount < 0 || retryCount < 0) {
|
||||
throw new IllegalArgumentException("valid task phase evidence is required");
|
||||
}
|
||||
this.phase = phase;
|
||||
this.fromSite = fromSite;
|
||||
this.toSite = toSite;
|
||||
this.plannedPath = Collections.unmodifiableList(
|
||||
new ArrayList<String>(plannedPath));
|
||||
this.startedMillis = startedMillis;
|
||||
this.completedMillis = completedMillis;
|
||||
this.holdCount = holdCount;
|
||||
this.retryCount = retryCount;
|
||||
this.completionReason = completionReason;
|
||||
}
|
||||
|
||||
public BusinessTaskPhase getPhase() { return phase; }
|
||||
public String getFromSite() { return fromSite; }
|
||||
public String getToSite() { return toSite; }
|
||||
public List<String> getPlannedPath() { return plannedPath; }
|
||||
public long getStartedMillis() { return startedMillis; }
|
||||
public Long getCompletedMillis() { return completedMillis; }
|
||||
public int getHoldCount() { return holdCount; }
|
||||
public int getRetryCount() { return retryCount; }
|
||||
public String getCompletionReason() { return completionReason; }
|
||||
|
||||
private static boolean blank(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.ft546.wcs.shadow.history.simulation;
|
||||
|
||||
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.GridMap;
|
||||
import com.ft546.wcs.shadow.history.HistoricalQueueRecord;
|
||||
import com.ft546.wcs.shadow.map.EffectiveTrafficMap;
|
||||
import com.ft546.wcs.shadow.map.TrafficMapCompiler;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.ft546.wcs.shadow.history.simulation.BusinessTaskPhase.EMPTY_RETURN_TO_TARGET_FRONT;
|
||||
import static com.ft546.wcs.shadow.history.simulation.BusinessTaskPhase.EMPTY_TO_PICKUP;
|
||||
import static com.ft546.wcs.shadow.history.simulation.BusinessTaskPhase.LOADED_RETURN_TO_SOURCE_FRONT;
|
||||
import static com.ft546.wcs.shadow.history.simulation.BusinessTaskPhase.LOADED_TO_PLACE;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class HistoricalDispatchSimulatorTwoStageTest {
|
||||
/** 入库在货物送入货位时归档和回调,随后同一车辆空车回前端才释放。 */
|
||||
@Test
|
||||
void inboundCallbacksAtDepositThenReturnsTheSameVehicleEmptyToFront() {
|
||||
HistoricalDispatchSimulationResult result = run(task(
|
||||
DispatchTask.TaskType.INBOUND, "PICK", "BIN", null, "FRONT"));
|
||||
SimulationTaskResult task = result.getTaskResults().get(0);
|
||||
|
||||
assertEquals(Arrays.asList(EMPTY_TO_PICKUP, LOADED_TO_PLACE,
|
||||
EMPTY_RETURN_TO_TARGET_FRONT), phases(task));
|
||||
assertEquals(Arrays.asList("PICK", "BIN", "FRONT"), targets(task));
|
||||
assertTrue(task.getBusinessCompletedMillis() < task.getVehicleReleasedMillis());
|
||||
assertEquals(CargoState.DEPOSITED, task.getCargoState());
|
||||
assertEquals(VehicleReleaseStatus.COMPLETED, task.getVehicleReleaseStatus());
|
||||
assertEquals(Collections.singletonList("1"), task.getAssignedVehicles());
|
||||
assertEquals(1, result.getArchiveCount());
|
||||
assertEquals(2, result.getWmsDeliveryAttempts());
|
||||
assertEquals(1, result.getWmsBusinessEffects());
|
||||
assertTrue(result.getEvents().stream().anyMatch(event ->
|
||||
"VEHICLE_RELEASED".equals(event.getType())
|
||||
&& "FRONT".equals(event.getToSite())));
|
||||
}
|
||||
|
||||
/** 出库取货后先载货退出源货道前端,再到业务目标,送达即归档并释放车辆。 */
|
||||
@Test
|
||||
void outboundCarriesCargoToSourceFrontBeforeBusinessDestination() {
|
||||
HistoricalDispatchSimulationResult result = run(task(
|
||||
DispatchTask.TaskType.OUTBOUND, "BIN", "EXIT", "MID", null));
|
||||
SimulationTaskResult task = result.getTaskResults().get(0);
|
||||
|
||||
assertEquals(Arrays.asList(EMPTY_TO_PICKUP,
|
||||
LOADED_RETURN_TO_SOURCE_FRONT, LOADED_TO_PLACE), phases(task));
|
||||
assertEquals(Arrays.asList("BIN", "MID", "EXIT"), targets(task));
|
||||
assertEquals(task.getBusinessCompletedMillis(), task.getVehicleReleasedMillis());
|
||||
assertEquals(VehicleReleaseStatus.COMPLETED, task.getVehicleReleaseStatus());
|
||||
assertEquals(1, result.getArchiveCount());
|
||||
assertEquals(1, result.getWmsBusinessEffects());
|
||||
}
|
||||
|
||||
private HistoricalDispatchSimulationResult run(HistoricalSimulationTask task) {
|
||||
GridMap grid = grid();
|
||||
HistoricalSimulationInput input = new HistoricalSimulationInput(
|
||||
Collections.singletonList(task), Collections.emptyList(),
|
||||
grid, traffic(), Collections.singletonList(
|
||||
new VehicleSnapshot("1", 1, "V", 5, 1,
|
||||
100, true, true, false)),
|
||||
new FloorTransferTopology(Collections.<Integer, String>emptyMap()),
|
||||
SimulationExecutionMode.SOURCE_ALIGNED_BUSINESS,
|
||||
new HistoricalInputAudit(1, 1, 0, 1, 0),
|
||||
ChannelFrontTopology.from(Collections.emptyList()));
|
||||
return new HistoricalDispatchSimulator().simulate(input,
|
||||
new SimulationConfig(100, 30_000L, 3, 100, 100),
|
||||
DeterministicFaultPlan.none());
|
||||
}
|
||||
|
||||
private HistoricalSimulationTask task(DispatchTask.TaskType type,
|
||||
String source, String target,
|
||||
String sourceFront, String targetFront) {
|
||||
LocalDateTime created = LocalDateTime.of(2026, 8, 10, 8, 0);
|
||||
HistoricalQueueRecord record = new HistoricalQueueRecord(
|
||||
"GUID", "BUSINESS-1", type.name(), 2,
|
||||
source, target, "HISTORY_ONLY", "2026-08-10 08:00:00",
|
||||
"2026-08-10 08:01:00", "HISTORY-VEHICLE");
|
||||
return new HistoricalSimulationTask("BUSINESS-1", record, type, 2,
|
||||
created, created.plusMinutes(1), source, target,
|
||||
Collections.singletonList("HISTORY_ONLY"), 0,
|
||||
sourceFront, targetFront,
|
||||
Collections.<HistoricalTaskSnapshotEvidence>emptyList());
|
||||
}
|
||||
|
||||
private GridMap grid() {
|
||||
GridMap grid = new GridMap();
|
||||
for (String id : Arrays.asList("V", "PICK", "MID", "BIN", "FRONT", "EXIT")) {
|
||||
grid.addSite(new Site(id, 1, id.hashCode(), 1, "TEST"));
|
||||
}
|
||||
grid.connect("V", "PICK");
|
||||
grid.connect("PICK", "MID");
|
||||
grid.connect("MID", "BIN");
|
||||
grid.connect("MID", "FRONT");
|
||||
grid.connect("FRONT", "EXIT");
|
||||
return grid;
|
||||
}
|
||||
|
||||
private EffectiveTrafficMap traffic() {
|
||||
LinkedHashSet<String> nodes = new LinkedHashSet<String>(
|
||||
Arrays.asList("V", "PICK", "MID", "BIN", "FRONT", "EXIT"));
|
||||
LinkedHashSet<TrafficMapCompiler.EdgeDefinition> edges =
|
||||
new LinkedHashSet<TrafficMapCompiler.EdgeDefinition>();
|
||||
edges.add(new TrafficMapCompiler.EdgeDefinition("V", "PICK"));
|
||||
edges.add(new TrafficMapCompiler.EdgeDefinition("PICK", "MID"));
|
||||
edges.add(new TrafficMapCompiler.EdgeDefinition("MID", "BIN"));
|
||||
edges.add(new TrafficMapCompiler.EdgeDefinition("MID", "FRONT"));
|
||||
edges.add(new TrafficMapCompiler.EdgeDefinition("FRONT", "EXIT"));
|
||||
return new TrafficMapCompiler().compile(new TrafficMapCompiler.MapDefinition(
|
||||
"TWO-STAGE", 1L, "TEST", true, "test", "test",
|
||||
nodes, edges, Collections.emptyMap(),
|
||||
Collections.emptySet(), Collections.emptySet()));
|
||||
}
|
||||
|
||||
private List<BusinessTaskPhase> phases(SimulationTaskResult task) {
|
||||
return task.getPhaseEvidence().stream().map(TaskPhaseEvidence::getPhase)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> targets(SimulationTaskResult task) {
|
||||
return task.getPhaseEvidence().stream().map(TaskPhaseEvidence::getToSite)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user