Junjie
2026-04-18 4117673c66366c57916064b7cfd5a4c0fa25bd57
#算法日志
4个文件已修改
254 ■■■■ 已修改文件
src/main/java/com/zy/core/move/StationMoveCoordinator.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/task/MainProcessAsyncTaskScheduler.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java 125 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/move/StationMoveCoordinator.java
@@ -26,6 +26,8 @@
public class StationMoveCoordinator {
    private static final int SESSION_EXPIRE_SECONDS = 60 * 60 * 24;
    private static final long TASK_DISPATCH_LOCK_SLOW_THRESHOLD_MS = 50L;
    private static final long RECORD_DISPATCH_SLOW_THRESHOLD_MS = 50L;
    private final Map<Integer, ReentrantLock> taskDispatchLocks = new ConcurrentHashMap<>();
    @Autowired
@@ -94,12 +96,18 @@
        if (taskNo == null || taskNo <= 0) {
            return supplier.get();
        }
        // 同一任务的切路和分段发送必须共享一把锁,避免旧 routeVersion 在线程晚到时继续把上一条段命令写出去。
        ReentrantLock lock = taskDispatchLocks.computeIfAbsent(taskNo, key -> new ReentrantLock());
        long waitStartMs = System.currentTimeMillis();
        lock.lock();
        long lockWaitMs = System.currentTimeMillis() - waitStartMs;
        long holdStartMs = System.currentTimeMillis();
        try {
            return supplier.get();
        } finally {
            long holdMs = System.currentTimeMillis() - holdStartMs;
            if (lockWaitMs > TASK_DISPATCH_LOCK_SLOW_THRESHOLD_MS || holdMs > TASK_DISPATCH_LOCK_SLOW_THRESHOLD_MS) {
                log.warn("taskDispatchLock slow, taskNo={}, lockWaitMs={}ms, lockHoldMs={}ms", taskNo, lockWaitMs, holdMs);
            }
            lock.unlock();
        }
    }
@@ -138,6 +146,7 @@
            return null;
        }
        long startMs = System.currentTimeMillis();
        StationMoveSession current = loadSession(taskNo);
        long now = System.currentTimeMillis();
        String pathSignature = buildPathSignature(command);
@@ -177,10 +186,16 @@
        command.setRouteVersion(session.getRouteVersion());
        saveSession(session);
        log.info("recordDispatch done, taskNo={}, routeVersion={}, reuse={}, prevRouteVersion={}, dispatchStationId={}, triggerName={}",
        long recordDispatchCostMs = System.currentTimeMillis() - startMs;
        log.info("recordDispatch done, taskNo={}, routeVersion={}, reuse={}, prevRouteVersion={}, dispatchStationId={}, triggerName={}, recordDispatchCostMs={}ms",
                taskNo, session.getRouteVersion(), reuseCurrent,
                current == null ? null : current.getRouteVersion(),
                dispatchStationId, triggerName);
                dispatchStationId, triggerName, recordDispatchCostMs);
        if (recordDispatchCostMs > RECORD_DISPATCH_SLOW_THRESHOLD_MS) {
            log.warn("recordDispatch slow, taskNo={}, dispatchStationId={}, triggerName={}, recordDispatchCostMs={}ms, pathSize={}",
                    taskNo, dispatchStationId, triggerName, recordDispatchCostMs,
                    fullPathStationIds == null ? 0 : fullPathStationIds.size());
        }
        if (circleRoute) {
            saveLegacyCircleCommand(taskNo, command);
src/main/java/com/zy/core/task/MainProcessAsyncTaskScheduler.java
@@ -46,6 +46,7 @@
                return false;
            }
            taskGuard.lastSubmitTimeMs = now;
            taskGuard.lastQueueEnterTimeMs = now;
        }
        try {
@@ -64,6 +65,7 @@
                             TaskGuard taskGuard,
                             Runnable task) {
        long startMs = System.currentTimeMillis();
        long queueWaitMs = taskGuard.lastQueueEnterTimeMs > 0L ? startMs - taskGuard.lastQueueEnterTimeMs : 0L;
        try {
            task.run();
        } catch (Exception e) {
@@ -71,7 +73,7 @@
        } finally {
            long costMs = System.currentTimeMillis() - startMs;
            if (slowLogThresholdMs > 0L && costMs > slowLogThresholdMs) {
                log.warn("MainProcess async task executed slowly, lane={}, task={}, cost={}ms", laneName, taskName, costMs);
                log.warn("MainProcess async task executed slowly, lane={}, task={}, cost={}ms, queueWaitMs={}ms", laneName, taskName, costMs, queueWaitMs);
            }
            taskGuard.running.set(false);
        }
@@ -137,6 +139,7 @@
        private final AtomicBoolean running = new AtomicBoolean(false);
        private volatile long lastSubmitTimeMs = 0L;
        private volatile long lastQueueEnterTimeMs = 0L;
    }
    private static class NamedThreadFactory implements ThreadFactory {
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,6 +120,7 @@
                                                                    WrkMast wrkMast,
                                                                    List<Integer> outOrderStationIds,
                                                                    Double pathLenFactor) {
        long startMs = System.currentTimeMillis();
        if (wrkMast == null || wrkMast.getStaNo() == null) {
            return null;
        }
@@ -120,6 +128,7 @@
        if (!shouldApplyOutOrder(wrkMast, outOrderStationIds)) {
            return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
        }
        long resolveDispatchTargetStartMs = System.currentTimeMillis();
        Integer dispatchStationId = resolveDispatchOutOrderTarget(
                wrkMast,
                wrkMast.getSourceStaNo(),
@@ -128,18 +137,33 @@
                pathLenFactor,
                decisionPathCache
        );
        long resolveDispatchTargetCostMs = System.currentTimeMillis() - resolveDispatchTargetStartMs;
        if (dispatchStationId == null) {
            return null;
        }
        if (isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor, decisionPathCache)) {
            return resolveCurrentOutOrderDispatchDecision(currentStationId, wrkMast, outOrderStationIds, pathLenFactor, decisionPathCache);
        }
        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,
@@ -207,26 +231,39 @@
                                                        Integer targetStationId,
                                                        Double pathLenFactor,
                                                        DecisionPathCache decisionPathCache) {
        long startMs = System.currentTimeMillis();
        Double normalizedFactor = normalizePathLenFactor(pathLenFactor);
        Integer currentTaskNo = wrkMast == null ? null : wrkMast.getWrkNo();
        boolean cacheHit = false;
        List<NavigateNode> path;
        if (decisionPathCache == null) {
            if (currentTaskNo == null) {
                return navigateUtils.calcOptimalPathByStationId(sourceStationId, targetStationId, null, normalizedFactor);
            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);
        }
        String cacheKey = buildPathCacheKey(currentTaskNo, sourceStationId, targetStationId, normalizedFactor);
        List<NavigateNode> cachedPath = decisionPathCache.pathMap.get(cacheKey);
        if (cachedPath != null) {
            return cachedPath;
        }
        List<NavigateNode> 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);
        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;
    }
@@ -301,10 +338,12 @@
                                                                           List<Integer> outOrderStationIds,
                                                                           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",
@@ -314,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());
        }
@@ -326,26 +366,36 @@
        }
        List<NavigateNode> initPath;
        long initPathStartMs = System.currentTimeMillis();
        try {
            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, decisionPathCache)) {
            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,
@@ -354,6 +404,15 @@
                    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;
@@ -361,11 +420,14 @@
            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,
@@ -374,6 +436,15 @@
                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;
@@ -496,6 +567,7 @@
                                                 Integer expectedLoopIssueCount,
                                                 Double pathLenFactor,
                                                 DecisionPathCache decisionPathCache) {
        long startMs = System.currentTimeMillis();
        if (currentStationId == null || orderedOutStationList == null || orderedOutStationList.size() <= 1) {
            return null;
        }
@@ -503,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);
@@ -513,6 +587,8 @@
                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) {
            }
@@ -539,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,
src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java
@@ -37,6 +37,7 @@
import com.zy.core.utils.station.model.RerouteExecutionResult;
import com.zy.core.utils.station.model.RerouteSceneType;
import com.zy.core.utils.WmsOperateUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -47,12 +48,15 @@
import java.util.Map;
import java.util.Objects;
@Slf4j
@Component
public class StationRerouteProcessor {
    private static final int OUT_ORDER_DISPATCH_LIMIT_SECONDS = 2;
    private static final long STATION_MOVE_RESET_WAIT_MS = 1000L;
    private static final int RUN_BLOCK_DIRECT_REASSIGN_LIMIT_SECONDS = 8 * 60;
    private static final int RUN_BLOCK_DIRECT_REASSIGN_NEAREST_CACHE_SECONDS = 60 * 60 * 24;
    private static final long CHECK_STATION_OUT_ORDER_SLOW_THRESHOLD_MS = 200L;
    private static final long EXECUTE_REROUTE_PLAN_SLOW_THRESHOLD_MS = 200L;
    @Autowired
    private WrkMastService wrkMastService;
@@ -150,16 +154,19 @@
    }
    public void checkStationOutOrder(BasDevp basDevp, StationObjModel stationObjModel) {
        long totalStartMs = System.currentTimeMillis();
        try {
            if (basDevp == null || basDevp.getDevpNo() == null || stationObjModel == null || stationObjModel.getStationId() == null) {
                return;
            }
            long runtimeStartMs = System.currentTimeMillis();
            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
            if (stationThread == null) {
                return;
            }
            Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
            StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(stationObjModel.getStationId());
            long runtimeCostMs = System.currentTimeMillis() - runtimeStartMs;
            if (stationProtocol == null
                    || !stationProtocol.isAutoing()
                    || !stationProtocol.isLoading()
@@ -169,17 +176,25 @@
                return;
            }
            long loadWrkStartMs = System.currentTimeMillis();
            WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
            long loadWrkCostMs = System.currentTimeMillis() - loadWrkStartMs;
            if (wrkMast == null
                    || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)
                    || Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) {
                return;
            }
            if (stationOutboundDecisionSupport.shouldSkipOutOrderDispatchForExistingRoute(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
            long skipCheckStartMs = System.currentTimeMillis();
            boolean skipForExistingRoute = stationOutboundDecisionSupport
                    .shouldSkipOutOrderDispatchForExistingRoute(wrkMast.getWrkNo(), stationProtocol.getStationId());
            long skipCheckCostMs = System.currentTimeMillis() - skipCheckStartMs;
            if (skipForExistingRoute) {
                return;
            }
            long pathFactorStartMs = System.currentTimeMillis();
            Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
            long pathFactorCostMs = System.currentTimeMillis() - pathFactorStartMs;
            RerouteContext context = RerouteContext.create(
                    RerouteSceneType.OUT_ORDER,
                    basDevp,
@@ -193,7 +208,29 @@
                    .withSuppressDispatchGuard()
                    .withOutOrderDispatchLock()
                    .withResetSegmentCommandsBeforeDispatch();
            executeSharedReroute(context);
            long rerouteStartMs = System.currentTimeMillis();
            RerouteExecutionResult result = executeSharedReroute(context);
            long rerouteCostMs = System.currentTimeMillis() - rerouteStartMs;
            long totalCostMs = System.currentTimeMillis() - totalStartMs;
            log.info("checkStationOutOrder profile, taskNo={}, stationId={}, deviceNo={}, batch={}, batchSeq={}, finalTargetStationId={}, pathLenFactor={}, runtimeCostMs={}ms, loadWrkCostMs={}ms, skipCheckCostMs={}ms, pathFactorCostMs={}ms, rerouteCostMs={}ms, totalCostMs={}ms, result={}",
                    stationProtocol.getTaskNo(),
                    stationProtocol.getStationId(),
                    stationObjModel.getDeviceNo(),
                    wrkMast.getBatch(),
                    wrkMast.getBatchSeq(),
                    wrkMast.getStaNo(),
                    pathLenFactor,
                    runtimeCostMs,
                    loadWrkCostMs,
                    skipCheckCostMs,
                    pathFactorCostMs,
                    rerouteCostMs,
                    totalCostMs,
                    result == null ? null : result.skipReason());
            if (totalCostMs > CHECK_STATION_OUT_ORDER_SLOW_THRESHOLD_MS) {
                log.warn("checkStationOutOrder slow, taskNo={}, stationId={}, totalCostMs={}ms, rerouteCostMs={}ms, loadWrkCostMs={}ms, pathFactorCostMs={}ms",
                        stationProtocol.getTaskNo(), stationProtocol.getStationId(), totalCostMs, rerouteCostMs, loadWrkCostMs, pathFactorCostMs);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
@@ -284,6 +321,7 @@
    }
    public RerouteDecision resolveSharedRerouteDecision(RerouteContext context) {
        long startMs = System.currentTimeMillis();
        if (context == null || context.wrkMast() == null || context.stationProtocol() == null) {
            return RerouteDecision.skip("missing-runtime-dependency");
        }
@@ -295,9 +333,13 @@
        if (context.sceneType() == RerouteSceneType.IDLE_RECOVER
                && !Objects.equals(context.wrkMast().getWrkSts(), WrkStsType.STATION_RUN.sts)) {
            Integer targetStationId = context.wrkMast().getStaNo();
            return targetStationId == null || Objects.equals(targetStationId, currentStationId)
            RerouteDecision decision = targetStationId == null || Objects.equals(targetStationId, currentStationId)
                    ? RerouteDecision.skip("same-station")
                    : RerouteDecision.proceed(targetStationId);
            log.info("resolveSharedRerouteDecision profile, sceneType={}, taskNo={}, currentStationId={}, targetStationId={}, decision={}, decisionCostMs={}ms",
                    context.sceneType(), context.wrkMast().getWrkNo(), currentStationId, targetStationId,
                    decision.skip() ? decision.skipReason() : "proceed", System.currentTimeMillis() - startMs);
            return decision;
        }
        OutOrderDispatchDecision dispatchDecision =
@@ -308,10 +350,15 @@
                        context.pathLenFactor()
                );
        Integer targetStationId = dispatchDecision == null ? null : dispatchDecision.getTargetStationId();
        if (targetStationId == null || Objects.equals(targetStationId, currentStationId)) {
            return RerouteDecision.skip("same-station");
        }
        return RerouteDecision.proceed(targetStationId, dispatchDecision);
        RerouteDecision decision = targetStationId == null || Objects.equals(targetStationId, currentStationId)
                ? RerouteDecision.skip("same-station")
                : RerouteDecision.proceed(targetStationId, dispatchDecision);
        log.info("resolveSharedRerouteDecision profile, sceneType={}, taskNo={}, currentStationId={}, targetStationId={}, decision={}, circle={}, decisionCostMs={}ms",
                context.sceneType(), context.wrkMast().getWrkNo(), currentStationId, targetStationId,
                decision.skip() ? decision.skipReason() : "proceed",
                dispatchDecision != null && dispatchDecision.isCircle(),
                System.currentTimeMillis() - startMs);
        return decision;
    }
    public boolean shouldUseRunBlockDirectReassign(WrkMast wrkMast,
@@ -479,6 +526,7 @@
                                                                  StationProtocol stationProtocol,
                                                                  Integer taskNo,
                                                                  Integer stationId) {
        long startMs = System.currentTimeMillis();
        boolean runBlockReroute = context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE;
        int currentTaskBufferCommandCount = countCurrentTaskBufferCommands(stationProtocol.getTaskBufferItems(), taskNo);
        if (currentTaskBufferCommandCount > 0 && !runBlockReroute) {
@@ -490,40 +538,65 @@
                    taskNo,
                    currentTaskBufferCommandCount);
        }
        if (!runBlockReroute
        long suppressStartMs = System.currentTimeMillis();
        boolean suppressDispatch = !runBlockReroute
                && context.checkSuppressDispatch()
                && stationMoveCoordinator != null
                && stationMoveCoordinator.shouldSuppressDispatch(taskNo, stationId, plan.command())) {
                && stationMoveCoordinator.shouldSuppressDispatch(taskNo, stationId, plan.command());
        long suppressCostMs = System.currentTimeMillis() - suppressStartMs;
        if (suppressDispatch) {
            return RerouteExecutionResult.skip("dispatch-suppressed");
        }
        if (context.requireOutOrderDispatchLock()
                && !stationDispatchRuntimeStateSupport.tryAcquireOutOrderDispatchLock(taskNo, stationId, OUT_ORDER_DISPATCH_LIMIT_SECONDS)) {
        long outOrderLockStartMs = System.currentTimeMillis();
        boolean outOrderLockAcquired = !context.requireOutOrderDispatchLock()
                || stationDispatchRuntimeStateSupport.tryAcquireOutOrderDispatchLock(taskNo, stationId, OUT_ORDER_DISPATCH_LIMIT_SECONDS);
        long outOrderLockCostMs = System.currentTimeMillis() - outOrderLockStartMs;
        if (!outOrderLockAcquired) {
            return RerouteExecutionResult.skip("out-order-lock");
        }
        if (!isBlank(context.executionLockKey())
                && !stationDispatchRuntimeStateSupport.tryAcquireLock(context.executionLockKey(), context.executionLockSeconds())) {
                return RerouteExecutionResult.skip("scene-lock");
        long sceneLockStartMs = System.currentTimeMillis();
        boolean sceneLockAcquired = isBlank(context.executionLockKey())
                || stationDispatchRuntimeStateSupport.tryAcquireLock(context.executionLockKey(), context.executionLockSeconds());
        long sceneLockCostMs = System.currentTimeMillis() - sceneLockStartMs;
        if (!sceneLockAcquired) {
            return RerouteExecutionResult.skip("scene-lock");
        }
        long resetCostMs = 0L;
        if (context.resetSegmentCommandsBeforeDispatch()) {
            long resetStartMs = System.currentTimeMillis();
            stationDispatchRuntimeStateSupport.signalSegmentReset(taskNo, STATION_MOVE_RESET_WAIT_MS);
            resetCostMs = System.currentTimeMillis() - resetStartMs;
        }
        int clearedCommandCount = 0;
        long cancelSessionCostMs = 0L;
        // 先取消旧 session 并记录新 session,再入队命令,避免消费线程在 session 写入 Redis 前取到命令导致路由校验失败。
        if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
            long cancelSessionStartMs = System.currentTimeMillis();
            stationMoveCoordinator.markCancelPending(taskNo, "reroute_pending");
            stationMoveCoordinator.cancelSession(taskNo);
            cancelSessionCostMs = System.currentTimeMillis() - cancelSessionStartMs;
        }
        long registerDispatchStartMs = System.currentTimeMillis();
        preRegisterDispatchSession(context, plan);
        long registerDispatchCostMs = System.currentTimeMillis() - registerDispatchStartMs;
        long offerDispatchStartMs = System.currentTimeMillis();
        boolean offered = offerDevpCommandWithDedup(context.dispatchDeviceNo(), plan.command(), plan.dispatchScene());
        long offerDispatchCostMs = System.currentTimeMillis() - offerDispatchStartMs;
        if (!offered) {
            return RerouteExecutionResult.skip("dispatch-dedup");
        }
        applyRerouteDispatchEffects(context, plan, clearedCommandCount);
        long totalCostMs = System.currentTimeMillis() - startMs;
        log.info("executeReroutePlanWithTaskLock profile, sceneType={}, taskNo={}, stationId={}, suppressCostMs={}ms, outOrderLockCostMs={}ms, sceneLockCostMs={}ms, resetCostMs={}ms, cancelSessionCostMs={}ms, registerDispatchCostMs={}ms, offerDispatchCostMs={}ms, totalCostMs={}ms",
                context.sceneType(), taskNo, stationId, suppressCostMs, outOrderLockCostMs, sceneLockCostMs, resetCostMs, cancelSessionCostMs, registerDispatchCostMs, offerDispatchCostMs, totalCostMs);
        if (totalCostMs > EXECUTE_REROUTE_PLAN_SLOW_THRESHOLD_MS) {
            log.warn("executeReroutePlanWithTaskLock slow, sceneType={}, taskNo={}, stationId={}, totalCostMs={}ms, resetCostMs={}ms, registerDispatchCostMs={}ms, offerDispatchCostMs={}ms",
                    context.sceneType(), taskNo, stationId, totalCostMs, resetCostMs, registerDispatchCostMs, offerDispatchCostMs);
        }
        return RerouteExecutionResult.dispatched(plan.command(), clearedCommandCount);
    }