#
Junjie
9 天以前 dc3f9cc91759823ce59486f19b138be4b296a0f1
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<>();
    }
}