From d16c9af7bb89a7e3abb4071003c63b3a6f6b9e4b Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期二, 07 四月 2026 14:45:27 +0800
Subject: [PATCH] #

---
 src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java |  272 ++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 202 insertions(+), 70 deletions(-)

diff --git a/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
index f72ce6a..c736296 100644
--- a/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
+++ b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
@@ -21,6 +21,7 @@
 import com.zy.core.model.command.StationCommand;
 import com.zy.core.model.protocol.StationProtocol;
 import com.zy.core.move.StationMoveCoordinator;
+import com.zy.core.move.StationMoveSession;
 import com.zy.core.thread.StationThread;
 import com.zy.core.utils.station.model.DispatchLimitConfig;
 import com.zy.core.utils.station.model.LoadGuardState;
@@ -28,6 +29,7 @@
 import com.zy.core.utils.station.model.OutOrderDispatchDecision;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
+import org.springframework.beans.factory.annotation.Value;
 
 import java.util.Date;
 import java.util.List;
@@ -38,6 +40,9 @@
 public class StationOutboundDispatchProcessor {
 
     private static final int PENDING_DISPATCH_EXPIRE_SECONDS = 60 * 10;
+    private static final long DEFAULT_PENDING_DISCOVER_TIMEOUT_MS = 60_000L;
+    private static final long DEFAULT_PENDING_SESSION_PROTECT_MS = 90_000L;
+    private static final long DEFAULT_RECENT_DISPATCH_PROTECT_MS = 60_000L;
 
     @Autowired
     private WrkMastService wrkMastService;
@@ -57,19 +62,15 @@
     private StationDispatchLoadSupport stationDispatchLoadSupport;
     @Autowired
     private StationOutboundDecisionSupport stationOutboundDecisionSupport;
+    @Autowired
+    private StationDispatchRuntimeStateSupport stationDispatchRuntimeStateSupport;
 
-    public void crnStationOutExecute() {
-        try {
-            List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
-                    .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
-                    .isNotNull("crn_no"));
-            for (WrkMast wrkMast : wrkMasts) {
-                crnStationOutExecute(wrkMast);
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-    }
+    @Value("${station.outbound.pending-discover-timeout-seconds:60}")
+    private long pendingDiscoverTimeoutSeconds;
+    @Value("${station.outbound.pending-session-protect-seconds:90}")
+    private long pendingSessionProtectSeconds;
+    @Value("${station.outbound.recent-dispatch-protect-seconds:60}")
+    private long recentDispatchProtectSeconds;
 
     public void crnStationOutExecute(WrkMast wrkMast) {
         try {
@@ -119,11 +120,22 @@
                 } catch (Exception ignore) {
                     createdAt = System.currentTimeMillis();
                 }
-                if (System.currentTimeMillis() - createdAt < 15_000L) {
+                long pendingDiscoverTimeoutMs = resolvePendingDiscoverTimeoutMs();
+                if (System.currentTimeMillis() - createdAt < pendingDiscoverTimeoutMs) {
+                    return;
+                }
+
+                PendingDispatchGuardResult guardResult = evaluatePendingDispatchGuard(wrkMast, pendingStationObjModel, pendingStationThread);
+                if (guardResult.keepPending()) {
+                    News.info("杈撻�佺珯鐐圭瓑寰呮簮绔欐墽琛岃秴鏃跺悗缁х画淇濇寔绛夊緟銆傚伐浣滃彿={}锛屾簮绔�={}锛屽師鍥�={}",
+                            wrkMast.getWrkNo(),
+                            pendingStationObjModel.getStationId(),
+                            guardResult.reason());
+                    markPendingDispatch(wrkMast.getWrkNo());
                     return;
                 }
                 clearPendingDispatch(wrkMast.getWrkNo());
-                News.warn("杈撻�佺珯鐐规墽琛岃秴鏃讹紝宸查噴鏀鹃噸璇曡祫鏍笺�傚伐浣滃彿={}", wrkMast.getWrkNo());
+                News.warn("杈撻�佺珯鐐规墽琛岀‘璁よ秴鏃讹紝涓旀湭鍙戠幇鏈夋晥娲诲姩閾捐矾锛屽凡閲婃斁閲嶈瘯璧勬牸銆傚伐浣滃彿={}", wrkMast.getWrkNo());
             }
 
             StationObjModel stationObjModel = getOutboundSourceStation(wrkMast);
@@ -224,69 +236,67 @@
         }
     }
 
-    public void dualCrnStationOutExecute() {
+    public void dualCrnStationOutExecute(WrkMast wrkMast) {
         try {
-            List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
-                    .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
-                    .isNotNull("dual_crn_no"));
-            for (WrkMast wrkMast : wrkMasts) {
-                if (hasPendingDispatch(wrkMast.getWrkNo())) {
-                    continue;
-                }
-                StationObjModel stationObjModel = getOutboundSourceStation(wrkMast);
-                if (stationObjModel == null || stationObjModel.getDeviceNo() == null || stationObjModel.getStationId() == null) {
-                    continue;
-                }
-                StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
-                if (stationThread == null) {
-                    continue;
+            if (wrkMast == null || wrkMast.getWrkNo() == null) {
+                return;
+            }
+            if (hasPendingDispatch(wrkMast.getWrkNo())) {
+                return;
+            }
+            StationObjModel stationObjModel = getOutboundSourceStation(wrkMast);
+            if (stationObjModel == null || stationObjModel.getDeviceNo() == null || stationObjModel.getStationId() == null) {
+                return;
+            }
+            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
+            if (stationThread == null) {
+                return;
+            }
+
+            Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
+            StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
+            if (stationProtocol == null) {
+                return;
+            }
+
+            Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
+            if (lock != null) {
+                return;
+            }
+
+            if (stationProtocol.isAutoing()
+                    && stationProtocol.isLoading()
+                    && stationProtocol.getTaskNo() == 0) {
+                Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
+                StationCommand command = stationOutboundDecisionSupport.buildOutboundMoveCommand(
+                        stationThread,
+                        wrkMast,
+                        stationProtocol.getStationId(),
+                        wrkMast.getStaNo(),
+                        pathLenFactor
+                );
+                if (command == null) {
+                    News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
+                    return;
                 }
 
-                Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
-                StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
-                if (stationProtocol == null) {
-                    continue;
+                boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "dualCrnStationOutExecute");
+                if (!offered) {
+                    return;
                 }
-
-                Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
-                if (lock != null) {
-                    continue;
-                }
-
-                if (stationProtocol.isAutoing()
-                        && stationProtocol.isLoading()
-                        && stationProtocol.getTaskNo() == 0) {
-                    Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
-                    StationCommand command = stationOutboundDecisionSupport.buildOutboundMoveCommand(
-                            stationThread,
-                            wrkMast,
+                if (stationMoveCoordinator != null) {
+                    stationMoveCoordinator.recordDispatch(
+                            wrkMast.getWrkNo(),
                             stationProtocol.getStationId(),
-                            wrkMast.getStaNo(),
-                            pathLenFactor
+                            "dualCrnStationOutExecute",
+                            command,
+                            false
                     );
-                    if (command == null) {
-                        News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
-                        continue;
-                    }
-
-                    boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "dualCrnStationOutExecute");
-                    if (!offered) {
-                        continue;
-                    }
-                    if (stationMoveCoordinator != null) {
-                        stationMoveCoordinator.recordDispatch(
-                                wrkMast.getWrkNo(),
-                                stationProtocol.getStationId(),
-                                "dualCrnStationOutExecute",
-                                command,
-                                false
-                        );
-                    }
-                    markPendingDispatch(wrkMast.getWrkNo());
-                    News.info("杈撻�佺珯鐐瑰嚭搴撳懡浠ゅ凡鍏ヨ澶囨墽琛岄摼璺紝绛夊緟婧愮珯鎺ュ崟銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}",
-                            stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
-                    redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5);
                 }
+                markPendingDispatch(wrkMast.getWrkNo());
+                News.info("杈撻�佺珯鐐瑰嚭搴撳懡浠ゅ凡鍏ヨ澶囨墽琛岄摼璺紝绛夊緟婧愮珯鎺ュ崟銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}",
+                        stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
+                redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5);
             }
         } catch (Exception e) {
             e.printStackTrace();
@@ -330,4 +340,126 @@
         }
         redisUtil.del(RedisKeyType.STATION_OUT_PENDING_DISPATCH_.key + wrkNo);
     }
+
+    private PendingDispatchGuardResult evaluatePendingDispatchGuard(WrkMast wrkMast,
+                                                                    StationObjModel stationObjModel,
+                                                                    StationThread stationThread) {
+        if (wrkMast == null || wrkMast.getWrkNo() == null || stationObjModel == null || stationObjModel.getStationId() == null) {
+            return PendingDispatchGuardResult.release("missing-runtime-context");
+        }
+        if (hasActiveMoveSession(wrkMast.getWrkNo(), stationObjModel.getStationId())) {
+            return PendingDispatchGuardResult.keep("active-session");
+        }
+        if (hasRecentSuccessfulDispatch(wrkMast.getWrkNo(), stationObjModel.getStationId())) {
+            return PendingDispatchGuardResult.keep("recent-successful-dispatch");
+        }
+        if (taskExistsInBuffer(stationThread, wrkMast.getWrkNo())) {
+            return PendingDispatchGuardResult.keep("task-buffer-detected");
+        }
+        return PendingDispatchGuardResult.release("no-active-chain");
+    }
+
+    private boolean hasActiveMoveSession(Integer wrkNo, Integer sourceStationId) {
+        if (wrkNo == null || wrkNo <= 0 || stationMoveCoordinator == null || sourceStationId == null) {
+            return false;
+        }
+        StationMoveSession session = stationMoveCoordinator.loadSession(wrkNo);
+        if (session == null || !session.isActive()) {
+            return false;
+        }
+        if (!Objects.equals(sourceStationId, session.getDispatchStationId())) {
+            return false;
+        }
+        long now = System.currentTimeMillis();
+        Long lastIssuedAt = session.getLastIssuedAt();
+        if (lastIssuedAt != null && now - lastIssuedAt <= resolvePendingSessionProtectMs()) {
+            return true;
+        }
+        Long updatedAt = session.getUpdatedAt();
+        return updatedAt != null && now - updatedAt <= resolvePendingSessionProtectMs();
+    }
+
+    private boolean hasRecentSuccessfulDispatch(Integer wrkNo, Integer sourceStationId) {
+        if (stationDispatchRuntimeStateSupport == null) {
+            return false;
+        }
+        return stationDispatchRuntimeStateSupport.hasRecentIssuedMoveCommand(
+                wrkNo,
+                sourceStationId,
+                resolveRecentDispatchProtectMs()
+        );
+    }
+
+    private boolean taskExistsInBuffer(StationThread stationThread, Integer wrkNo) {
+        if (stationThread == null || wrkNo == null || wrkNo <= 0) {
+            return false;
+        }
+        Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
+        if (statusMap == null || statusMap.isEmpty()) {
+            return false;
+        }
+        for (StationProtocol stationProtocol : statusMap.values()) {
+            if (stationProtocol == null || stationProtocol.getTaskBufferItems() == null) {
+                continue;
+            }
+            if (containsTaskNo(stationProtocol, wrkNo)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean containsTaskNo(StationProtocol stationProtocol, Integer wrkNo) {
+        if (stationProtocol == null || stationProtocol.getTaskBufferItems() == null || wrkNo == null) {
+            return false;
+        }
+        return stationProtocol.getTaskBufferItems().stream()
+                .filter(Objects::nonNull)
+                .anyMatch(item -> Objects.equals(wrkNo, item.getTaskNo()));
+    }
+
+    private long resolvePendingDiscoverTimeoutMs() {
+        return resolveMillis(pendingDiscoverTimeoutSeconds, DEFAULT_PENDING_DISCOVER_TIMEOUT_MS);
+    }
+
+    private long resolvePendingSessionProtectMs() {
+        return resolveMillis(pendingSessionProtectSeconds, DEFAULT_PENDING_SESSION_PROTECT_MS);
+    }
+
+    private long resolveRecentDispatchProtectMs() {
+        return resolveMillis(recentDispatchProtectSeconds, DEFAULT_RECENT_DISPATCH_PROTECT_MS);
+    }
+
+    private long resolveMillis(long seconds, long defaultMs) {
+        if (seconds <= 0L) {
+            return defaultMs;
+        }
+        return seconds * 1000L;
+    }
+
+    private static class PendingDispatchGuardResult {
+        private final boolean keepPending;
+        private final String reason;
+
+        private PendingDispatchGuardResult(boolean keepPending, String reason) {
+            this.keepPending = keepPending;
+            this.reason = reason;
+        }
+
+        public static PendingDispatchGuardResult keep(String reason) {
+            return new PendingDispatchGuardResult(true, reason);
+        }
+
+        public static PendingDispatchGuardResult release(String reason) {
+            return new PendingDispatchGuardResult(false, reason);
+        }
+
+        public boolean keepPending() {
+            return keepPending;
+        }
+
+        public String reason() {
+            return reason;
+        }
+    }
 }

--
Gitblit v1.9.1