From dc3f9cc91759823ce59486f19b138be4b296a0f1 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 28 四月 2026 09:43:28 +0800
Subject: [PATCH] #

---
 src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java |  182 +++++++++++++++++++++++++++++++++++++++------
 1 files changed, 157 insertions(+), 25 deletions(-)

diff --git a/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java b/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java
index 3a1450c..f6073dc 100644
--- a/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java
+++ b/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java
@@ -26,6 +26,7 @@
 import com.zy.core.thread.StationThread;
 import com.zy.core.utils.station.model.CircleTargetCandidate;
 import com.zy.core.utils.station.model.OutOrderDispatchDecision;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -37,8 +38,14 @@
 import java.util.Map;
 import java.util.Objects;
 
+@Slf4j
 @Component
 public class StationOutboundDecisionSupport {
+
+    private static final long PATH_CALC_SLOW_THRESHOLD_MS = 50L;
+    private static final long OUT_ORDER_DECISION_SLOW_THRESHOLD_MS = 100L;
+    private static final long CURRENT_OUT_ORDER_DECISION_SLOW_THRESHOLD_MS = 100L;
+    private static final long CIRCLE_TARGET_EVAL_SLOW_THRESHOLD_MS = 100L;
 
     @Autowired
     private WrkMastService wrkMastService;
@@ -113,31 +120,50 @@
                                                                     WrkMast wrkMast,
                                                                     List<Integer> outOrderStationIds,
                                                                     Double pathLenFactor) {
+        long startMs = System.currentTimeMillis();
         if (wrkMast == null || wrkMast.getStaNo() == null) {
             return null;
         }
+        DecisionPathCache decisionPathCache = new DecisionPathCache();
         if (!shouldApplyOutOrder(wrkMast, outOrderStationIds)) {
             return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
         }
+        long resolveDispatchTargetStartMs = System.currentTimeMillis();
         Integer dispatchStationId = resolveDispatchOutOrderTarget(
                 wrkMast,
                 wrkMast.getSourceStaNo(),
                 wrkMast.getStaNo(),
                 outOrderStationIds,
-                pathLenFactor
+                pathLenFactor,
+                decisionPathCache
         );
+        long resolveDispatchTargetCostMs = System.currentTimeMillis() - resolveDispatchTargetStartMs;
         if (dispatchStationId == null) {
             return null;
         }
-        if (isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor)) {
-            return resolveCurrentOutOrderDispatchDecision(currentStationId, wrkMast, outOrderStationIds, pathLenFactor);
-        }
-        if (!Objects.equals(dispatchStationId, wrkMast.getStaNo())
+        long currentDispatchCheckStartMs = System.currentTimeMillis();
+        boolean currentOutOrderDispatchStation = isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor, decisionPathCache);
+        long currentDispatchCheckCostMs = System.currentTimeMillis() - currentDispatchCheckStartMs;
+        OutOrderDispatchDecision decision;
+        if (currentOutOrderDispatchStation) {
+            decision = resolveCurrentOutOrderDispatchDecision(currentStationId, wrkMast, outOrderStationIds, pathLenFactor, decisionPathCache);
+        } else if (!Objects.equals(dispatchStationId, wrkMast.getStaNo())
                 && isCurrentOutOrderStation(currentStationId, outOrderStationIds)
                 && isWatchingCircleArrival(wrkMast.getWrkNo(), currentStationId)) {
-            return OutOrderDispatchDecision.circle(dispatchStationId, null, false);
+            decision = OutOrderDispatchDecision.circle(dispatchStationId, null, false);
+        } else {
+            decision = OutOrderDispatchDecision.direct(dispatchStationId);
         }
-        return OutOrderDispatchDecision.direct(dispatchStationId);
+        long totalCostMs = System.currentTimeMillis() - startMs;
+        log.info("resolveOutboundDispatchDecision profile, taskNo={}, currentStationId={}, finalTargetStationId={}, dispatchStationId={}, currentOutOrderDispatchStation={}, decision={}, circle={}, resolveDispatchTargetCostMs={}ms, currentDispatchCheckCostMs={}ms, totalCostMs={}ms",
+                wrkMast.getWrkNo(), currentStationId, wrkMast.getStaNo(), dispatchStationId, currentOutOrderDispatchStation,
+                decision == null ? null : decision.getTargetStationId(), decision != null && decision.isCircle(),
+                resolveDispatchTargetCostMs, currentDispatchCheckCostMs, totalCostMs);
+        if (totalCostMs > OUT_ORDER_DECISION_SLOW_THRESHOLD_MS) {
+            log.warn("resolveOutboundDispatchDecision slow, taskNo={}, currentStationId={}, totalCostMs={}ms, resolveDispatchTargetCostMs={}ms, currentDispatchCheckCostMs={}ms",
+                    wrkMast.getWrkNo(), currentStationId, totalCostMs, resolveDispatchTargetCostMs, currentDispatchCheckCostMs);
+        }
+        return decision;
     }
 
     public void syncOutOrderWatchState(WrkMast wrkMast,
@@ -197,12 +223,48 @@
                                                         Integer sourceStationId,
                                                         Integer targetStationId,
                                                         Double pathLenFactor) {
+        return calcOutboundNavigatePath(wrkMast, sourceStationId, targetStationId, pathLenFactor, null);
+    }
+
+    private List<NavigateNode> calcOutboundNavigatePath(WrkMast wrkMast,
+                                                        Integer sourceStationId,
+                                                        Integer targetStationId,
+                                                        Double pathLenFactor,
+                                                        DecisionPathCache decisionPathCache) {
+        long startMs = System.currentTimeMillis();
         Double normalizedFactor = normalizePathLenFactor(pathLenFactor);
         Integer currentTaskNo = wrkMast == null ? null : wrkMast.getWrkNo();
-        if (currentTaskNo == null) {
-            return navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, null, normalizedFactor);
+        boolean cacheHit = false;
+        List<NavigateNode> path;
+        if (decisionPathCache == null) {
+            path = currentTaskNo == null
+                    ? navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, null, normalizedFactor)
+                    : navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, currentTaskNo, normalizedFactor);
+        } else {
+            String cacheKey = buildPathCacheKey(currentTaskNo, sourceStationId, targetStationId, normalizedFactor);
+            List<NavigateNode> cachedPath = decisionPathCache.pathMap.get(cacheKey);
+            if (cachedPath != null) {
+                cacheHit = true;
+                path = cachedPath;
+            } else {
+                path = currentTaskNo == null
+                        ? navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, null, normalizedFactor)
+                        : navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, currentTaskNo, normalizedFactor);
+                if (path == null) {
+                    path = Collections.emptyList();
+                }
+                decisionPathCache.pathMap.put(cacheKey, path);
+            }
         }
-        return navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, currentTaskNo, normalizedFactor);
+        if (path == null) {
+            path = Collections.emptyList();
+        }
+        long costMs = System.currentTimeMillis() - startMs;
+        if (costMs > PATH_CALC_SLOW_THRESHOLD_MS) {
+            log.warn("calcOutboundNavigatePath slow, taskNo={}, sourceStationId={}, targetStationId={}, pathCacheHit={}, pathNodeCount={}, pathCostMs={}ms",
+                    currentTaskNo, sourceStationId, targetStationId, cacheHit, path.size(), costMs);
+        }
+        return path;
     }
 
     private boolean isBatchOutboundTaskWithSeq(WrkMast wrkMast) {
@@ -246,7 +308,8 @@
     private boolean isCurrentOutOrderDispatchStation(Integer currentStationId,
                                                      WrkMast wrkMast,
                                                      List<Integer> outOrderStationIds,
-                                                     Double pathLenFactor) {
+                                                     Double pathLenFactor,
+                                                     DecisionPathCache decisionPathCache) {
         if (!shouldApplyOutOrder(wrkMast, outOrderStationIds) || currentStationId == null) {
             return false;
         }
@@ -255,7 +318,8 @@
                 wrkMast.getSourceStaNo(),
                 wrkMast.getStaNo(),
                 outOrderStationIds,
-                pathLenFactor
+                pathLenFactor,
+                decisionPathCache
         );
         return dispatchStationId != null
                 && !Objects.equals(dispatchStationId, wrkMast.getStaNo())
@@ -272,11 +336,14 @@
     private OutOrderDispatchDecision resolveCurrentOutOrderDispatchDecision(Integer currentStationId,
                                                                            WrkMast wrkMast,
                                                                            List<Integer> outOrderStationIds,
-                                                                           Double pathLenFactor) {
-        if (!isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor)) {
+                                                                           Double pathLenFactor,
+                                                                           DecisionPathCache decisionPathCache) {
+        long startMs = System.currentTimeMillis();
+        if (!isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor, decisionPathCache)) {
             return null;
         }
 
+        long batchQueryStartMs = System.currentTimeMillis();
         List<WrkMast> batchWrkList = wrkMastService.list(new QueryWrapper<WrkMast>()
                 .eq("io_type", WrkIoType.OUT.id)
                 .notIn("wrk_sts",
@@ -286,6 +353,7 @@
                 .eq("batch", wrkMast.getBatch())
                 .orderByAsc("batch_seq")
                 .orderByAsc("wrk_no"));
+        long batchQueryCostMs = System.currentTimeMillis() - batchQueryStartMs;
         if (batchWrkList.isEmpty()) {
             return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
         }
@@ -298,33 +366,53 @@
         }
 
         List<NavigateNode> initPath;
+        long initPathStartMs = System.currentTimeMillis();
         try {
-            initPath = calcOutboundNavigatePath(wrkMast, wrkMast.getSourceStaNo(), wrkMast.getStaNo(), pathLenFactor);
+            initPath = calcOutboundNavigatePath(wrkMast, wrkMast.getSourceStaNo(), wrkMast.getStaNo(), pathLenFactor, decisionPathCache);
         } catch (Exception e) {
             News.taskInfo(wrkMast.getWrkNo(), "鎵规:{} 璁$畻鎺掑簭璺緞澶辫触锛屽綋鍓嶇珯鐐�={}", wrkMast.getBatch(), currentStationId);
             return null;
         }
+        long initPathCostMs = System.currentTimeMillis() - initPathStartMs;
 
+        long batchSeqScanStartMs = System.currentTimeMillis();
         Integer seq = getOutStationBatchSeq(initPath, currentStationId, wrkMast.getBatch());
+        long batchSeqScanCostMs = System.currentTimeMillis() - batchSeqScanStartMs;
         boolean toTarget = seq == null
                 ? currentBatchSeq.equals(wrkMast.getBatchSeq())
                 : Integer.valueOf(seq + 1).equals(wrkMast.getBatchSeq());
         if (toTarget) {
-            if (hasReachableOutReleaseSlot(wrkMast, currentStationId, wrkMast.getStaNo(), pathLenFactor)) {
+            long releaseSlotCheckStartMs = System.currentTimeMillis();
+            boolean hasReachableReleaseSlot = hasReachableOutReleaseSlot(wrkMast, currentStationId, wrkMast.getStaNo(), pathLenFactor, decisionPathCache);
+            long releaseSlotCheckCostMs = System.currentTimeMillis() - releaseSlotCheckStartMs;
+            if (hasReachableReleaseSlot) {
                 return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
             }
+            long loopEvalStartMs = System.currentTimeMillis();
             StationTaskLoopService.LoopEvaluation loopEvaluation = evaluateOutOrderLoop(
                     wrkMast.getWrkNo(),
                     currentStationId,
                     outOrderStationIds
             );
+            long loopEvalCostMs = System.currentTimeMillis() - loopEvalStartMs;
+            long circleTargetEvalStartMs = System.currentTimeMillis();
             Integer circleTarget = resolveNextCircleOrderTarget(
                     wrkMast,
                     currentStationId,
                     outOrderStationIds,
                     loopEvaluation.getExpectedLoopIssueCount(),
-                    pathLenFactor
+                    pathLenFactor,
+                    decisionPathCache
             );
+            long circleTargetEvalCostMs = System.currentTimeMillis() - circleTargetEvalStartMs;
+            long totalCostMs = System.currentTimeMillis() - startMs;
+            log.info("resolveCurrentOutOrderDispatchDecision profile, taskNo={}, currentStationId={}, batch={}, currentBatchSeq={}, taskBatchSeq={}, toTarget={}, batchQueryCostMs={}ms, initPathCostMs={}ms, batchSeqScanCostMs={}ms, releaseSlotCheckCostMs={}ms, loopEvalCostMs={}ms, circleTargetEvalCostMs={}ms, totalCostMs={}ms, circleTarget={}",
+                    wrkMast.getWrkNo(), currentStationId, wrkMast.getBatch(), currentBatchSeq, wrkMast.getBatchSeq(), toTarget,
+                    batchQueryCostMs, initPathCostMs, batchSeqScanCostMs, releaseSlotCheckCostMs, loopEvalCostMs, circleTargetEvalCostMs, totalCostMs, circleTarget);
+            if (totalCostMs > CURRENT_OUT_ORDER_DECISION_SLOW_THRESHOLD_MS) {
+                log.warn("resolveCurrentOutOrderDispatchDecision slow, taskNo={}, currentStationId={}, totalCostMs={}ms, batchQueryCostMs={}ms, initPathCostMs={}ms, batchSeqScanCostMs={}ms, releaseSlotCheckCostMs={}ms, circleTargetEvalCostMs={}ms",
+                        wrkMast.getWrkNo(), currentStationId, totalCostMs, batchQueryCostMs, initPathCostMs, batchSeqScanCostMs, releaseSlotCheckCostMs, circleTargetEvalCostMs);
+            }
             if (circleTarget == null) {
                 News.taskInfo(wrkMast.getWrkNo(), "鐩爣绔欏綋鍓嶄笉鍙繘锛屼笖鏈壘鍒板彲鎵ц鐨勪笅涓�鎺掑簭妫�娴嬬偣锛屽綋鍓嶇珯鐐�={}", currentStationId);
                 return null;
@@ -332,18 +420,31 @@
             return OutOrderDispatchDecision.circle(circleTarget, loopEvaluation, true);
         }
 
+        long loopEvalStartMs = System.currentTimeMillis();
         StationTaskLoopService.LoopEvaluation loopEvaluation = evaluateOutOrderLoop(
                 wrkMast.getWrkNo(),
                 currentStationId,
                 outOrderStationIds
         );
+        long loopEvalCostMs = System.currentTimeMillis() - loopEvalStartMs;
+        long circleTargetEvalStartMs = System.currentTimeMillis();
         Integer circleTarget = resolveNextCircleOrderTarget(
                 wrkMast,
                 currentStationId,
                 outOrderStationIds,
                 loopEvaluation.getExpectedLoopIssueCount(),
-                pathLenFactor
+                pathLenFactor,
+                decisionPathCache
         );
+        long circleTargetEvalCostMs = System.currentTimeMillis() - circleTargetEvalStartMs;
+        long totalCostMs = System.currentTimeMillis() - startMs;
+        log.info("resolveCurrentOutOrderDispatchDecision profile, taskNo={}, currentStationId={}, batch={}, currentBatchSeq={}, taskBatchSeq={}, toTarget={}, batchQueryCostMs={}ms, initPathCostMs={}ms, batchSeqScanCostMs={}ms, loopEvalCostMs={}ms, circleTargetEvalCostMs={}ms, totalCostMs={}ms, circleTarget={}",
+                wrkMast.getWrkNo(), currentStationId, wrkMast.getBatch(), currentBatchSeq, wrkMast.getBatchSeq(), toTarget,
+                batchQueryCostMs, initPathCostMs, batchSeqScanCostMs, loopEvalCostMs, circleTargetEvalCostMs, totalCostMs, circleTarget);
+        if (totalCostMs > CURRENT_OUT_ORDER_DECISION_SLOW_THRESHOLD_MS) {
+            log.warn("resolveCurrentOutOrderDispatchDecision slow, taskNo={}, currentStationId={}, totalCostMs={}ms, batchQueryCostMs={}ms, initPathCostMs={}ms, batchSeqScanCostMs={}ms, circleTargetEvalCostMs={}ms",
+                    wrkMast.getWrkNo(), currentStationId, totalCostMs, batchQueryCostMs, initPathCostMs, batchSeqScanCostMs, circleTargetEvalCostMs);
+        }
         if (circleTarget == null) {
             News.taskInfo(wrkMast.getWrkNo(), "鏈壘鍒板彲鎵ц鐨勪笅涓�鎺掑簭妫�娴嬬偣锛屽綋鍓嶇珯鐐�={}", currentStationId);
             return null;
@@ -377,7 +478,8 @@
                                                   Integer sourceStationId,
                                                   Integer finalTargetStationId,
                                                   List<Integer> outOrderList,
-                                                  Double pathLenFactor) {
+                                                  Double pathLenFactor,
+                                                  DecisionPathCache decisionPathCache) {
         if (finalTargetStationId == null) {
             return null;
         }
@@ -386,7 +488,7 @@
         }
 
         try {
-            List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, sourceStationId, finalTargetStationId, pathLenFactor);
+            List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, sourceStationId, finalTargetStationId, pathLenFactor, decisionPathCache);
             for (int i = nodes.size() - 1; i >= 0; i--) {
                 Integer stationId = getStationIdFromNode(nodes.get(i));
                 if (stationId == null) {
@@ -407,13 +509,14 @@
     private boolean hasReachableOutReleaseSlot(WrkMast wrkMast,
                                                Integer currentStationId,
                                                Integer finalTargetStationId,
-                                               Double pathLenFactor) {
+                                               Double pathLenFactor,
+                                               DecisionPathCache decisionPathCache) {
         if (currentStationId == null || finalTargetStationId == null) {
             return true;
         }
 
         try {
-            List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, currentStationId, finalTargetStationId, pathLenFactor);
+            List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, currentStationId, finalTargetStationId, pathLenFactor, decisionPathCache);
             if (nodes == null || nodes.isEmpty()) {
                 return true;
             }
@@ -462,7 +565,9 @@
                                                  Integer currentStationId,
                                                  List<Integer> orderedOutStationList,
                                                  Integer expectedLoopIssueCount,
-                                                 Double pathLenFactor) {
+                                                 Double pathLenFactor,
+                                                 DecisionPathCache decisionPathCache) {
+        long startMs = System.currentTimeMillis();
         if (currentStationId == null || orderedOutStationList == null || orderedOutStationList.size() <= 1) {
             return null;
         }
@@ -470,6 +575,8 @@
         int startIndex = orderedOutStationList.indexOf(currentStationId);
         int total = orderedOutStationList.size();
         List<CircleTargetCandidate> candidateList = new ArrayList<>();
+        int minPathLen = Integer.MAX_VALUE;
+        int maxPathLen = 0;
         for (int offset = 1; offset < total; offset++) {
             int candidateIndex = (startIndex + offset + total) % total;
             Integer candidateStationId = orderedOutStationList.get(candidateIndex);
@@ -477,9 +584,11 @@
                 continue;
             }
             try {
-                List<NavigateNode> path = calcOutboundNavigatePath(wrkMast, currentStationId, candidateStationId, pathLenFactor);
+                List<NavigateNode> path = calcOutboundNavigatePath(wrkMast, currentStationId, candidateStationId, pathLenFactor, decisionPathCache);
                 if (path != null && !path.isEmpty()) {
                     candidateList.add(new CircleTargetCandidate(candidateStationId, path.size(), offset));
+                    minPathLen = Math.min(minPathLen, path.size());
+                    maxPathLen = Math.max(maxPathLen, path.size());
                 }
             } catch (Exception ignore) {
             }
@@ -506,7 +615,16 @@
                 return Integer.compare(left.getOffset(), right.getOffset());
             }
         });
-        return resolveGradualCircleTargetByPathLength(expectedLoopIssueCount, candidateList, pathLenFactor);
+        Integer circleTarget = resolveGradualCircleTargetByPathLength(expectedLoopIssueCount, candidateList, pathLenFactor);
+        long totalCostMs = System.currentTimeMillis() - startMs;
+        log.info("resolveNextCircleOrderTarget profile, taskNo={}, currentStationId={}, candidateCount={}, successfulCandidateCount={}, selectedTargetStationId={}, minPathLen={}, maxPathLen={}, totalCostMs={}ms",
+                wrkMast == null ? null : wrkMast.getWrkNo(), currentStationId, Math.max(total - 1, 0), candidateList.size(), circleTarget,
+                minPathLen == Integer.MAX_VALUE ? 0 : minPathLen, maxPathLen, totalCostMs);
+        if (totalCostMs > CIRCLE_TARGET_EVAL_SLOW_THRESHOLD_MS) {
+            log.warn("resolveNextCircleOrderTarget slow, taskNo={}, currentStationId={}, successfulCandidateCount={}, selectedTargetStationId={}, totalCostMs={}ms",
+                    wrkMast == null ? null : wrkMast.getWrkNo(), currentStationId, candidateList.size(), circleTarget, totalCostMs);
+        }
+        return circleTarget;
     }
 
     private Integer resolveGradualCircleTargetByPathLength(Integer expectedLoopIssueCount,
@@ -609,7 +727,21 @@
         return pathLenFactor;
     }
 
+    private String buildPathCacheKey(Integer currentTaskNo,
+                                     Integer sourceStationId,
+                                     Integer targetStationId,
+                                     Double normalizedFactor) {
+        return String.valueOf(currentTaskNo) + "->"
+                + String.valueOf(sourceStationId) + "->"
+                + String.valueOf(targetStationId) + "@"
+                + String.format(java.util.Locale.ROOT, "%.4f", normalizedFactor == null ? 0.0d : normalizedFactor);
+    }
+
     private boolean isBlank(String value) {
         return value == null || value.trim().isEmpty();
     }
+
+    private static class DecisionPathCache {
+        private final Map<String, List<NavigateNode>> pathMap = new HashMap<>();
+    }
 }

--
Gitblit v1.9.1