From 9f13307b0ad0d7a0bac431773ec073cb93b170d4 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期四, 02 四月 2026 18:50:23 +0800
Subject: [PATCH] #预调度堆垛机2

---
 src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java |  904 ++++++++++++++++++++++++++++++++-----------------------
 1 files changed, 521 insertions(+), 383 deletions(-)

diff --git a/src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java b/src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
index 903d756..67cfb8a 100644
--- a/src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
@@ -15,6 +15,7 @@
 import com.zy.asrs.service.BasCrnpService;
 import com.zy.asrs.service.BasStationService;
 import com.zy.asrs.service.LocMastService;
+import com.zy.asrs.service.WrkAnalysisService;
 import com.zy.asrs.service.WrkMastService;
 import com.zy.asrs.utils.NotifyUtils;
 import com.zy.asrs.utils.Utils;
@@ -30,6 +31,8 @@
 import com.zy.core.model.command.CrnCommand;
 import com.zy.core.model.protocol.CrnProtocol;
 import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.task.MainProcessLane;
+import com.zy.core.task.MainProcessTaskSubmitter;
 import com.zy.core.thread.CrnThread;
 import com.zy.core.thread.StationThread;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -39,8 +42,11 @@
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
 
 @Component
 public class CrnOperateProcessUtils {
@@ -63,93 +69,97 @@
     private NotifyUtils notifyUtils;
     @Autowired
     private StationOperateProcessUtils stationOperateProcessUtils;
+    @Autowired
+    private WrkAnalysisService wrkAnalysisService;
+    @Autowired
+    private MainProcessTaskSubmitter mainProcessTaskSubmitter;
+
+    private static final String CRN_OUT_REQUIRE_STATION_OUT_ENABLE_CONFIG = "crnOutRequireStationOutEnable";
 
     public synchronized void crnIoExecute() {
+        Set<Integer> crnMoveBlockedCrnNos = executeCrnMoveTask();
+        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
+        for (BasCrnp basCrnp : basCrnps) {
+            crnIoExecute(basCrnp, crnMoveBlockedCrnNos);
+        }
+    }
+
+    public void crnIoExecute(BasCrnp basCrnp, Set<Integer> crnMoveBlockedCrnNos) {
+        if (basCrnp == null || basCrnp.getCrnNo() == null) {
+            return;
+        }
         Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
         if (systemConfigMapObj != null) {
             HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
             if (systemConfigMap.get("crnRunMethod").equals("solver")) {
-                plannerExecute();
+                plannerExecute(basCrnp, crnMoveBlockedCrnNos);
             }else {
-                crnIoExecuteNormal();
+                crnIoExecuteNormal(basCrnp, crnMoveBlockedCrnNos);
             }
         }
     }
 
     //鍏ュ嚭搴�  ===>>  鍫嗗灈鏈哄叆鍑哄簱浣滀笟涓嬪彂
-    public synchronized void crnIoExecuteNormal() {
-        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
-        Map<Integer, BasCrnp> dispatchCrnMap = new HashMap<>();
-        Map<Integer, CrnThread> dispatchThreadMap = new HashMap<>();
-        Map<Integer, CrnProtocol> dispatchProtocolMap = new HashMap<>();
-        for (BasCrnp basCrnp : basCrnps) {
-            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
-            if (crnThread == null) {
-                continue;
-            }
-
-            CrnProtocol crnProtocol = crnThread.getStatus();
-            if (crnProtocol == null) {
-                continue;
-            }
-
-            long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
-                    .eq("crn_no", basCrnp.getCrnNo())
-                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts));
-            if (runningCount > 0) {
-                continue;
-            }
-
-            // 鍙湁褰撳爢鍨涙満绌洪棽 骞朵笖 鏃犱换鍔℃椂鎵嶇户缁墽琛�
-            if (crnProtocol.getMode() == CrnModeType.AUTO.id
-                    && crnProtocol.getTaskNo() == 0
-                    && crnProtocol.getStatus() == CrnStatusType.IDLE.id
-                    && crnProtocol.getLoaded() == 0
-                    && crnProtocol.getForkPos() == 0
-                    && crnProtocol.getAlarm() == 0
-            ) {
-                Object clearLock = redisUtil.get(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + basCrnp.getCrnNo());
-                if (clearLock != null) {
-                    continue;
-                }
-                dispatchCrnMap.put(basCrnp.getCrnNo(), basCrnp);
-                dispatchThreadMap.put(basCrnp.getCrnNo(), crnThread);
-                dispatchProtocolMap.put(basCrnp.getCrnNo(), crnProtocol);
-            }
-        }
-
-        if (dispatchCrnMap.isEmpty()) {
+    private void crnIoExecuteNormal(BasCrnp currentCrn, Set<Integer> crnMoveBlockedCrnNos) {
+        if (currentCrn == null || currentCrn.getCrnNo() == null || crnMoveBlockedCrnNos.contains(currentCrn.getCrnNo())) {
             return;
         }
 
+        Integer crnNo = currentCrn.getCrnNo();
+        CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
+        if (crnThread == null) {
+            return;
+        }
+
+        CrnProtocol crnProtocol = crnThread.getStatus();
+        if (crnProtocol == null) {
+            return;
+        }
+
+        long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
+                .eq("crn_no", crnNo)
+                .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts, WrkStsType.CRN_MOVE_RUN.sts));
+        if (runningCount > 0) {
+            return;
+        }
+
+        // 鍙湁褰撳爢鍨涙満绌洪棽 骞朵笖 鏃犱换鍔℃椂鎵嶇户缁墽琛�
+        if (crnProtocol.getMode() != CrnModeType.AUTO.id
+                || crnProtocol.getTaskNo() != 0
+                || crnProtocol.getStatus() != CrnStatusType.IDLE.id
+                || crnProtocol.getLoaded() != 0
+                || crnProtocol.getForkPos() != 0
+                || crnProtocol.getAlarm() != 0) {
+            return;
+        }
+
+        Object clearLock = redisUtil.get(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + crnNo);
+        if (clearLock != null) {
+            return;
+        }
+
+        String lastIo = resolveCrnLastIo(crnProtocol);
+
         List<WrkMast> taskQueue = wrkMastService.list(new QueryWrapper<WrkMast>()
-                .in("crn_no", new ArrayList<>(dispatchCrnMap.keySet()))
+                .eq("crn_no", crnNo)
                 .in("wrk_sts",
-                        WrkStsType.INBOUND_DEVICE_RUN.sts,
+                        WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts,
                         WrkStsType.NEW_OUTBOUND.sts,
                         WrkStsType.NEW_LOC_MOVE.sts));
-        taskQueue.sort(Comparator
-                .comparingInt(this::resolveBatchOutboundRank)
+        taskQueue.sort(Comparator.comparingInt((WrkMast wrkMast) -> resolveTaskTypeRank(wrkMast, lastIo))
+                .thenComparingInt(this::resolveBatchOutboundRank)
                 .thenComparingInt(this::resolveBatchSeqOrder)
                 .thenComparingDouble(this::resolveTaskIoPri)
                 .thenComparingLong(this::resolveTaskQueueTime)
                 .thenComparingInt(this::resolveTaskQueueNo));
 
         for (WrkMast wrkMast : taskQueue) {
-            if (wrkMast == null || wrkMast.getCrnNo() == null) {
+            if (wrkMast == null || wrkMast.getCrnNo() == null || !Objects.equals(wrkMast.getCrnNo(), crnNo)) {
                 continue;
             }
 
-            Integer crnNo = wrkMast.getCrnNo();
-            BasCrnp basCrnp = dispatchCrnMap.get(crnNo);
-            CrnThread crnThread = dispatchThreadMap.get(crnNo);
-            CrnProtocol crnProtocol = dispatchProtocolMap.get(crnNo);
-            if (basCrnp == null || crnThread == null || crnProtocol == null) {
-                continue;
-            }
-
-            if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.INBOUND_DEVICE_RUN.sts) {
-                boolean result = this.crnExecuteInPlanner(basCrnp, crnThread, wrkMast);
+            if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) {
+                boolean result = this.crnExecuteInPlanner(currentCrn, crnThread, wrkMast);
                 if (result) {
                     crnProtocol.setLastIo("O");
                     return;
@@ -158,7 +168,7 @@
             }
 
             if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.NEW_OUTBOUND.sts) {
-                boolean result = this.crnExecuteOutPlanner(basCrnp, crnThread, wrkMast);
+                boolean result = this.crnExecuteOutPlanner(currentCrn, crnThread, wrkMast);
                 if (result) {
                     crnProtocol.setLastIo("I");
                     return;
@@ -167,7 +177,7 @@
             }
 
             if (wrkMast.getWrkSts() != null && wrkMast.getWrkSts() == WrkStsType.NEW_LOC_MOVE.sts) {
-                boolean transfer = this.crnExecuteMovePlanner(basCrnp, crnThread, wrkMast);
+                boolean transfer = this.crnExecuteMovePlanner(currentCrn, crnThread, wrkMast);
                 if (transfer) {
                     return;
                 }
@@ -177,6 +187,44 @@
 
     private int resolveBatchOutboundRank(WrkMast wrkMast) {
         return isBatchOutboundTask(wrkMast) ? 0 : 1;
+    }
+
+    private String resolveCrnLastIo(CrnProtocol crnProtocol) {
+        if (crnProtocol == null) {
+            return "I";
+        }
+        if (Cools.isEmpty(crnProtocol.getLastIo())) {
+            return "I";
+        }
+        return crnProtocol.getLastIo();
+    }
+
+    private int resolveTaskTypeRank(WrkMast wrkMast, String lastIo) {
+        if (wrkMast == null || wrkMast.getWrkSts() == null) {
+            return Integer.MAX_VALUE;
+        }
+
+        if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_LOC_MOVE.sts)) {
+            return 0;
+        }
+
+        if (Objects.equals("O", lastIo)) {
+            if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts)) {
+                return 1;
+            }
+            if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_OUTBOUND.sts)) {
+                return 2;
+            }
+            return 3;
+        }
+
+        if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_OUTBOUND.sts)) {
+            return 1;
+        }
+        if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts)) {
+            return 2;
+        }
+        return 3;
     }
 
     private int resolveBatchSeqOrder(WrkMast wrkMast) {
@@ -212,209 +260,6 @@
         return wrkMast != null
                 && Integer.valueOf(WrkIoType.OUT.id).equals(wrkMast.getIoType())
                 && !Cools.isEmpty(wrkMast.getBatch());
-    }
-
-    private synchronized boolean crnExecuteIn(BasCrnp basCrnp, CrnThread crnThread) {
-        CrnProtocol crnProtocol = crnThread.getStatus();
-        if(crnProtocol == null){
-            return false;
-        }
-
-        if(!basCrnp.getInEnable().equals("Y")){
-            News.info("鍫嗗灈鏈�:{} 鍙叆淇″彿涓嶆弧瓒�", basCrnp.getCrnNo());
-            return false;
-        }
-
-        List<StationObjModel> inStationList = basCrnp.getInStationList$();
-        if(inStationList.isEmpty()){
-            News.info("鍫嗗灈鏈�:{} 鍏ュ簱绔欑偣鏈缃�", basCrnp.getCrnNo());
-            return false;
-        }
-
-        Integer crnNo = basCrnp.getCrnNo();
-
-        for (StationObjModel stationObjModel : inStationList) {
-            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
-            if (stationThread == null) {
-                continue;
-            }
-
-            Map<Integer, StationProtocol> stationProtocolMap = stationThread.getStatusMap();
-            StationProtocol stationProtocol = stationProtocolMap.get(stationObjModel.getStationId());
-            if (stationProtocol == null) {
-                continue;
-            }
-
-            if (!stationProtocol.isAutoing()) {
-                continue;
-            }
-
-            if (!stationProtocol.isLoading()) {
-                continue;
-            }
-
-            if (stationProtocol.getTaskNo() <= 0) {
-                continue;
-            }
-
-            if (!stationProtocol.isInEnable()) {
-                News.taskInfo(stationProtocol.getTaskNo(), "鍙栬揣绔欑偣:{} 娌℃湁鍙叆淇″彿", stationObjModel.getStationId());
-                continue;
-            }
-
-            // 鑾峰彇浠诲姟
-            WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
-            if (null == wrkMast) {
-                News.taskInfo(stationProtocol.getTaskNo(), "宸ヤ綔鍙�:{} 浠诲姟淇℃伅涓嶅瓨鍦�", stationProtocol.getTaskNo());
-                continue;
-            }
-
-            if (!wrkMast.getCrnNo().equals(basCrnp.getCrnNo())) {
-                continue;
-            }
-
-            if(wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts){
-                continue;
-            }
-
-            // 鑾峰彇搴撲綅淇℃伅
-            LocMast locMast = locMastService.getById(wrkMast.getLocNo());
-            if (locMast == null) {
-                News.taskInfo(wrkMast.getWrkNo(), "鐩爣搴撲綅:{} 淇℃伅涓嶅瓨鍦�", wrkMast.getLocNo());
-                continue;
-            }
-
-            if (!locMast.getLocSts().equals("S")) {
-                News.taskInfo(wrkMast.getWrkNo(), "鐩爣搴撲綅:{} 鐘舵�佸紓甯�", wrkMast.getLocNo());
-                continue;
-            }
-
-            //妫�娴嬫祬搴撲綅鐘舵��
-            boolean checkStatus = checkShallowLocStatus(locMast.getLocNo(), wrkMast.getWrkNo());
-            if (!checkStatus) {
-                News.taskInfo(wrkMast.getWrkNo(), "鍥犳祬搴撲綅鍫靛鏃犳硶鎵ц");
-                continue;
-            }
-
-            String sourceLocNo = Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
-
-            CrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
-
-            wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts);
-            wrkMast.setCrnNo(crnNo);
-            wrkMast.setSystemMsg("");
-            wrkMast.setIoTime(new Date());
-            if (wrkMastService.updateById(wrkMast)) {
-                MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
-                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_RUN, null);
-                News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private synchronized boolean crnExecuteOut(BasCrnp basCrnp, CrnThread crnThread) {
-        CrnProtocol crnProtocol = crnThread.getStatus();
-        if(crnProtocol == null){
-            return false;
-        }
-
-        if(!basCrnp.getOutEnable().equals("Y")){
-            News.info("鍫嗗灈鏈�:{} 鍙嚭淇″彿涓嶆弧瓒�", basCrnp.getCrnNo());
-            return false;
-        }
-
-        List<StationObjModel> outStationList = basCrnp.getOutStationList$();
-        if(outStationList.isEmpty()){
-            News.info("鍫嗗灈鏈�:{} 鍑哄簱绔欑偣鏈缃�", basCrnp.getCrnNo());
-            return false;
-        }
-
-        if (isOutboundStationTaskLimitReached()) {
-            return false;
-        }
-
-        Integer crnNo = basCrnp.getCrnNo();
-
-        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
-                .eq("crn_no", crnNo)
-                .eq("wrk_sts", WrkStsType.NEW_OUTBOUND.sts)
-                .orderBy(true, true, "batch_seq")
-        );
-
-        for (WrkMast wrkMast : wrkMasts) {
-            if (isOutboundTargetStationTaskLimitReached(wrkMast)) {
-                continue;
-            }
-            if (!allowBatchOutboundExecute(wrkMast)) {
-                continue;
-            }
-            for (StationObjModel stationObjModel : outStationList) {
-                StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
-                if (stationThread == null) {
-                    continue;
-                }
-
-                Map<Integer, StationProtocol> stationProtocolMap = stationThread.getStatusMap();
-                StationProtocol stationProtocol = stationProtocolMap.get(stationObjModel.getStationId());
-                if (stationProtocol == null) {
-                    continue;
-                }
-
-                if (!stationProtocol.isAutoing()) {
-                    continue;
-                }
-
-                if (stationProtocol.isLoading()) {
-                    continue;
-                }
-
-                if (stationProtocol.getTaskNo() != 0) {
-                    continue;
-                }
-
-                if (!stationProtocol.isOutEnable()) {
-                    News.info("鏀捐揣绔欑偣:{} 娌℃湁鍙嚭淇″彿", stationObjModel.getStationId());
-                    continue;
-                }
-
-                // 鑾峰彇搴撲綅淇℃伅
-                LocMast locMast = locMastService.getById(wrkMast.getSourceLocNo());
-                if (locMast == null) {
-                    News.taskInfo(wrkMast.getWrkNo(), "婧愬簱浣�:{} 淇℃伅涓嶅瓨鍦�", wrkMast.getSourceLocNo());
-                    continue;
-                }
-
-                if (!locMast.getLocSts().equals("R")) {
-                    News.taskInfo(wrkMast.getWrkNo(), "婧愬簱浣�:{} 鐘舵�佸紓甯�", wrkMast.getSourceLocNo());
-                    continue;
-                }
-
-                //妫�娴嬫祬搴撲綅鐘舵��
-                boolean checkStatus = checkShallowLocStatus(locMast.getLocNo(), wrkMast.getWrkNo());
-                if (!checkStatus) {
-                    News.taskInfo(wrkMast.getWrkNo(), "鍥犳祬搴撲綅鍫靛鏃犳硶鎵ц");
-                    continue;
-                }
-
-                String targetLocNo = Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
-
-                CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), targetLocNo, wrkMast.getWrkNo(), crnNo);
-
-                wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts);
-                wrkMast.setCrnNo(crnNo);
-                wrkMast.setSystemMsg("");
-                wrkMast.setIoTime(new Date());
-                if (wrkMastService.updateById(wrkMast)) {
-                    MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
-                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_RUN, null);
-                    News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
-                    return true;
-                }
-            }
-        }
-        return false;
     }
 
     private synchronized boolean crnExecuteInPlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
@@ -469,7 +314,7 @@
                 continue;
             }
 
-            if (wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) {
+            if (wrkMast.getWrkSts() != WrkStsType.INBOUND_STATION_RUN_COMPLETE.sts) {
                 continue;
             }
 
@@ -496,11 +341,14 @@
 
             CrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
 
+            Date now = new Date();
             wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts);
             wrkMast.setCrnNo(crnNo);
             wrkMast.setSystemMsg("");
-            wrkMast.setIoTime(new Date());
+            wrkMast.setIoTime(now);
+            wrkMast.setModiTime(now);
             if (wrkMastService.updateById(wrkMast)) {
+                wrkAnalysisService.markCraneStart(wrkMast, now);
                 MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
                 notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_RUN, null);
                 News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
@@ -565,8 +413,8 @@
                 continue;
             }
 
-            if (!stationProtocol.isOutEnable()) {
-                News.info("鏀捐揣绔欑偣:{} 娌℃湁鍙嚭淇″彿", stationObjModel.getStationId());
+            if (isRequireOutboundStationOutEnable() && !stationProtocol.isOutEnable()) {
+                News.taskInfo(wrkMast.getWrkNo(), "鏀捐揣绔欑偣:{} 娌℃湁鍙嚭淇″彿", stationObjModel.getStationId());
                 continue;
             }
 
@@ -593,11 +441,14 @@
 
             CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), targetLocNo, wrkMast.getWrkNo(), crnNo);
 
+            Date now = new Date();
             wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts);
             wrkMast.setCrnNo(crnNo);
             wrkMast.setSystemMsg("");
-            wrkMast.setIoTime(new Date());
+            wrkMast.setIoTime(now);
+            wrkMast.setModiTime(now);
             if (wrkMastService.updateById(wrkMast)) {
+                wrkAnalysisService.markCraneStart(wrkMast, now);
                 MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
                 notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_RUN, null);
                 News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
@@ -645,6 +496,25 @@
             return true;
         }
 
+        List<WrkMast> unfinishedBatchWrkMasts = listUnfinishedBatchWrkMasts(wrkMast.getBatch());
+        if (hasMissingBatchSeq(unfinishedBatchWrkMasts)) {
+            News.taskInfo(wrkMast.getWrkNo(), "鎵规:{} 瀛樺湪鏈厤缃壒娆″簭鍙风殑鏈畬鎴愪换鍔★紝鏆備笉鍏佽鍫嗗灈鏈哄嚭搴�", wrkMast.getBatch());
+            return false;
+        }
+
+        if (!isWithinBatchExecuteWindow(wrkMast, unfinishedBatchWrkMasts, batchRunningLimit)) {
+            List<Integer> windowBatchSeqList = unfinishedBatchWrkMasts.stream()
+                    .limit(Math.min(batchRunningLimit, unfinishedBatchWrkMasts.size()))
+                    .map(WrkMast::getBatchSeq)
+                    .collect(java.util.stream.Collectors.toList());
+            News.taskInfo(wrkMast.getWrkNo(),
+                    "鎵规:{} 褰撳墠涓ユ牸鎵ц绐楀彛搴忓彿鍒楄〃涓簕}锛屽綋鍓嶅簭鍙�={}锛屾殏涓嶅厑璁稿爢鍨涙満鍑哄簱",
+                    wrkMast.getBatch(),
+                    windowBatchSeqList,
+                    wrkMast.getBatchSeq());
+            return false;
+        }
+
         long batchRunningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
                 .eq("io_type", WrkIoType.OUT.id)
                 .eq("batch", wrkMast.getBatch())
@@ -658,6 +528,46 @@
         }
 
         return true;
+    }
+
+    private List<WrkMast> listUnfinishedBatchWrkMasts(String batch) {
+        if (Cools.isEmpty(batch)) {
+            return new ArrayList<>();
+        }
+        return wrkMastService.list(new QueryWrapper<WrkMast>()
+                .eq("io_type", WrkIoType.OUT.id)
+                .eq("batch", batch)
+                .notIn("wrk_sts",
+                        WrkStsType.COMPLETE_OUTBOUND.sts,
+                        WrkStsType.SETTLE_OUTBOUND.sts)
+                .orderByAsc("batch_seq")
+                .orderByAsc("wrk_no"));
+    }
+
+    private boolean hasMissingBatchSeq(List<WrkMast> wrkMasts) {
+        if (wrkMasts == null || wrkMasts.isEmpty()) {
+            return false;
+        }
+        for (WrkMast item : wrkMasts) {
+            if (item == null || Cools.isEmpty(item.getBatchSeq())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isWithinBatchExecuteWindow(WrkMast wrkMast, List<WrkMast> unfinishedBatchWrkMasts, int batchRunningLimit) {
+        if (wrkMast == null || unfinishedBatchWrkMasts == null || unfinishedBatchWrkMasts.isEmpty()) {
+            return true;
+        }
+        int windowSize = Math.min(batchRunningLimit, unfinishedBatchWrkMasts.size());
+        for (int i = 0; i < windowSize; i++) {
+            WrkMast current = unfinishedBatchWrkMasts.get(i);
+            if (current != null && wrkMast.getWrkNo().equals(current.getWrkNo())) {
+                return true;
+            }
+        }
+        return false;
     }
 
     private boolean isOutboundStationTaskLimitReached() {
@@ -707,158 +617,150 @@
         }
     }
 
-    private synchronized boolean crnExecuteLocTransfer(BasCrnp basCrnp, CrnThread crnThread) {
-        CrnProtocol crnProtocol = crnThread.getStatus();
-        if(crnProtocol == null){
-            return false;
+    private boolean isRequireOutboundStationOutEnable() {
+        return getSystemConfigBoolean(CRN_OUT_REQUIRE_STATION_OUT_ENABLE_CONFIG, true);
+    }
+
+    private boolean getSystemConfigBoolean(String code, boolean defaultValue) {
+        Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
+        if (!(systemConfigMapObj instanceof Map)) {
+            return defaultValue;
         }
-
-        Integer crnNo = basCrnp.getCrnNo();
-
-        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
-                .eq("crn_no", crnNo)
-                .eq("wrk_sts", WrkStsType.NEW_LOC_MOVE.sts)
-        );
-
-        for (WrkMast wrkMast : wrkMasts) {
-            // 鑾峰彇婧愬簱浣嶄俊鎭�
-            LocMast sourceLocMast = locMastService.getById(wrkMast.getSourceLocNo());
-            if (sourceLocMast == null) {
-                News.taskInfo(wrkMast.getWrkNo(), "婧愬簱浣�:{} 淇℃伅涓嶅瓨鍦�", wrkMast.getSourceLocNo());
-                continue;
+        try {
+            Object value = ((Map<?, ?>) systemConfigMapObj).get(code);
+            if (value == null) {
+                return defaultValue;
             }
-
-            if(!sourceLocMast.getLocSts().equals("R")){
-                News.taskInfo(wrkMast.getWrkNo(), "婧愬簱浣�:{} 鐘舵�佸紓甯革紝涓嶅睘浜庡嚭搴撻绾︾姸鎬�", wrkMast.getSourceLocNo());
-                continue;
-            }
-            
-            // 鑾峰彇搴撲綅淇℃伅
-            LocMast locMast = locMastService.getById(wrkMast.getLocNo());
-            if (locMast == null) {
-                News.taskInfo(wrkMast.getWrkNo(), "搴撲綅:{} 淇℃伅涓嶅瓨鍦�", wrkMast.getLocNo());
-                continue;
-            }
-
-            if (!locMast.getLocSts().equals("S")) {
-                News.taskInfo(wrkMast.getWrkNo(), "搴撲綅:{} 鐘舵�佸紓甯革紝涓嶅睘浜庡叆搴撻绾︾姸鎬�", wrkMast.getLocNo());
-                continue;
-            }
-
-            CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
-
-            wrkMast.setWrkSts(WrkStsType.LOC_MOVE_RUN.sts);
-            wrkMast.setCrnNo(crnNo);
-            wrkMast.setSystemMsg("");
-            wrkMast.setIoTime(new Date());
-            if (wrkMastService.updateById(wrkMast)) {
-                MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
-                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_RUN, null);
-                News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
+            String text = String.valueOf(value).trim();
+            if ("Y".equalsIgnoreCase(text) || "true".equalsIgnoreCase(text) || "1".equals(text)) {
                 return true;
             }
+            if ("N".equalsIgnoreCase(text) || "false".equalsIgnoreCase(text) || "0".equals(text)) {
+                return false;
+            }
+        } catch (Exception ignore) {
         }
-        return false;
+        return defaultValue;
     }
 
     //鍫嗗灈鏈轰换鍔℃墽琛屽畬鎴�
     public synchronized void crnIoExecuteFinish() {
         List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
         for (BasCrnp basCrnp : basCrnps) {
-            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
-            if(crnThread == null){
-                continue;
+            crnIoExecuteFinish(basCrnp);
+        }
+    }
+
+    public void crnIoExecuteFinish(BasCrnp basCrnp) {
+        if (basCrnp == null || basCrnp.getCrnNo() == null) {
+            return;
+        }
+        CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
+        if(crnThread == null){
+            return;
+        }
+
+        CrnProtocol crnProtocol = crnThread.getStatus();
+        if(crnProtocol == null){
+            return;
+        }
+
+        if (crnProtocol.getMode() == CrnModeType.AUTO.id
+                && crnProtocol.getTaskNo() > 0
+                && crnProtocol.getStatus() == CrnStatusType.WAITING.id
+        ) {
+            Object lock = redisUtil.get(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo());
+            if(lock != null){
+                return;
             }
 
-            CrnProtocol crnProtocol = crnThread.getStatus();
-            if(crnProtocol == null){
-                continue;
+            WrkMast wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo());
+            if (wrkMast == null) {
+                News.error("鍫嗗灈鏈哄浜庣瓑寰呯‘璁や笖浠诲姟瀹屾垚鐘舵�侊紝浣嗘湭鎵惧埌宸ヤ綔妗c�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
+                return;
             }
 
-            if (crnProtocol.getMode() == CrnModeType.AUTO.id
-                    && crnProtocol.getTaskNo() > 0
-                    && crnProtocol.getStatus() == CrnStatusType.WAITING.id
-            ) {
-                Object lock = redisUtil.get(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo());
-                if(lock != null){
-                    continue;
+            Long updateWrkSts = null;
+            Date now = new Date();
+            if(wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts){
+                updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
+                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_COMPLETE, null);
+            }else if(wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts){
+                updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts;
+                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_COMPLETE, null);
+
+                List<StationObjModel> outStationList = basCrnp.getOutStationList$();
+                if(outStationList.isEmpty()){
+                    News.info("鍫嗗灈鏈�:{} 鍑哄簱绔欑偣鏈缃�", basCrnp.getCrnNo());
+                    return;
                 }
 
-                // 鑾峰彇寰呯‘璁ゅ伐浣滄。
-                WrkMast wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo());
-                if (wrkMast == null) {
-                    News.error("鍫嗗灈鏈哄浜庣瓑寰呯‘璁や笖浠诲姟瀹屾垚鐘舵�侊紝浣嗘湭鎵惧埌宸ヤ綔妗c�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
-                    continue;
-                }
-
-                Long updateWrkSts = null;
-                if(wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts){
-                    updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
-                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_IN_TASK_COMPLETE, null);
-                }else if(wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts){
-                    updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts;
-                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_OUT_TASK_COMPLETE, null);
-
-                    List<StationObjModel> outStationList = basCrnp.getOutStationList$();
-                    if(outStationList.isEmpty()){
-                        News.info("鍫嗗灈鏈�:{} 鍑哄簱绔欑偣鏈缃�", basCrnp.getCrnNo());
-                        return;
+                StationObjModel outStationObjModel = null;
+                for (StationObjModel stationObjModel : outStationList) {
+                    if (stationObjModel.getStationId().equals(wrkMast.getSourceStaNo())) {
+                        outStationObjModel = stationObjModel;
+                        break;
                     }
-
-                    StationObjModel outStationObjModel = null;
-                    for (StationObjModel stationObjModel : outStationList) {
-                        if (stationObjModel.getStationId().equals(wrkMast.getSourceStaNo())) {
-                            outStationObjModel = stationObjModel;
-                            break;
-                        }
-                    }
-                    redisUtil.set(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo(), JSON.toJSONString(outStationObjModel, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24);
-                }else if(wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts){
-                    updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
-                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_COMPLETE, null);
-                }else{
-                    News.error("鍫嗗灈鏈哄浜庣瓑寰呯‘璁や笖浠诲姟瀹屾垚鐘舵�侊紝浣嗗伐浣滅姸鎬佸紓甯搞�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
-                    continue;
                 }
-
-                wrkMast.setWrkSts(updateWrkSts);
-                wrkMast.setSystemMsg("");
-                wrkMast.setIoTime(new Date());
-                if (wrkMastService.updateById(wrkMast)) {
-                    CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo());
-                    MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand));
-                    News.info("鍫嗗灈鏈轰换鍔$姸鎬佹洿鏂版垚鍔燂紝鍫嗗灈鏈哄彿={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
-                }
-
-                redisUtil.set(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo(), "lock",10);
+                redisUtil.set(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo(), JSON.toJSONString(outStationObjModel, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24);
+            }else if(wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts){
+                updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
+                notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_COMPLETE, null);
+            }else if(wrkMast.getWrkSts() == WrkStsType.CRN_MOVE_RUN.sts){
+                updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
+            }else{
+                News.error("鍫嗗灈鏈哄浜庣瓑寰呯‘璁や笖浠诲姟瀹屾垚鐘舵�侊紝浣嗗伐浣滅姸鎬佸紓甯搞�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
+                return;
             }
+
+            wrkMast.setWrkSts(updateWrkSts);
+            wrkMast.setSystemMsg("");
+            wrkMast.setIoTime(now);
+            wrkMast.setModiTime(now);
+            if (wrkMastService.updateById(wrkMast)) {
+                wrkAnalysisService.markCraneComplete(wrkMast, now, updateWrkSts);
+                CrnCommand resetCommand = crnThread.getResetCommand(crnProtocol.getTaskNo(), crnProtocol.getCrnNo());
+                MessageQueue.offer(SlaveType.Crn, crnProtocol.getCrnNo(), new Task(2, resetCommand));
+                News.info("鍫嗗灈鏈轰换鍔$姸鎬佹洿鏂版垚鍔燂紝鍫嗗灈鏈哄彿={}锛屽伐浣滃彿={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
+            }
+
+            redisUtil.set(RedisKeyType.CRN_IO_EXECUTE_FINISH_LIMIT.key + basCrnp.getCrnNo(), "lock",10);
         }
     }
 
     public synchronized void plannerExecute() {
-        int nowSec = (int) (System.currentTimeMillis() / 1000);
+        Set<Integer> crnMoveBlockedCrnNos = executeCrnMoveTask();
         List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
         for (BasCrnp basCrnp : basCrnps) {
+            plannerExecute(basCrnp, crnMoveBlockedCrnNos);
+        }
+    }
+
+    private void plannerExecute(BasCrnp basCrnp, Set<Integer> crnMoveBlockedCrnNos) {
+        int nowSec = (int) (System.currentTimeMillis() / 1000);
+        if (basCrnp == null || basCrnp.getCrnNo() == null || crnMoveBlockedCrnNos.contains(basCrnp.getCrnNo())) {
+            return;
+        }
             String key = RedisKeyType.PLANNER_SCHEDULE.key + "CRN-" + basCrnp.getCrnNo();
             List<Object> items = redisUtil.lGet(key, 0, -1);
             if (items == null || items.isEmpty()) {
-                continue;
+                return;
             }
 
             CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
             if (crnThread == null) {
-                continue;
+                return;
             }
             CrnProtocol crnProtocol = crnThread.getStatus();
             if (crnProtocol == null) {
-                continue;
+                return;
             }
             List<WrkMast> running = wrkMastService.list(new QueryWrapper<WrkMast>()
                     .eq("crn_no", basCrnp.getCrnNo())
-                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts)
+                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts, WrkStsType.CRN_MOVE_RUN.sts)
             );
             if (!running.isEmpty()) {
-                continue;
+                return;
             }
             if (!(crnProtocol.getMode() == CrnModeType.AUTO.id
                     && crnProtocol.getTaskNo() == 0
@@ -866,7 +768,7 @@
                     && crnProtocol.getLoaded() == 0
                     && crnProtocol.getForkPos() == 0
                     && crnProtocol.getAlarm() == 0)) {
-                continue;
+                return;
             }
 
             for (Object v : items) {
@@ -929,7 +831,6 @@
                     }
                 }
             }
-        }
     }
 
     private synchronized boolean crnExecuteMovePlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
@@ -970,17 +871,175 @@
 
         CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
 
+        Date now = new Date();
         wrkMast.setWrkSts(WrkStsType.LOC_MOVE_RUN.sts);
         wrkMast.setCrnNo(crnNo);
         wrkMast.setSystemMsg("");
-        wrkMast.setIoTime(new Date());
+        wrkMast.setIoTime(now);
+        wrkMast.setModiTime(now);
         if (wrkMastService.updateById(wrkMast)) {
+            wrkAnalysisService.markCraneStart(wrkMast, now);
             MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, command));
             notifyUtils.notify(String.valueOf(SlaveType.Crn), crnNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_RUN, null);
             News.info("鍫嗗灈鏈哄懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(command));
             return true;
         }
         return false;
+    }
+
+    private Set<Integer> executeCrnMoveTask() {
+        List<WrkMast> pendingTaskQueue = wrkMastService.list(new QueryWrapper<WrkMast>()
+                .eq("io_type", WrkIoType.CRN_MOVE.id)
+                .eq("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts)
+                .orderByAsc("appe_time")
+                .orderByAsc("wrk_no"));
+        Set<Integer> blockedCrnNoSet = new HashSet<>();
+        for (WrkMast wrkMast : pendingTaskQueue) {
+            if (wrkMast != null && wrkMast.getCrnNo() != null) {
+                blockedCrnNoSet.add(wrkMast.getCrnNo());
+            }
+        }
+        if (blockedCrnNoSet.isEmpty()) {
+            return blockedCrnNoSet;
+        }
+
+        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
+        Map<Integer, CrnThread> dispatchThreadMap = new HashMap<>();
+        Map<Integer, CrnProtocol> dispatchProtocolMap = new HashMap<>();
+        for (BasCrnp basCrnp : basCrnps) {
+            if (basCrnp == null || basCrnp.getCrnNo() == null) {
+                continue;
+            }
+
+            Integer crnNo = basCrnp.getCrnNo();
+            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
+            if (crnThread == null) {
+                continue;
+            }
+
+            CrnProtocol crnProtocol = crnThread.getStatus();
+            if (crnProtocol == null) {
+                continue;
+            }
+
+            long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
+                    .eq("crn_no", crnNo)
+                    .in("wrk_sts",
+                            WrkStsType.INBOUND_RUN.sts,
+                            WrkStsType.OUTBOUND_RUN.sts,
+                            WrkStsType.LOC_MOVE_RUN.sts,
+                            WrkStsType.CRN_MOVE_RUN.sts));
+            if (runningCount > 0) {
+                continue;
+            }
+
+            if (!Objects.equals(crnProtocol.getMode(), CrnModeType.AUTO.id)
+                    || !Objects.equals(crnProtocol.getTaskNo(), 0)
+                    || !Objects.equals(crnProtocol.getStatus(), CrnStatusType.IDLE.id)
+                    || !Objects.equals(crnProtocol.getLoaded(), 0)
+                    || !Objects.equals(crnProtocol.getForkPos(), 0)
+                    || !Objects.equals(crnProtocol.getAlarm(), 0)) {
+                continue;
+            }
+
+            Object clearLock = redisUtil.get(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + crnNo);
+            if (clearLock != null) {
+                continue;
+            }
+
+            dispatchThreadMap.put(crnNo, crnThread);
+            dispatchProtocolMap.put(crnNo, crnProtocol);
+        }
+
+        if (dispatchThreadMap.isEmpty()) {
+            return blockedCrnNoSet;
+        }
+
+        List<WrkMast> taskQueue = wrkMastService.list(new QueryWrapper<WrkMast>()
+                .in("crn_no", new ArrayList<>(dispatchThreadMap.keySet()))
+                .eq("io_type", WrkIoType.CRN_MOVE.id)
+                .eq("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts)
+                .orderByAsc("appe_time")
+                .orderByAsc("wrk_no"));
+
+        for (WrkMast wrkMast : taskQueue) {
+            if (wrkMast == null || wrkMast.getCrnNo() == null || Cools.isEmpty(wrkMast.getLocNo())) {
+                continue;
+            }
+
+            Integer crnNo = wrkMast.getCrnNo();
+            CrnThread crnThread = dispatchThreadMap.get(crnNo);
+            if (crnThread == null || dispatchProtocolMap.get(crnNo) == null) {
+                continue;
+            }
+
+            CrnCommand moveCommand = crnThread.getMoveCommand(wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
+            if (moveCommand == null) {
+                continue;
+            }
+
+            Date now = new Date();
+            wrkMast.setWrkSts(WrkStsType.CRN_MOVE_RUN.sts);
+            wrkMast.setCrnNo(crnNo);
+            wrkMast.setSystemMsg("");
+            wrkMast.setIoTime(now);
+            wrkMast.setModiTime(now);
+            if (wrkMastService.updateById(wrkMast)) {
+                wrkAnalysisService.markCraneStart(wrkMast, now);
+                MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, moveCommand));
+                News.info("鍫嗗灈鏈虹Щ鍔ㄥ懡浠や笅鍙戞垚鍔燂紝鍫嗗灈鏈哄彿={}锛屽伐浣滃彿={}锛岀洰鏍囦綅={}锛屼换鍔℃暟鎹�={}",
+                        crnNo, wrkMast.getWrkNo(), wrkMast.getLocNo(), JSON.toJSON(moveCommand));
+                return blockedCrnNoSet;
+            }
+        }
+        return blockedCrnNoSet;
+    }
+
+    public Set<Integer> executeCrnMoveTaskAndGetBlockedCrnNos() {
+        return executeCrnMoveTask();
+    }
+
+    public void submitCrnIoTasks(long minIntervalMs) {
+        submitCrnIoTasks(MainProcessLane.CRN_IO, minIntervalMs);
+    }
+
+    public void submitCrnIoTasks(MainProcessLane lane, long minIntervalMs) {
+        Set<Integer> blockedCrnNos = executeCrnMoveTaskAndGetBlockedCrnNos();
+        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
+        for (BasCrnp basCrnp : basCrnps) {
+            Integer crnNo = basCrnp == null ? null : basCrnp.getCrnNo();
+            if (crnNo == null) {
+                continue;
+            }
+            mainProcessTaskSubmitter.submitKeyedSerialTask(
+                    lane,
+                    crnNo,
+                    "crnIoExecute",
+                    minIntervalMs,
+                    () -> crnIoExecute(basCrnp, blockedCrnNos)
+            );
+        }
+    }
+
+    public void submitCrnIoExecuteFinishTasks(long minIntervalMs) {
+        submitCrnIoExecuteFinishTasks(MainProcessLane.CRN_IO_FINISH, minIntervalMs);
+    }
+
+    public void submitCrnIoExecuteFinishTasks(MainProcessLane lane, long minIntervalMs) {
+        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
+        for (BasCrnp basCrnp : basCrnps) {
+            Integer crnNo = basCrnp == null ? null : basCrnp.getCrnNo();
+            if (crnNo == null) {
+                continue;
+            }
+            mainProcessTaskSubmitter.submitKeyedSerialTask(
+                    lane,
+                    crnNo,
+                    "crnIoExecuteFinish",
+                    minIntervalMs,
+                    () -> crnIoExecuteFinish(basCrnp)
+            );
+        }
     }
 
     //妫�娴嬫祬搴撲綅鐘舵��
@@ -1045,4 +1104,83 @@
         return false;
     }
 
+    //璋冨害鍫嗗灈鏈虹Щ鍔�
+    public synchronized boolean dispatchCrnMove(Integer crnNo, String targetLocNo) {
+        if (crnNo == null || Cools.isEmpty(targetLocNo)) {
+            return false;
+        }
+
+        int targetRow = Utils.getRow(targetLocNo);
+        int targetBay = Utils.getBay(targetLocNo);
+        int targetLev = Utils.getLev(targetLocNo);
+        try {
+            targetRow = Utils.getRow(targetLocNo);
+            targetBay = Utils.getBay(targetLocNo);
+            targetLev = Utils.getLev(targetLocNo);
+        } catch (Exception e) {
+            News.error("鐢熸垚鍫嗗灈鏈虹Щ鍔ㄤ换鍔″け璐ワ紝鐩爣浣�:{} 瑙f瀽寮傚父", targetLocNo);
+            return false;
+        }
+
+        CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
+        if (crnThread == null) {
+            return false;
+        }
+
+        CrnProtocol crnProtocol = crnThread.getStatus();
+        if (crnProtocol == null) {
+            return false;
+        }
+
+        if (crnProtocol.getBay() == targetBay) {
+            return false;
+        }
+
+        if (crnProtocol.getLevel() == targetLev) {
+            return false;
+        }
+
+        long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
+                .eq("crn_no", crnNo)
+                .in("wrk_sts",
+                        WrkStsType.INBOUND_RUN.sts,
+                        WrkStsType.OUTBOUND_RUN.sts,
+                        WrkStsType.LOC_MOVE_RUN.sts,
+                        WrkStsType.CRN_MOVE_RUN.sts));
+        if (runningCount > 0) {
+            News.info("鍫嗗灈鏈�:{} 瀛樺湪鎵ц涓殑浠诲姟锛屾殏涓嶇敓鎴愮Щ鍔ㄤ换鍔�", crnNo);
+            return false;
+        }
+
+        WrkMast activeTask = wrkMastService.getOne(new QueryWrapper<WrkMast>()
+                .eq("crn_no", crnNo)
+                .eq("io_type", WrkIoType.CRN_MOVE.id)
+                .in("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts, WrkStsType.CRN_MOVE_RUN.sts, WrkStsType.CRN_MOVE_MANUAL.sts)
+                .orderByDesc("appe_time")
+                .last("limit 1"));
+        if (activeTask != null) {
+            News.info("鍫嗗灈鏈�:{} 宸插瓨鍦ㄦ湭瀹屾垚绉诲姩浠诲姟锛屽伐浣滃彿={}", crnNo, activeTask.getWrkNo());
+            return false;
+        }
+
+        Date now = new Date();
+        WrkMast wrkMast = new WrkMast();
+        wrkMast.setWrkNo(commonService.getWorkNo(WrkIoType.CRN_MOVE.id));
+        wrkMast.setIoTime(now);
+        wrkMast.setWrkSts(WrkStsType.NEW_CRN_MOVE.sts);
+        wrkMast.setIoType(WrkIoType.CRN_MOVE.id);
+        wrkMast.setIoPri(0D);
+        wrkMast.setLocNo(targetLocNo);
+        wrkMast.setCrnNo(crnNo);
+        wrkMast.setAppeTime(now);
+        wrkMast.setModiTime(now);
+        wrkMast.setMemo("dispatchCrnMove");
+        if (!wrkMastService.save(wrkMast)) {
+            News.info("鐢熸垚鍫嗗灈鏈虹Щ鍔ㄤ换鍔″け璐ワ紝宸ヤ綔鍙�={}锛屼换鍔℃暟鎹�={}", wrkMast.getWrkNo(), JSON.toJSON(wrkMast));
+            return false;
+        }
+        wrkAnalysisService.initForTask(wrkMast);
+        return true;
+    }
+
 }

--
Gitblit v1.9.1