feat(ft546): run standard WCS as the single scheduler
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
package com.ruoyi.wcs.application;
|
||||
|
||||
import com.ruoyi.wcs.application.device.OutboxDispatchResult;
|
||||
import com.ruoyi.wcs.application.dispatch.DispatchCycleResult;
|
||||
import com.ruoyi.wcs.application.dispatch.DispatchSnapshot;
|
||||
import com.ruoyi.wcs.application.dispatch.GrantedDispatch;
|
||||
import com.ruoyi.wcs.application.device.SnapshotCollector;
|
||||
import com.ruoyi.wcs.application.recovery.RecoveryGate;
|
||||
import com.ruoyi.wcs.domain.topology.EffectiveTopology;
|
||||
import com.ruoyi.wcs.domain.topology.TopologyCompiler;
|
||||
import com.ruoyi.wcs.domain.topology.TopologyDefinition;
|
||||
import com.ruoyi.wcs.domain.topology.TopologyNode;
|
||||
import com.ruoyi.wcs.domain.traffic.ResourceLedger;
|
||||
import com.ruoyi.wcs.domain.traffic.RoutePlan;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class WcsRuntimeCoordinatorTest {
|
||||
|
||||
@Test
|
||||
void tickRunsSnapshotRecoveryDispatchCommandAndOutboxInOrder() {
|
||||
List<String> calls = new ArrayList<String>();
|
||||
SnapshotCollector snapshots = () -> calls.add("snapshot");
|
||||
RecoveryGate recovery = () -> {
|
||||
calls.add("recovery");
|
||||
return true;
|
||||
};
|
||||
GrantedDispatch grant = grant();
|
||||
DispatchCycleResult dispatchResult = DispatchCycleResult.committed(
|
||||
Collections.singletonList(grant),
|
||||
Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.emptyList());
|
||||
OutboxDispatchResult outboxResult = new OutboxDispatchResult();
|
||||
WcsRuntimeCoordinator coordinator = new WcsRuntimeCoordinator(
|
||||
snapshots, recovery,
|
||||
() -> {
|
||||
calls.add("load");
|
||||
return snapshot();
|
||||
},
|
||||
value -> {
|
||||
calls.add("dispatch");
|
||||
return dispatchResult;
|
||||
},
|
||||
value -> calls.add("command:" + value.getStepId()),
|
||||
(limit, now) -> {
|
||||
calls.add("outbox:" + limit);
|
||||
return outboxResult;
|
||||
},
|
||||
() -> Instant.parse("2026-08-11T08:00:00Z"), 100);
|
||||
|
||||
RuntimeTickResult result = coordinator.tick();
|
||||
|
||||
assertEquals(Arrays.asList("snapshot", "recovery", "load",
|
||||
"dispatch", "command:STEP-1", "outbox:100"), calls);
|
||||
assertEquals(RuntimeTickResult.Status.COMPLETED, result.getStatus());
|
||||
assertEquals(1, result.getCreatedCommandCount());
|
||||
assertEquals(dispatchResult, result.getDispatchResult());
|
||||
assertEquals(outboxResult, result.getOutboxResult());
|
||||
}
|
||||
|
||||
@Test
|
||||
void recoveryGateStopsBeforeDispatchAndOutbox() {
|
||||
List<String> calls = new ArrayList<String>();
|
||||
WcsRuntimeCoordinator coordinator = new WcsRuntimeCoordinator(
|
||||
() -> calls.add("snapshot"),
|
||||
() -> {
|
||||
calls.add("recovery");
|
||||
return false;
|
||||
},
|
||||
() -> {
|
||||
calls.add("load");
|
||||
return snapshot();
|
||||
},
|
||||
value -> {
|
||||
calls.add("dispatch");
|
||||
return DispatchCycleResult.retrySnapshotChanged();
|
||||
},
|
||||
value -> calls.add("command"),
|
||||
(limit, now) -> {
|
||||
calls.add("outbox");
|
||||
return new OutboxDispatchResult();
|
||||
},
|
||||
Instant::now, 100);
|
||||
|
||||
RuntimeTickResult result = coordinator.tick();
|
||||
|
||||
assertEquals(Arrays.asList("snapshot", "recovery"), calls);
|
||||
assertEquals(RuntimeTickResult.Status.RECOVERY_BLOCKED,
|
||||
result.getStatus());
|
||||
assertEquals(0, result.getCreatedCommandCount());
|
||||
assertNull(result.getDispatchResult());
|
||||
assertNull(result.getOutboxResult());
|
||||
}
|
||||
|
||||
private static GrantedDispatch grant() {
|
||||
RoutePlan route = new RoutePlan(Arrays.asList("A", "B"),
|
||||
Arrays.asList("NODE:A", "EDGE:A:B", "NODE:B"),
|
||||
1, 0, 7L);
|
||||
return new GrantedDispatch("ORDER-1", "STEP-1", "VEH-1", route,
|
||||
Collections.singletonList("RES-1"), 7L, 11L);
|
||||
}
|
||||
|
||||
private static DispatchSnapshot snapshot() {
|
||||
EffectiveTopology topology = new TopologyCompiler().compile(
|
||||
new TopologyDefinition("FT546", 7L, true,
|
||||
Arrays.asList(
|
||||
new TopologyNode("A", "F1", 0, 0, 1,
|
||||
false, false),
|
||||
new TopologyNode("B", "F1", 1, 0, 1,
|
||||
false, false)),
|
||||
Collections.emptyList(), Collections.emptyList(),
|
||||
Collections.emptyList(), Collections.emptyList()));
|
||||
return new DispatchSnapshot(Collections.emptyList(),
|
||||
Collections.emptyList(), topology,
|
||||
new ResourceLedger(topology), 1L);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,26 @@ class CommandOutboxDispatcherTest {
|
||||
assertEquals(0, gateway.sendCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void explicitTaskNumberAndTypeReachTheDevicePayload() {
|
||||
Map<String, String> commandParameters =
|
||||
new LinkedHashMap<String, String>();
|
||||
commandParameters.put("taskNo", "30001");
|
||||
commandParameters.put("taskType", "1");
|
||||
RoutePlan route = new RoutePlan(Arrays.asList("A", "B"),
|
||||
Arrays.asList("NODE:A", "EDGE:A:B", "NODE:B"),
|
||||
1, 0, 7L);
|
||||
GrantedDispatch grant = new GrantedDispatch("ORDER-1", "STEP-1",
|
||||
"VEH-1", route, Collections.singletonList("RES-1"), 7L,
|
||||
11L, commandParameters);
|
||||
|
||||
DeviceCommand command = service.createCommand(grant);
|
||||
|
||||
assertEquals("orderId=ORDER-1|stepId=STEP-1|vehicleId=VEH-1"
|
||||
+ "|route=A>B|taskNo=30001|taskType=1",
|
||||
command.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
void acceptedSendIsNotMarkedCompleted() {
|
||||
service.createCommand(grantedDispatch());
|
||||
|
||||
@@ -83,6 +83,24 @@ class FeedbackReconcilerTest {
|
||||
recovery.reasons);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unorderedSnapshotWithoutBootSessionIsDiagnosticOnly() {
|
||||
DeviceSnapshot snapshot = DeviceSnapshot.unorderedWithoutBootSession(
|
||||
"SNAP-LEGACY-1", "VEH-1", "CMD-KEY-1", "TASK-1",
|
||||
at(5), true, 7L, 11L, "B", Arrays.asList("B", "C"),
|
||||
false, Collections.<String, String>emptyMap());
|
||||
|
||||
ReconcileResult result = reconciler.reconcile(snapshot);
|
||||
|
||||
assertEquals(ReconcileStatus.CONSISTENT, result.getStatus());
|
||||
assertEquals("LEGACY_UNORDERED_DIAGNOSTIC_ONLY",
|
||||
result.getRecoveryReason());
|
||||
assertTrue(recovery.reasons.isEmpty());
|
||||
assertEquals(0, reservations.releaseCallCount);
|
||||
assertEquals(-1L, executions.findByCommandKey("CMD-KEY-1")
|
||||
.getLastDeviceSequence());
|
||||
}
|
||||
|
||||
@Test
|
||||
void releasesOnlyResourcesStrictlyPassedByMatchingProgress() {
|
||||
DeviceSnapshot snapshot = snapshot("TASK-1", "B",
|
||||
|
||||
@@ -25,6 +25,8 @@ import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@@ -77,6 +79,27 @@ class DispatchCoordinatorTest {
|
||||
assertEquals(1, repository.commitCount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void carriesExplicitDeviceCommandParametersIntoTheGrant() {
|
||||
EffectiveTopology topology = topology();
|
||||
Map<String, String> commandParameters =
|
||||
new LinkedHashMap<String, String>();
|
||||
commandParameters.put("taskNo", "30001");
|
||||
commandParameters.put("taskType", "1");
|
||||
DispatchTask task = new DispatchTask("O1", "S1", "A", "X", 1,
|
||||
60L, 30, Collections.<DeviceCapability>emptySet(),
|
||||
commandParameters);
|
||||
|
||||
DispatchCycleResult result = coordinator(
|
||||
new RecordingTrafficRepository(true)).dispatch(
|
||||
new DispatchSnapshot(Collections.singletonList(task),
|
||||
Collections.singletonList(vehicle("V1", "A")),
|
||||
topology, new ResourceLedger(topology), 11L));
|
||||
|
||||
assertEquals(commandParameters,
|
||||
result.getGranted().get(0).getCommandParameters());
|
||||
}
|
||||
|
||||
private static DispatchCoordinator coordinator(TrafficStateRepository repository) {
|
||||
AStarRoutePlanner routePlanner = new AStarRoutePlanner();
|
||||
AtomicInteger sequence = new AtomicInteger();
|
||||
|
||||
Reference in New Issue
Block a user