package com.zy.asrs.utils;
|
|
import com.zy.asrs.entity.BasDevpPosition;
|
import com.zy.asrs.entity.WrkMast;
|
|
import java.util.*;
|
|
/**
|
* 任务下发优先级工具:
|
* 基于时间、距离、区域计算最终优先级,并返回排序后的任务分配建议。
|
*/
|
public final class TaskDispatchPriorityUtil {
|
|
private TaskDispatchPriorityUtil() {
|
}
|
|
public static List<DispatchPlan> rankDispatchPlans(List<WrkMast> taskPool,
|
Map<Integer, BasDevpPosition> siteMap,
|
List<RgvSnapshot> rgvSnapshots,
|
Map<Integer, Double> regionPriorityMap,
|
Date now,
|
long distanceThreshold,
|
long perimeter,
|
double timeWeight,
|
double distanceWeight,
|
double regionWeight) {
|
if (taskPool == null || taskPool.isEmpty() || siteMap == null || rgvSnapshots == null || rgvSnapshots.isEmpty()) {
|
return Collections.emptyList();
|
}
|
|
Date calcNow = now == null ? new Date() : now;
|
long maxWaitMs = 0L;
|
for (WrkMast task : taskPool) {
|
Date createTime = getTaskCreateTime(task);
|
if (task == null || createTime == null) {
|
continue;
|
}
|
long waitMs = Math.max(0L, calcNow.getTime() - createTime.getTime());
|
if (waitMs > maxWaitMs) {
|
maxWaitMs = waitMs;
|
}
|
}
|
|
Set<Integer> usedSource = new HashSet<>();
|
List<DispatchPlan> plans = new ArrayList<>();
|
for (WrkMast task : taskPool) {
|
if (task == null || task.getSourceStaNo() == null || task.getStaNo() == null) {
|
continue;
|
}
|
if (usedSource.contains(task.getSourceStaNo())) {
|
continue;
|
}
|
BasDevpPosition source = siteMap.get(task.getSourceStaNo());
|
if (source == null) {
|
continue;
|
}
|
|
RgvChoice choice = chooseBestRgv(source.getPlcPosition(), rgvSnapshots, distanceThreshold, perimeter);
|
if (choice == null) {
|
continue;
|
}
|
|
double timePriorityByWindow = TimePriorityUtil.calculate(getTaskCreateTime(task), calcNow, maxWaitMs);
|
double timePriorityByCurve = TimePriorityUtil.calculateDynamic(getTaskCreateTime(task), calcNow, 120D, 300D, 0.0083D);
|
double timePriority = Math.max(timePriorityByWindow, timePriorityByCurve);
|
double distancePriority = DistancePriorityUtil.calculate(choice.distance, distanceThreshold, perimeter);
|
double regionPriority = resolveRegionPriority(source.getDevRegion(), regionPriorityMap);
|
double finalPriority = PriorityWeightCalculatorUtil.calculate(
|
timePriority, distancePriority, regionPriority, timeWeight, distanceWeight, regionWeight
|
);
|
plans.add(new DispatchPlan(task, choice.rgvNo, choice.distance, DistancePriorityUtil.isInRange(choice.distance, distanceThreshold),
|
timePriority, distancePriority, regionPriority, finalPriority));
|
usedSource.add(task.getSourceStaNo());
|
}
|
|
plans.sort(
|
Comparator.comparingDouble(DispatchPlan::getFinalPriority).reversed()
|
.thenComparingDouble(DispatchPlan::getDistancePriority).reversed()
|
.thenComparingDouble(DispatchPlan::getTimePriority).reversed()
|
.thenComparingLong(DispatchPlan::getDistance)
|
.thenComparingLong(p -> p.task.getWrkNo() == null ? Long.MAX_VALUE : p.task.getWrkNo())
|
);
|
return plans;
|
}
|
|
private static RgvChoice chooseBestRgv(long sourcePos, List<RgvSnapshot> rgvSnapshots, long distanceThreshold, long perimeter) {
|
RgvChoice best = null;
|
for (RgvSnapshot snapshot : rgvSnapshots) {
|
if (snapshot == null || snapshot.rgvNo == null || snapshot.position == null) {
|
continue;
|
}
|
long distance = ringDistance(sourcePos, snapshot.position, perimeter);
|
double distancePriority = DistancePriorityUtil.calculate(distance, distanceThreshold, perimeter);
|
if (best == null || distancePriority > best.distancePriority ||
|
(distancePriority == best.distancePriority && distance < best.distance)) {
|
best = new RgvChoice(snapshot.rgvNo, distance, distancePriority);
|
}
|
}
|
return best;
|
}
|
|
private static long ringDistance(long sourcePos, long rgvPos, long perimeter) {
|
return SortTheExecutionOfTheCarUtil.LatelyAndLessThan(sourcePos, rgvPos, Math.max(1L, perimeter)).longValue();
|
}
|
|
private static Date getTaskCreateTime(WrkMast task) {
|
if (task == null) {
|
return null;
|
}
|
if (task.getAppeTime() != null) {
|
return task.getAppeTime();
|
}
|
return task.getModiTime();
|
}
|
|
private static double resolveRegionPriority(Integer region, Map<Integer, Double> regionPriorityMap) {
|
if (region == null || regionPriorityMap == null || regionPriorityMap.isEmpty()) {
|
return 0D;
|
}
|
Double score = regionPriorityMap.get(region);
|
if (score == null) {
|
return 0D;
|
}
|
if (score > 1D) {
|
return 1D;
|
}
|
if (score < -1D) {
|
return -1D;
|
}
|
return score;
|
}
|
|
public static class RgvSnapshot {
|
private final Integer rgvNo;
|
private final Long position;
|
|
public RgvSnapshot(Integer rgvNo, Long position) {
|
this.rgvNo = rgvNo;
|
this.position = position;
|
}
|
}
|
|
public static class DispatchPlan {
|
private final WrkMast task;
|
private final Integer rgvNo;
|
private final long distance;
|
private final boolean inRange;
|
private final double timePriority;
|
private final double distancePriority;
|
private final double regionPriority;
|
private final double finalPriority;
|
|
public DispatchPlan(WrkMast task, Integer rgvNo, long distance, boolean inRange,
|
double timePriority, double distancePriority, double regionPriority, double finalPriority) {
|
this.task = task;
|
this.rgvNo = rgvNo;
|
this.distance = distance;
|
this.inRange = inRange;
|
this.timePriority = timePriority;
|
this.distancePriority = distancePriority;
|
this.regionPriority = regionPriority;
|
this.finalPriority = finalPriority;
|
}
|
|
public WrkMast getTask() {
|
return task;
|
}
|
|
public Integer getRgvNo() {
|
return rgvNo;
|
}
|
|
public long getDistance() {
|
return distance;
|
}
|
|
public boolean isInRange() {
|
return inRange;
|
}
|
|
public double getTimePriority() {
|
return timePriority;
|
}
|
|
public double getDistancePriority() {
|
return distancePriority;
|
}
|
|
public double getRegionPriority() {
|
return regionPriority;
|
}
|
|
public double getFinalPriority() {
|
return finalPriority;
|
}
|
}
|
|
private static class RgvChoice {
|
private final Integer rgvNo;
|
private final long distance;
|
private final double distancePriority;
|
|
private RgvChoice(Integer rgvNo, long distance, double distancePriority) {
|
this.rgvNo = rgvNo;
|
this.distance = distance;
|
this.distancePriority = distancePriority;
|
}
|
}
|
}
|