feat: add deterministic vehicle assignment demo
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package com.ft546.wcs.demo.center;
|
||||
|
||||
import com.ft546.wcs.demo.model.DecisionCode;
|
||||
import com.ft546.wcs.demo.model.VehicleSnapshot;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public final class VehicleAssignmentDecision {
|
||||
private final DecisionCode code;
|
||||
private final VehicleSnapshot vehicle;
|
||||
private final String reason;
|
||||
|
||||
private VehicleAssignmentDecision(
|
||||
DecisionCode code,
|
||||
VehicleSnapshot vehicle,
|
||||
String reason
|
||||
) {
|
||||
this.code = code;
|
||||
this.vehicle = vehicle;
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public static VehicleAssignmentDecision assigned(
|
||||
VehicleSnapshot vehicle,
|
||||
String reason
|
||||
) {
|
||||
return new VehicleAssignmentDecision(DecisionCode.ASSIGNED, vehicle, reason);
|
||||
}
|
||||
|
||||
public static VehicleAssignmentDecision rejected(
|
||||
DecisionCode code,
|
||||
String reason
|
||||
) {
|
||||
if (code == DecisionCode.ASSIGNED) {
|
||||
throw new IllegalArgumentException("rejected decision cannot be ASSIGNED");
|
||||
}
|
||||
return new VehicleAssignmentDecision(code, null, reason);
|
||||
}
|
||||
|
||||
public DecisionCode getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public Optional<VehicleSnapshot> getVehicle() {
|
||||
return Optional.ofNullable(vehicle);
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package com.ft546.wcs.demo.center;
|
||||
|
||||
import com.ft546.wcs.demo.model.DecisionCode;
|
||||
import com.ft546.wcs.demo.model.DispatchTask;
|
||||
import com.ft546.wcs.demo.model.Site;
|
||||
import com.ft546.wcs.demo.model.VehicleSnapshot;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class VehicleAssignmentService {
|
||||
|
||||
public VehicleAssignmentDecision select(
|
||||
DispatchTask task,
|
||||
List<VehicleSnapshot> vehicles,
|
||||
Map<String, Site> sites
|
||||
) {
|
||||
if (task == null || vehicles == null || sites == null) {
|
||||
throw new IllegalArgumentException("task, vehicles and sites are required");
|
||||
}
|
||||
Site source = sites.get(task.getSourceSite());
|
||||
if (source == null) {
|
||||
return VehicleAssignmentDecision.rejected(
|
||||
DecisionCode.REJECT_INVALID_SITE,
|
||||
"unknown task source site: " + task.getSourceSite()
|
||||
);
|
||||
}
|
||||
|
||||
List<Candidate> candidates = new ArrayList<Candidate>();
|
||||
boolean eligibleWithUnknownSite = false;
|
||||
for (VehicleSnapshot vehicle : vehicles) {
|
||||
if (!isEligible(vehicle)) {
|
||||
continue;
|
||||
}
|
||||
Site current = sites.get(vehicle.getCurrentSite());
|
||||
if (current == null) {
|
||||
eligibleWithUnknownSite = true;
|
||||
continue;
|
||||
}
|
||||
candidates.add(new Candidate(vehicle, current, source));
|
||||
}
|
||||
|
||||
if (candidates.isEmpty()) {
|
||||
if (eligibleWithUnknownSite) {
|
||||
return VehicleAssignmentDecision.rejected(
|
||||
DecisionCode.REJECT_INVALID_SITE,
|
||||
"eligible vehicle references an unknown current site"
|
||||
);
|
||||
}
|
||||
return VehicleAssignmentDecision.rejected(
|
||||
DecisionCode.HOLD_NO_ELIGIBLE_VEHICLE,
|
||||
"no connected automatic idle vehicle with battery > 30 is available"
|
||||
);
|
||||
}
|
||||
|
||||
Collections.sort(candidates, Candidate.ORDER);
|
||||
Candidate selected = candidates.get(0);
|
||||
return VehicleAssignmentDecision.assigned(
|
||||
selected.vehicle,
|
||||
"vehicle=" + selected.vehicle.getVehicleId()
|
||||
+ ",sameFloorPenalty=" + selected.sameFloorPenalty
|
||||
+ ",floorDistance=" + selected.floorDistance
|
||||
+ ",yDistance=" + selected.yDistance
|
||||
+ ",xDistance=" + selected.xDistance
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isEligible(VehicleSnapshot vehicle) {
|
||||
return vehicle != null
|
||||
&& vehicle.isConnected()
|
||||
&& vehicle.getWorkMode() == 5
|
||||
&& vehicle.getStatus() == 1
|
||||
&& vehicle.isAllowSend()
|
||||
&& vehicle.getBattery() > 30;
|
||||
}
|
||||
|
||||
private static final class Candidate {
|
||||
private static final Comparator<Candidate> ORDER = new Comparator<Candidate>() {
|
||||
@Override
|
||||
public int compare(Candidate left, Candidate right) {
|
||||
int result = Integer.compare(
|
||||
left.sameFloorPenalty,
|
||||
right.sameFloorPenalty
|
||||
);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = Integer.compare(left.floorDistance, right.floorDistance);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = Integer.compare(left.yDistance, right.yDistance);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
result = Integer.compare(left.xDistance, right.xDistance);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return left.vehicle.getVehicleId().compareTo(right.vehicle.getVehicleId());
|
||||
}
|
||||
};
|
||||
|
||||
private final VehicleSnapshot vehicle;
|
||||
private final int sameFloorPenalty;
|
||||
private final int floorDistance;
|
||||
private final int yDistance;
|
||||
private final int xDistance;
|
||||
|
||||
private Candidate(VehicleSnapshot vehicle, Site current, Site source) {
|
||||
this.vehicle = vehicle;
|
||||
this.sameFloorPenalty = vehicle.getFloor() == source.getFloor() ? 0 : 1;
|
||||
this.floorDistance = Math.abs(vehicle.getFloor() - source.getFloor());
|
||||
this.yDistance = Math.abs(current.getY() - source.getY());
|
||||
this.xDistance = Math.abs(current.getX() - source.getX());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.ft546.wcs.demo.model;
|
||||
|
||||
public enum DecisionCode {
|
||||
ASSIGNED,
|
||||
PATH_READY,
|
||||
AVOIDANCE_CREATED,
|
||||
HOLD_NO_ELIGIBLE_VEHICLE,
|
||||
REJECT_INVALID_SITE,
|
||||
HOLD_NO_PATH,
|
||||
HOLD_CHANNEL_BLOCKED,
|
||||
HOLD_PROTOCOL_FAILURE,
|
||||
SENT,
|
||||
COMPLETED
|
||||
}
|
||||
Reference in New Issue
Block a user