| | |
| | | import com.zy.asrs.domain.path.StationPathProfileConfig; |
| | | import com.zy.asrs.domain.path.StationPathResolvedPolicy; |
| | | import com.zy.asrs.domain.path.StationPathRuleConfig; |
| | | import com.zy.asrs.domain.vo.StationTaskTraceVo; |
| | | import com.zy.asrs.domain.vo.StationCycleCapacityVo; |
| | | import com.zy.asrs.domain.vo.StationCycleLoopVo; |
| | | import com.zy.asrs.entity.BasDevp; |
| | |
| | | import com.zy.core.enums.SlaveType; |
| | | import com.zy.core.model.protocol.StationProtocol; |
| | | import com.zy.core.thread.StationThread; |
| | | import com.zy.core.trace.StationTaskTraceRegistry; |
| | | |
| | | @Component |
| | | public class NavigateUtils { |
| | | |
| | | private static final double CONGESTION_BUSY_BASE = 1.0d; |
| | | private static final double CONGESTION_ISSUED_RESERVE_BASE = 0.75d; |
| | | private static final double CONGESTION_PENDING_QUEUE_BASE = 0.45d; |
| | | private static final double CONGESTION_RUN_BLOCK_BASE = 1.5d; |
| | | private static final double WAIT_BUSY_SECONDS = 10.0d; |
| | | private static final double WAIT_ISSUED_RESERVE_SECONDS = 8.0d; |
| | | private static final double WAIT_PENDING_QUEUE_SECONDS = 5.0d; |
| | | private static final double WAIT_RUN_BLOCK_SECONDS = 30.0d; |
| | | private static final int DEADLOCK_PREFIX_LOOKAHEAD = 6; |
| | | |
| | | @Autowired |
| | | private BasStationService basStationService; |
| | |
| | | private StationPathPolicyService stationPathPolicyService; |
| | | @Autowired |
| | | private StationCycleCapacityService stationCycleCapacityService; |
| | | @Autowired |
| | | private StationTaskTraceRegistry stationTaskTraceRegistry; |
| | | |
| | | public synchronized List<NavigateNode> calcByStationId(Integer startStationId, Integer endStationId) { |
| | | return calcByStationId(startStationId, endStationId, null); |
| | | } |
| | | |
| | | public synchronized List<NavigateNode> calcByStationId(Integer startStationId, Integer endStationId, Integer currentTaskNo) { |
| | | BasStation startStation = basStationService.getById(startStationId); |
| | | if (startStation == null) { |
| | | throw new CoolException("未找到该 起点 对应的站点数据"); |
| | |
| | | |
| | | startTime = System.currentTimeMillis(); |
| | | News.info("[WCS Debug] 站点路径权重开始分析,startStationId={},endStationId={}", startStationId, endStationId); |
| | | List<NavigateNode> list = findStationBestPathTwoStage(allList, resolvedPolicy); |
| | | List<NavigateNode> list = findStationBestPathTwoStage(allList, resolvedPolicy, currentTaskNo); |
| | | News.info("[WCS Debug] 站点路径权重分析完成,耗时:{}ms", System.currentTimeMillis() - startTime); |
| | | |
| | | //去重 |
| | |
| | | return new StationPathResolvedPolicy(); |
| | | } |
| | | |
| | | private List<NavigateNode> findStationBestPathTwoStage(List<List<NavigateNode>> allList, StationPathResolvedPolicy resolvedPolicy) { |
| | | private List<NavigateNode> findStationBestPathTwoStage(List<List<NavigateNode>> allList, |
| | | StationPathResolvedPolicy resolvedPolicy, |
| | | Integer currentTaskNo) { |
| | | if (allList == null || allList.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | |
| | | |
| | | Map<Integer, StationProtocol> statusMap = loadStationStatusMap(); |
| | | Map<Integer, Double> stationLoopLoadMap = loadStationLoopLoadMap(); |
| | | StationTrafficSnapshot trafficSnapshot = loadStationTrafficSnapshot(statusMap, currentTaskNo); |
| | | Set<Integer> outStationIdSet = loadAllOutStationIdSet(); |
| | | List<PathCandidateMetrics> metricsList = new ArrayList<>(); |
| | | int skippedByOtherOutStation = 0; |
| | |
| | | if (path == null || path.isEmpty()) { |
| | | continue; |
| | | } |
| | | PathCandidateMetrics metrics = buildCandidateMetrics(path, statusMap, stationLoopLoadMap, profileConfig, ruleConfig, globalPolicy, outStationIdSet); |
| | | PathCandidateMetrics metrics = buildCandidateMetrics(path, statusMap, stationLoopLoadMap, trafficSnapshot, profileConfig, ruleConfig, globalPolicy, outStationIdSet); |
| | | if (globalPolicy.forceSkipPassOtherOutStation && metrics.passOtherOutStationCount > 0) { |
| | | skippedByOtherOutStation++; |
| | | continue; |
| | |
| | | private PathCandidateMetrics buildCandidateMetrics(List<NavigateNode> path, |
| | | Map<Integer, StationProtocol> statusMap, |
| | | Map<Integer, Double> stationLoopLoadMap, |
| | | StationTrafficSnapshot trafficSnapshot, |
| | | StationPathProfileConfig profileConfig, |
| | | StationPathRuleConfig ruleConfig, |
| | | PathGlobalPolicy globalPolicy, |
| | |
| | | metrics.liftTransferCount = countLiftTransferCount(path); |
| | | |
| | | List<Integer> stationIdList = extractStationIdList(path); |
| | | metrics.busyStationCount = countBusyStationCount(stationIdList, statusMap); |
| | | metrics.congestionScore = calcCongestionScore(stationIdList, trafficSnapshot); |
| | | metrics.queueDepthScore = calcQueueDepthScore(stationIdList, trafficSnapshot); |
| | | metrics.estimatedWaitSeconds = calcEstimatedWaitSeconds(stationIdList, trafficSnapshot); |
| | | metrics.deadlockRiskScore = calcDeadlockRiskScore(stationIdList, trafficSnapshot); |
| | | metrics.runBlockCount = countRunBlockCount(stationIdList, statusMap); |
| | | metrics.loopPenalty = calcLoopPenalty(stationIdList, stationLoopLoadMap); |
| | | metrics.passOtherOutStationCount = countPassOtherOutStations(path, outStationIdSet); |
| | |
| | | + softDeviationWeight * metrics.softDeviationCount; |
| | | |
| | | metrics.dynamicCost = |
| | | safeDouble(profileConfig.getS2BusyWeight(), 2.0d) * congWeightFactor * metrics.busyStationCount |
| | | safeDouble(profileConfig.getS2BusyWeight(), 2.0d) * congWeightFactor * metrics.congestionScore |
| | | + safeDouble(profileConfig.getS2QueueWeight(), 2.5d) * metrics.queueDepthScore |
| | | + safeDouble(profileConfig.getS2WaitWeight(), 1.5d) * (metrics.estimatedWaitSeconds / 60.0d) |
| | | + safeDouble(profileConfig.getS2DeadlockWeight(), 8.0d) * metrics.deadlockRiskScore |
| | | + safeDouble(profileConfig.getS2RunBlockWeight(), 10.0d) * metrics.runBlockCount |
| | | + safeDouble(profileConfig.getS2LoopLoadWeight(), 12.0d) * metrics.loopPenalty; |
| | | return metrics; |
| | |
| | | return count; |
| | | } |
| | | |
| | | private int countBusyStationCount(List<Integer> stationIdList, Map<Integer, StationProtocol> statusMap) { |
| | | int count = 0; |
| | | private double calcCongestionScore(List<Integer> stationIdList, StationTrafficSnapshot trafficSnapshot) { |
| | | if (trafficSnapshot == null || trafficSnapshot.congestionScoreMap.isEmpty()) { |
| | | return 0.0d; |
| | | } |
| | | double score = 0.0d; |
| | | for (Integer stationId : stationIdList) { |
| | | StationProtocol protocol = statusMap.get(stationId); |
| | | if (protocol != null && protocol.getTaskNo() != null && protocol.getTaskNo() > 0) { |
| | | count++; |
| | | score += trafficSnapshot.congestionScoreMap.getOrDefault(stationId, 0.0d); |
| | | } |
| | | return score; |
| | | } |
| | | |
| | | private double calcQueueDepthScore(List<Integer> stationIdList, StationTrafficSnapshot trafficSnapshot) { |
| | | if (trafficSnapshot == null || trafficSnapshot.queueDepthMap.isEmpty()) { |
| | | return 0.0d; |
| | | } |
| | | double score = 0.0d; |
| | | for (Integer stationId : stationIdList) { |
| | | score += trafficSnapshot.queueDepthMap.getOrDefault(stationId, 0); |
| | | } |
| | | return score; |
| | | } |
| | | |
| | | private double calcEstimatedWaitSeconds(List<Integer> stationIdList, StationTrafficSnapshot trafficSnapshot) { |
| | | if (trafficSnapshot == null || trafficSnapshot.estimatedWaitSecondsMap.isEmpty()) { |
| | | return 0.0d; |
| | | } |
| | | double score = 0.0d; |
| | | for (Integer stationId : stationIdList) { |
| | | score += trafficSnapshot.estimatedWaitSecondsMap.getOrDefault(stationId, 0.0d); |
| | | } |
| | | return score; |
| | | } |
| | | |
| | | private double calcDeadlockRiskScore(List<Integer> stationIdList, StationTrafficSnapshot trafficSnapshot) { |
| | | if (trafficSnapshot == null || trafficSnapshot.traceRouteList.isEmpty() || stationIdList == null || stationIdList.size() <= 1) { |
| | | return 0.0d; |
| | | } |
| | | List<Integer> candidateFutureStations = distinctPositiveStationIds(stationIdList.subList(1, stationIdList.size())); |
| | | if (candidateFutureStations.isEmpty()) { |
| | | return 0.0d; |
| | | } |
| | | |
| | | double totalRisk = 0.0d; |
| | | for (TraceRouteSnapshot routeSnapshot : trafficSnapshot.traceRouteList) { |
| | | if (routeSnapshot == null) { |
| | | continue; |
| | | } |
| | | OverlapMetrics pendingMetrics = calcOrderedOverlapMetrics(candidateFutureStations, routeSnapshot.pendingStationIds); |
| | | OverlapMetrics issuedMetrics = calcOrderedOverlapMetrics(candidateFutureStations, routeSnapshot.issuedStationIds); |
| | | |
| | | totalRisk += pendingMetrics.sequentialRisk * 0.9d; |
| | | totalRisk += issuedMetrics.sequentialRisk * 1.2d; |
| | | totalRisk += pendingMetrics.sharedCount * 0.12d; |
| | | totalRisk += issuedMetrics.sharedCount * 0.18d; |
| | | |
| | | int currentHitIndex = findFirstOverlapIndex(candidateFutureStations, routeSnapshot.currentStationId); |
| | | if (currentHitIndex >= 0) { |
| | | totalRisk += prefixRiskFactor(currentHitIndex) * 1.5d; |
| | | } |
| | | } |
| | | return count; |
| | | return totalRisk; |
| | | } |
| | | |
| | | private int countRunBlockCount(List<Integer> stationIdList, Map<Integer, StationProtocol> statusMap) { |
| | |
| | | return stationLoopLoadMap; |
| | | } |
| | | |
| | | private StationTrafficSnapshot loadStationTrafficSnapshot(Map<Integer, StationProtocol> statusMap, Integer currentTaskNo) { |
| | | StationTrafficSnapshot snapshot = new StationTrafficSnapshot(); |
| | | Map<Integer, Integer> busyMap = new HashMap<>(); |
| | | Map<Integer, Integer> issuedReserveMap = new HashMap<>(); |
| | | Map<Integer, Integer> pendingQueueMap = new HashMap<>(); |
| | | |
| | | if (statusMap != null && !statusMap.isEmpty()) { |
| | | for (Map.Entry<Integer, StationProtocol> entry : statusMap.entrySet()) { |
| | | Integer stationId = entry.getKey(); |
| | | StationProtocol protocol = entry.getValue(); |
| | | if (stationId == null || protocol == null) { |
| | | continue; |
| | | } |
| | | if (protocol.getTaskNo() != null && protocol.getTaskNo() > 0) { |
| | | busyMap.put(stationId, 1); |
| | | } |
| | | } |
| | | } |
| | | |
| | | for (StationTaskTraceVo traceVo : loadActiveTraceList(currentTaskNo)) { |
| | | if (traceVo == null) { |
| | | continue; |
| | | } |
| | | List<Integer> pendingStationIds = distinctPositiveStationIds(traceVo.getPendingStationIds()); |
| | | List<Integer> issuedStationIds = distinctPositiveStationIds(traceVo.getLatestIssuedSegmentPath()); |
| | | TraceRouteSnapshot routeSnapshot = new TraceRouteSnapshot(); |
| | | routeSnapshot.taskNo = traceVo.getTaskNo(); |
| | | routeSnapshot.currentStationId = traceVo.getCurrentStationId(); |
| | | routeSnapshot.pendingStationIds = pendingStationIds; |
| | | routeSnapshot.issuedStationIds = issuedStationIds; |
| | | snapshot.traceRouteList.add(routeSnapshot); |
| | | Set<Integer> pendingSet = new HashSet<>(pendingStationIds); |
| | | for (Integer stationId : issuedStationIds) { |
| | | if (stationId == null || !pendingSet.contains(stationId)) { |
| | | continue; |
| | | } |
| | | increaseIntMap(issuedReserveMap, stationId, 1); |
| | | } |
| | | Set<Integer> issuedSet = new HashSet<>(issuedStationIds); |
| | | for (Integer stationId : pendingStationIds) { |
| | | if (stationId == null || issuedSet.contains(stationId)) { |
| | | continue; |
| | | } |
| | | increaseIntMap(pendingQueueMap, stationId, 1); |
| | | } |
| | | } |
| | | |
| | | Set<Integer> stationIdSet = new HashSet<>(); |
| | | stationIdSet.addAll(busyMap.keySet()); |
| | | stationIdSet.addAll(issuedReserveMap.keySet()); |
| | | stationIdSet.addAll(pendingQueueMap.keySet()); |
| | | if (statusMap != null) { |
| | | stationIdSet.addAll(statusMap.keySet()); |
| | | } |
| | | |
| | | for (Integer stationId : stationIdSet) { |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | int busyCount = busyMap.getOrDefault(stationId, 0); |
| | | int issuedReserveCount = issuedReserveMap.getOrDefault(stationId, 0); |
| | | int pendingQueueCount = pendingQueueMap.getOrDefault(stationId, 0); |
| | | boolean runBlock = false; |
| | | if (statusMap != null) { |
| | | StationProtocol protocol = statusMap.get(stationId); |
| | | runBlock = protocol != null && protocol.isRunBlock(); |
| | | } |
| | | |
| | | int queueDepth = issuedReserveCount + pendingQueueCount; |
| | | double congestionScore = busyCount * CONGESTION_BUSY_BASE |
| | | + issuedReserveCount * CONGESTION_ISSUED_RESERVE_BASE |
| | | + pendingQueueCount * CONGESTION_PENDING_QUEUE_BASE |
| | | + (runBlock ? CONGESTION_RUN_BLOCK_BASE : 0.0d); |
| | | double estimatedWaitSeconds = busyCount * WAIT_BUSY_SECONDS |
| | | + issuedReserveCount * WAIT_ISSUED_RESERVE_SECONDS |
| | | + pendingQueueCount * WAIT_PENDING_QUEUE_SECONDS |
| | | + (runBlock ? WAIT_RUN_BLOCK_SECONDS : 0.0d); |
| | | |
| | | if (queueDepth > 0) { |
| | | snapshot.queueDepthMap.put(stationId, queueDepth); |
| | | } |
| | | if (congestionScore > 0.0d) { |
| | | snapshot.congestionScoreMap.put(stationId, congestionScore); |
| | | } |
| | | if (estimatedWaitSeconds > 0.0d) { |
| | | snapshot.estimatedWaitSecondsMap.put(stationId, estimatedWaitSeconds); |
| | | } |
| | | } |
| | | return snapshot; |
| | | } |
| | | |
| | | private List<StationTaskTraceVo> loadActiveTraceList(Integer currentTaskNo) { |
| | | if (stationTaskTraceRegistry == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<StationTaskTraceVo> traceList; |
| | | try { |
| | | traceList = stationTaskTraceRegistry.listLatestTraces(); |
| | | } catch (Exception ignore) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (traceList == null || traceList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | List<StationTaskTraceVo> result = new ArrayList<>(); |
| | | for (StationTaskTraceVo traceVo : traceList) { |
| | | if (!isPlanningActiveTrace(traceVo)) { |
| | | continue; |
| | | } |
| | | if (currentTaskNo != null && currentTaskNo.equals(traceVo.getTaskNo())) { |
| | | continue; |
| | | } |
| | | result.add(traceVo); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private boolean isPlanningActiveTrace(StationTaskTraceVo traceVo) { |
| | | if (traceVo == null) { |
| | | return false; |
| | | } |
| | | String status = traceVo.getStatus(); |
| | | return StationTaskTraceRegistry.STATUS_WAITING.equals(status) |
| | | || StationTaskTraceRegistry.STATUS_RUNNING.equals(status) |
| | | || StationTaskTraceRegistry.STATUS_REROUTED.equals(status); |
| | | } |
| | | |
| | | private List<Integer> distinctPositiveStationIds(List<Integer> stationIdList) { |
| | | if (stationIdList == null || stationIdList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<Integer> result = new ArrayList<>(); |
| | | Set<Integer> seen = new HashSet<>(); |
| | | for (Integer stationId : stationIdList) { |
| | | if (stationId == null || stationId <= 0) { |
| | | continue; |
| | | } |
| | | if (seen.add(stationId)) { |
| | | result.add(stationId); |
| | | } |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void increaseIntMap(Map<Integer, Integer> target, Integer stationId, int delta) { |
| | | if (target == null || stationId == null || delta == 0) { |
| | | return; |
| | | } |
| | | target.put(stationId, target.getOrDefault(stationId, 0) + delta); |
| | | } |
| | | |
| | | private OverlapMetrics calcOrderedOverlapMetrics(List<Integer> candidateStations, List<Integer> routeStations) { |
| | | OverlapMetrics metrics = new OverlapMetrics(); |
| | | if (candidateStations == null || candidateStations.isEmpty() || routeStations == null || routeStations.isEmpty()) { |
| | | return metrics; |
| | | } |
| | | |
| | | Map<Integer, Integer> routeIndexMap = new HashMap<>(); |
| | | for (int i = 0; i < routeStations.size(); i++) { |
| | | Integer stationId = routeStations.get(i); |
| | | if (stationId != null && !routeIndexMap.containsKey(stationId)) { |
| | | routeIndexMap.put(stationId, i); |
| | | } |
| | | } |
| | | if (routeIndexMap.isEmpty()) { |
| | | return metrics; |
| | | } |
| | | |
| | | for (int i = 0; i < candidateStations.size(); i++) { |
| | | Integer stationId = candidateStations.get(i); |
| | | if (stationId != null && routeIndexMap.containsKey(stationId)) { |
| | | metrics.sharedCount++; |
| | | } |
| | | } |
| | | |
| | | for (int candidateStart = 0; candidateStart < candidateStations.size(); candidateStart++) { |
| | | Integer firstStation = candidateStations.get(candidateStart); |
| | | Integer routeStart = routeIndexMap.get(firstStation); |
| | | if (routeStart == null) { |
| | | continue; |
| | | } |
| | | |
| | | int length = 1; |
| | | int prevRouteIndex = routeStart; |
| | | for (int j = candidateStart + 1; j < candidateStations.size(); j++) { |
| | | Integer nextRouteIndex = routeIndexMap.get(candidateStations.get(j)); |
| | | if (nextRouteIndex == null || nextRouteIndex.intValue() != prevRouteIndex + 1) { |
| | | break; |
| | | } |
| | | length++; |
| | | prevRouteIndex = nextRouteIndex; |
| | | } |
| | | |
| | | double risk = length * prefixRiskFactor(candidateStart); |
| | | if (risk > metrics.sequentialRisk) { |
| | | metrics.sequentialRisk = risk; |
| | | } |
| | | } |
| | | return metrics; |
| | | } |
| | | |
| | | private int findFirstOverlapIndex(List<Integer> stationIdList, Integer targetStationId) { |
| | | if (stationIdList == null || stationIdList.isEmpty() || targetStationId == null) { |
| | | return -1; |
| | | } |
| | | for (int i = 0; i < stationIdList.size(); i++) { |
| | | Integer stationId = stationIdList.get(i); |
| | | if (targetStationId.equals(stationId)) { |
| | | return i; |
| | | } |
| | | } |
| | | return -1; |
| | | } |
| | | |
| | | private double prefixRiskFactor(int candidateIndex) { |
| | | if (candidateIndex < 0) { |
| | | return 0.0d; |
| | | } |
| | | if (candidateIndex >= DEADLOCK_PREFIX_LOOKAHEAD) { |
| | | return 0.15d; |
| | | } |
| | | return (double) (DEADLOCK_PREFIX_LOOKAHEAD - candidateIndex) / (double) DEADLOCK_PREFIX_LOOKAHEAD; |
| | | } |
| | | |
| | | private int compareDouble(double left, double right, int thenLeft1, int thenRight1, int thenLeft2, int thenRight2) { |
| | | int result = Double.compare(left, right); |
| | | if (result != 0) { |
| | |
| | | private int turnCount; |
| | | private int liftTransferCount; |
| | | private int passOtherOutStationCount; |
| | | private int busyStationCount; |
| | | private double congestionScore; |
| | | private double queueDepthScore; |
| | | private double estimatedWaitSeconds; |
| | | private double deadlockRiskScore; |
| | | private int runBlockCount; |
| | | private int softDeviationCount; |
| | | private double loopPenalty; |
| | |
| | | private double dynamicCost; |
| | | } |
| | | |
| | | private static class StationTrafficSnapshot { |
| | | private final Map<Integer, Double> congestionScoreMap = new HashMap<>(); |
| | | private final Map<Integer, Integer> queueDepthMap = new HashMap<>(); |
| | | private final Map<Integer, Double> estimatedWaitSecondsMap = new HashMap<>(); |
| | | private final List<TraceRouteSnapshot> traceRouteList = new ArrayList<>(); |
| | | } |
| | | |
| | | private static class TraceRouteSnapshot { |
| | | private Integer taskNo; |
| | | private Integer currentStationId; |
| | | private List<Integer> pendingStationIds = Collections.emptyList(); |
| | | private List<Integer> issuedStationIds = Collections.emptyList(); |
| | | } |
| | | |
| | | private static class OverlapMetrics { |
| | | private int sharedCount; |
| | | private double sequentialRisk; |
| | | } |
| | | |
| | | private static class PathGlobalPolicy { |
| | | private double lenWeightFactor = 1.0d; |
| | | private double congWeightFactor = 1.0d; |