From f5e49b2c04137fdc88c453cbe5d190cf7daab079 Mon Sep 17 00:00:00 2001
From: Junjie <DELL@qq.com>
Date: 星期二, 30 十二月 2025 15:39:53 +0800
Subject: [PATCH] #

---
 src/main/java/com/zy/core/utils/StationOperateProcessUtils.java   |   27 
 src/main/resources/mapper/WrkMastLogMapper.xml                    |    5 
 src/main/webapp/views/wrkMast/wrkMast.html                        |    2 
 src/main/java/com/zy/asrs/controller/DualCrnController.java       |   10 
 src/main/java/com/zy/core/network/fake/ZyDualCrnFakeConnect.java  |    8 
 src/main/java/com/zy/asrs/entity/BasDualCrnp.java                 |   47 ++
 src/main/java/com/zy/asrs/entity/WrkMast.java                     |    7 
 src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java   |  495 ++++++++++++++++++++++++
 src/main/java/com/zy/core/utils/WmsOperateUtils.java              |   63 ++
 src/main/resources/mapper/WrkMastMapper.xml                       |    1 
 src/main/java/com/zy/asrs/entity/WrkMastLog.java                  |    8 
 src/main/java/com/zy/asrs/entity/BasCrnp.java                     |    1 
 src/main/webapp/views/basDualCrnp/basDualCrnp.html                |   14 
 src/main/java/com/zy/asrs/utils/Utils.java                        |  131 ++++++
 src/main/webapp/static/js/wrkMastLog/wrkMastLog.js                |    1 
 src/main/java/com/zy/core/enums/RedisKeyType.java                 |    5 
 src/main/java/com/zy/common/entity/FindCrnNoResult.java           |   13 
 src/main/webapp/static/js/basDualCrnp/basDualCrnp.js              |    2 
 src/main/java/com/zy/common/service/CommonService.java            |  156 +++++--
 src/main/java/com/zy/core/thread/impl/ZySiemensDualCrnThread.java |   92 ++++
 src/main/java/com/zy/core/plugin/FakeProcess.java                 |  107 +++-
 21 files changed, 1,063 insertions(+), 132 deletions(-)

diff --git a/src/main/java/com/zy/asrs/controller/DualCrnController.java b/src/main/java/com/zy/asrs/controller/DualCrnController.java
index 599eb45..7262f27 100644
--- a/src/main/java/com/zy/asrs/controller/DualCrnController.java
+++ b/src/main/java/com/zy/asrs/controller/DualCrnController.java
@@ -103,7 +103,7 @@
             return R.error("绾跨▼涓嶅瓨鍦�");
         }
         DualCrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, targetLocNo, 9999, crnNo, station);
-        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, command));
+        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command));
         return R.ok();
     }
 
@@ -121,7 +121,7 @@
             return R.error("绾跨▼涓嶅瓨鍦�");
         }
         DualCrnCommand command = crnThread.getPickCommand(targetLocNo, 9999, crnNo, station);
-        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, command));
+        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command));
         return R.ok();
     }
 
@@ -139,7 +139,7 @@
             return R.error("绾跨▼涓嶅瓨鍦�");
         }
         DualCrnCommand command = crnThread.getPutCommand(targetLocNo, 9999, crnNo, station);
-        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, command));
+        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command));
         return R.ok();
     }
     
@@ -161,7 +161,7 @@
         if (station != null) {
             command.setStation(station.shortValue());
         }
-        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, command));
+        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command));
         return R.ok();
     }
 
@@ -178,7 +178,7 @@
             return R.error("绾跨▼涓嶅瓨鍦�");
         }
         DualCrnCommand command = crnThread.getResetCommand(crnNo, station);
-        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, command));
+        MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(3, command));
         return R.ok();
     }
 }
diff --git a/src/main/java/com/zy/asrs/entity/BasCrnp.java b/src/main/java/com/zy/asrs/entity/BasCrnp.java
index 66d3515..8a24879 100644
--- a/src/main/java/com/zy/asrs/entity/BasCrnp.java
+++ b/src/main/java/com/zy/asrs/entity/BasCrnp.java
@@ -6,7 +6,6 @@
 import com.baomidou.mybatisplus.annotations.TableId;
 import com.baomidou.mybatisplus.enums.IdType;
 import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.annotations.TableField;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
diff --git a/src/main/java/com/zy/asrs/entity/BasDualCrnp.java b/src/main/java/com/zy/asrs/entity/BasDualCrnp.java
index 74f1027..c4feaf8 100644
--- a/src/main/java/com/zy/asrs/entity/BasDualCrnp.java
+++ b/src/main/java/com/zy/asrs/entity/BasDualCrnp.java
@@ -1,10 +1,17 @@
 package com.zy.asrs.entity;
 
-import com.core.common.Cools;import com.baomidou.mybatisplus.annotations.TableId;
+import com.core.common.Cools;
+import com.zy.core.model.StationObjModel;
+import com.baomidou.mybatisplus.annotations.TableId;
 import com.baomidou.mybatisplus.enums.IdType;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
 import com.baomidou.mybatisplus.annotations.TableField;
 import java.text.SimpleDateFormat;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.List;
+
 import org.springframework.format.annotation.DateTimeFormat;
 
 import io.swagger.annotations.ApiModelProperty;
@@ -195,5 +202,43 @@
         return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime);
     }
 
+    public List<StationObjModel> getInStationList$(){
+        List<StationObjModel> list = new ArrayList<>();
+        if (Cools.isEmpty(this.inStationList)){
+            return list;
+        }
+
+        List<StationObjModel> jsonList = JSON.parseArray(this.inStationList,StationObjModel.class);
+        for (StationObjModel json : jsonList){
+            list.add(json);
+        }
+        return list;
+    }
+
+    public List<StationObjModel> getOutStationList$(){
+        List<StationObjModel> list = new ArrayList<>();
+        if (Cools.isEmpty(this.outStationList)){
+            return list;
+        }
+
+        List<StationObjModel> jsonList = JSON.parseArray(this.outStationList,StationObjModel.class);
+        for (StationObjModel json : jsonList){
+            list.add(json);
+        }
+        return list;
+    }
+
+    public List<List<Integer>> getControlRows$(){
+        List<List<Integer>> rowList = new ArrayList<>();
+        if(Cools.isEmpty(this.controlRows)){
+            return rowList;
+        }
+        JSONArray list = JSON.parseArray(this.controlRows);
+        for (Object o : list) {
+            List<Integer> rows = JSON.parseArray(JSON.toJSONString(o), Integer.class);
+            rowList.add(rows);
+        }
+        return rowList;
+    }
 
 }
diff --git a/src/main/java/com/zy/asrs/entity/WrkMast.java b/src/main/java/com/zy/asrs/entity/WrkMast.java
index b4d78ed..20b6591 100644
--- a/src/main/java/com/zy/asrs/entity/WrkMast.java
+++ b/src/main/java/com/zy/asrs/entity/WrkMast.java
@@ -148,6 +148,13 @@
     private Integer crnNo;
 
     /**
+     * 鍙屽伐浣嶅爢鍨涙満鍙�
+     */
+    @ApiModelProperty(value= "鍙屽伐浣嶅爢鍨涙満鍙�")
+    @TableField(value = "dual_crn_no",strategy = FieldStrategy.IGNORED)
+    private Integer dualCrnNo;
+
+    /**
      * RGV鍙�
      */
     @ApiModelProperty(value= "RGV鍙�")
diff --git a/src/main/java/com/zy/asrs/entity/WrkMastLog.java b/src/main/java/com/zy/asrs/entity/WrkMastLog.java
index 2d87a39..aa9b739 100644
--- a/src/main/java/com/zy/asrs/entity/WrkMastLog.java
+++ b/src/main/java/com/zy/asrs/entity/WrkMastLog.java
@@ -3,7 +3,6 @@
 import com.baomidou.mybatisplus.annotations.TableField;
 import com.baomidou.mybatisplus.annotations.TableId;
 import com.baomidou.mybatisplus.annotations.TableName;
-import com.baomidou.mybatisplus.enums.FieldStrategy;
 import com.baomidou.mybatisplus.enums.IdType;
 import com.core.common.Cools;
 import com.core.common.SpringUtils;
@@ -153,6 +152,13 @@
     @TableField(value = "crn_no")
     private Integer crnNo;
 
+     /**
+     * 鍙屽伐浣嶅爢鍨涙満鍙�
+     */
+    @ApiModelProperty(value= "鍙屽伐浣嶅爢鍨涙満鍙�")
+    @TableField(value = "dual_crn_no")
+    private Integer dualCrnNo;
+
     /**
      * RGV鍙�
      */
diff --git a/src/main/java/com/zy/asrs/utils/Utils.java b/src/main/java/com/zy/asrs/utils/Utils.java
index 1222a32..bb645ef 100644
--- a/src/main/java/com/zy/asrs/utils/Utils.java
+++ b/src/main/java/com/zy/asrs/utils/Utils.java
@@ -10,18 +10,22 @@
 import com.core.common.SpringUtils;
 import com.core.exception.CoolException;
 import com.zy.asrs.entity.BasCrnp;
+import com.zy.asrs.entity.BasDualCrnp;
 import com.zy.asrs.entity.WrkMast;
 import com.zy.asrs.service.BasCrnpService;
+import com.zy.asrs.service.BasDualCrnpService;
 import com.zy.asrs.service.WrkMastService;
+import com.zy.common.model.NavigateNode;
+import com.zy.common.utils.NavigateUtils;
 import com.zy.common.utils.RedisUtil;
 import com.zy.core.News;
 import com.zy.core.cache.SlaveConnection;
-import com.zy.core.enums.CrnModeType;
-import com.zy.core.enums.RedisKeyType;
-import com.zy.core.enums.SlaveType;
-import com.zy.core.enums.WrkIoType;
+import com.zy.core.enums.*;
+import com.zy.core.model.StationObjModel;
 import com.zy.core.model.protocol.CrnProtocol;
+import com.zy.core.model.protocol.DualCrnProtocol;
 import com.zy.core.thread.CrnThread;
+import com.zy.core.thread.DualCrnThread;
 
 import java.lang.reflect.Field;
 import java.text.DecimalFormat;
@@ -140,15 +144,28 @@
     }
 
     //鑾峰彇鍏ュ簱浠诲姟鍙敤鎺�
-    public static List<Integer> getInTaskEnableRow() {
-        return getInTaskEnableRow(new ArrayList<>(), true);
+    public static List<Integer> getInTaskEnableRow(Integer stationId) {
+        return getInTaskEnableRow(stationId, new ArrayList<>(), new ArrayList<>(), true);
     }
 
     //鑾峰彇鍏ュ簱浠诲姟鍙敤鎺�
-    public static List<Integer> getInTaskEnableRow(List<Integer> excludeCrnList, boolean maxInTaskControl) {
+    public static List<Integer> getInTaskEnableRow(Integer stationId, List<Integer> excludeCrnList, List<Integer> excludeDualCrnList, boolean maxInTaskControl) {
+        //鑾峰彇鍫嗗灈鏈哄叆搴撲换鍔″彲鐢ㄦ帓
+        List<Integer> rowList = getCrnInTaskEnableRow(stationId, excludeCrnList, maxInTaskControl);
+        //鑾峰彇鍙屽伐浣嶅爢鍨涙満鍏ュ簱浠诲姟鍙敤鎺�
+        List<Integer> dualRowList = getDualCrnInTaskEnableRow(stationId, excludeDualCrnList, maxInTaskControl);
+        if (!dualRowList.isEmpty()) {
+            rowList.addAll(dualRowList);
+        }
+        return rowList;
+    }
+
+    //鑾峰彇鍫嗗灈鏈哄叆搴撲换鍔″彲鐢ㄦ帓
+    public static List<Integer> getCrnInTaskEnableRow(Integer stationId, List<Integer> excludeCrnList, boolean maxInTaskControl) {
         List<Integer> list = new ArrayList<>();
         try {
             RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
+            NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class);
             WrkMastService wrkMastService = SpringUtils.getBean(WrkMastService.class);
             BasCrnpService basCrnpService = SpringUtils.getBean(BasCrnpService.class);
 
@@ -209,7 +226,25 @@
                 if (excludeCrnList.contains(basCrnp.getCrnNo())) {
                     continue;
                 }
-                enabledCrnps.add(basCrnp);
+
+                //璁$畻绔欑偣鏄惁鍙揪璇ュ爢鍨涙満
+                List<StationObjModel> inStationList = basCrnp.getInStationList$();
+                boolean enableGo = false;
+                for (StationObjModel stationObjModel : inStationList) {
+                    try {
+                        List<NavigateNode> navigateNodes = navigateUtils.calcByStationId(stationId, stationObjModel.getStationId());
+                        if(navigateNodes != null && !navigateNodes.isEmpty()) {
+                            enableGo = true;
+                            break;
+                        }
+                    } catch (Exception e) {
+
+                    }
+                }
+
+                if (enableGo) {
+                    enabledCrnps.add(basCrnp);
+                }
             }
 
             enabledCrnps.sort(Comparator.comparingInt(o -> map.getOrDefault(o.getCrnNo(), 0)));
@@ -226,6 +261,86 @@
         return list;
     }
 
+    //鑾峰彇鍙屽伐浣嶅爢鍨涙満鍏ュ簱浠诲姟鍙敤鎺�
+    public static List<Integer> getDualCrnInTaskEnableRow(Integer stationId, List<Integer> excludeCrnList, boolean maxInTaskControl) {
+        List<Integer> list = new ArrayList<>();
+        try {
+            RedisUtil redisUtil = SpringUtils.getBean(RedisUtil.class);
+            NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class);
+            WrkMastService wrkMastService = SpringUtils.getBean(WrkMastService.class);
+            BasDualCrnpService basDualCrnpService = SpringUtils.getBean(BasDualCrnpService.class);
+
+            Wrapper<BasDualCrnp> wrapper = new EntityWrapper<BasDualCrnp>()
+                    .eq("in_enable", "Y")
+                    .eq("status", 1);
+
+            HashMap<Integer, Integer> map = new HashMap<>();
+            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<>());
+            List<BasDualCrnp> basDualCrnps = basDualCrnpService.selectList(wrapper);
+
+            for (WrkMast wrkMast : wrkMasts) {
+                Integer dualCrnNo = wrkMast.getDualCrnNo();
+                map.put(dualCrnNo, map.getOrDefault(dualCrnNo, 0) + 1);
+            }
+
+            List<BasDualCrnp> enabledCrnps = new ArrayList<>();
+            for (BasDualCrnp basDualCrnp : basDualCrnps) {
+                DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, basDualCrnp.getCrnNo());
+                if (dualCrnThread == null) {
+                    continue;
+                }
+                DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+                if (dualCrnProtocol.getMode() != DualCrnModeType.AUTO.id) {
+                    continue;
+                }
+
+                List<WrkMast> inWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                        .eq("dual_crn_no", basDualCrnp.getCrnNo())
+                        .eq("io_type", WrkIoType.IN.id)
+                );
+                // 妫�鏌ユ槸鍚﹁秴杩囨渶澶у叆搴撲换鍔℃暟
+                if (maxInTaskControl && inWrkMasts.size() >= basDualCrnp.getMaxInTask()) {
+                    News.info("鍫嗗灈鏈�:{} 宸茶揪鏈�澶у叆搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:{}", basDualCrnp.getCrnNo(), inWrkMasts.size());
+                    continue;
+                }
+                if (excludeCrnList.contains(basDualCrnp.getCrnNo())) {
+                    continue;
+                }
+
+                //璁$畻绔欑偣鏄惁鍙揪璇ュ爢鍨涙満
+                List<StationObjModel> inStationList = basDualCrnp.getInStationList$();
+                boolean enableGo = false;
+                for (StationObjModel stationObjModel : inStationList) {
+                    try {
+                        List<NavigateNode> navigateNodes = navigateUtils.calcByStationId(stationId, stationObjModel.getStationId());
+                        if(navigateNodes != null && !navigateNodes.isEmpty()) {
+                            enableGo = true;
+                            break;
+                        }
+                    } catch (Exception e) {
+
+                    }
+                }
+
+                if (enableGo) {
+                    enabledCrnps.add(basDualCrnp);
+                }
+            }
+
+            enabledCrnps.sort(Comparator.comparingInt(o -> map.getOrDefault(o.getCrnNo(), 0)));
+
+            for (BasDualCrnp basDualCrnp : enabledCrnps) {
+                List<List<Integer>> rowList = basDualCrnp.getControlRows$();
+                for (List<Integer> rows : rowList) {
+                    list.addAll(rows);
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return list;
+    }
+
     public static Map<String, Object> convertObjectToMap(Object obj) {
         Map<String, Object> map = new HashMap<>();
         Class<?> clazz = obj.getClass();
diff --git a/src/main/java/com/zy/common/entity/FindCrnNoResult.java b/src/main/java/com/zy/common/entity/FindCrnNoResult.java
new file mode 100644
index 0000000..38ce336
--- /dev/null
+++ b/src/main/java/com/zy/common/entity/FindCrnNoResult.java
@@ -0,0 +1,13 @@
+package com.zy.common.entity;
+
+import com.zy.core.enums.SlaveType;
+import lombok.Data;
+
+@Data
+public class FindCrnNoResult {
+
+    private Integer crnNo;
+
+    private SlaveType crnType;
+
+}
diff --git a/src/main/java/com/zy/common/service/CommonService.java b/src/main/java/com/zy/common/service/CommonService.java
index 35c7d93..138e46b 100644
--- a/src/main/java/com/zy/common/service/CommonService.java
+++ b/src/main/java/com/zy/common/service/CommonService.java
@@ -9,11 +9,13 @@
 import com.zy.asrs.entity.*;
 import com.zy.asrs.service.*;
 import com.zy.asrs.utils.Utils;
+import com.zy.common.entity.FindCrnNoResult;
 import com.zy.common.model.NavigateNode;
 import com.zy.common.utils.NavigateUtils;
 import com.zy.common.utils.RedisUtil;
 import com.zy.core.News;
 import com.zy.core.enums.RedisKeyType;
+import com.zy.core.enums.SlaveType;
 import com.zy.core.enums.WrkIoType;
 import com.zy.core.enums.WrkStsType;
 import com.zy.core.model.StationObjModel;
@@ -21,6 +23,7 @@
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
 
@@ -36,6 +39,8 @@
     private LocMastService locMastService;
     @Autowired
     private BasCrnpService basCrnpService;
+    @Autowired
+    private BasDualCrnpService basDualCrnpService;
     @Autowired
     private NavigateUtils navigateUtils;
     @Autowired
@@ -138,19 +143,20 @@
             ioPri = param.getTaskPri().doubleValue();
         }
 
-        Integer sourceCrnNo = this.findCrnNoByLocNo(sourceLocMast.getLocNo());
-        if (sourceCrnNo == null) {
+        FindCrnNoResult sourceCrnResult = this.findCrnNoByLocNo(sourceLocMast.getLocNo());
+        if (sourceCrnResult == null) {
             throw new CoolException("鏈壘鍒板搴斿爢鍨涙満");
         }
 
-        Integer crnNo = this.findCrnNoByLocNo(locMast.getLocNo());
-        if (crnNo == null) {
+        FindCrnNoResult targetCrnResult = this.findCrnNoByLocNo(locMast.getLocNo());
+        if (targetCrnResult == null) {
             throw new CoolException("鏈壘鍒板搴斿爢鍨涙満");
         }
 
-        if (!sourceCrnNo.equals(crnNo)) {
+        if (!sourceCrnResult.getCrnNo().equals(targetCrnResult.getCrnNo())) {
             throw new CoolException("婧愬簱浣嶅拰鐩爣搴撲綅涓嶅湪鍚屼竴宸烽亾");
         }
+        Integer crnNo = targetCrnResult.getCrnNo();
 
         // 鑾峰彇宸ヤ綔鍙�
         int workNo = getWorkNo(WrkIoType.LOC_MOVE.id);
@@ -202,10 +208,11 @@
             ioPri = param.getTaskPri().doubleValue();
         }
 
-        Integer crnNo = this.findCrnNoByLocNo(locMast.getLocNo());
-        if (crnNo == null) {
+        FindCrnNoResult findCrnResult = this.findCrnNoByLocNo(locMast.getLocNo());
+        if (findCrnResult == null) {
             throw new CoolException("鏈壘鍒板搴斿爢鍨涙満");
         }
+        Integer crnNo = findCrnResult.getCrnNo();
 
         // 鑾峰彇宸ヤ綔鍙�
         int workNo = getWorkNo(WrkIoType.IN.id);
@@ -221,9 +228,20 @@
         wrkMast.setStaNo(param.getStaNo());//鐩爣绔�
         wrkMast.setWmsWrkNo(param.getTaskNo());
         wrkMast.setBarcode(param.getBarcode());
-        wrkMast.setCrnNo(crnNo);
         wrkMast.setAppeTime(now);
         wrkMast.setModiTime(now);
+
+        if (findCrnResult.getCrnType().equals(SlaveType.Crn)) {
+            wrkMast.setCrnNo(findCrnResult.getCrnNo());
+
+            //缂撳瓨璁板綍褰撳墠鍛戒护鍫嗗灈鏈虹紪鍙�
+            redisUtil.set(RedisKeyType.CURRENT_CIRCLE_TASK_CRN_NO.key, crnNo, 60 * 60 * 24);
+        } else if (findCrnResult.getCrnType().equals(SlaveType.DualCrn)) {
+            wrkMast.setDualCrnNo(findCrnResult.getCrnNo());
+        }else {
+            throw new CoolException("鏈煡璁惧绫诲瀷");
+        }
+
         boolean res = wrkMastService.insert(wrkMast);
         if (!res) {
             News.error("鍏ュ簱浠诲姟 --- 淇濆瓨宸ヤ綔妗eけ璐ワ紒");
@@ -233,9 +251,6 @@
         locMast.setLocSts("S");
         locMast.setModiTime(new Date());
         locMastService.updateById(locMast);
-
-        //缂撳瓨璁板綍褰撳墠鍛戒护鍫嗗灈鏈虹紪鍙�
-        redisUtil.set(RedisKeyType.CURRENT_CIRCLE_TASK_CRN_NO.key, crnNo, 60 * 60 * 24);
         return wrkMast;
     }
 
@@ -256,28 +271,15 @@
             ioPri = param.getTaskPri().doubleValue();
         }
 
-        Integer crnNo = this.findCrnNoByLocNo(locMast.getLocNo());
-        if (crnNo == null) {
+        FindCrnNoResult findCrnResult = this.findCrnNoByLocNo(locMast.getLocNo());
+        if (findCrnResult == null) {
             throw new CoolException("鏈壘鍒板搴斿爢鍨涙満");
         }
+        Integer crnNo = findCrnResult.getCrnNo();
 
-        Integer sourceStationId = this.findOutStationId(crnNo, param.getStaNo());
+        Integer sourceStationId = this.findOutStationId(findCrnResult, param.getStaNo());
         if (sourceStationId == null) {
             throw new CoolException("鏈壘鍒拌緭閫佺洰鏍囩珯鐐瑰彲璧拌璺緞");
-        }
-
-        BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
-        if(basCrnp == null) {
-            throw new CoolException("鏈壘鍒板搴斿爢鍨涙満鏁版嵁");
-        }
-        List<WrkMast> outWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
-                .eq("crn_no", crnNo)
-                .eq("io_type", WrkIoType.OUT.id)
-        );
-        // 妫�鏌ユ槸鍚﹁秴杩囨渶澶у嚭搴撲换鍔℃暟
-        if(outWrkMasts.size() >= basCrnp.getMaxOutTask()){
-            News.info("鍫嗗灈鏈�:{} 宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:{}", basCrnp.getCrnNo(), outWrkMasts.size());
-            throw new CoolException("鍫嗗灈鏈�:" + basCrnp.getCrnNo() + "宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:" + outWrkMasts.size());
         }
 
         // 鑾峰彇宸ヤ綔鍙�
@@ -294,9 +296,45 @@
         wrkMast.setStaNo(param.getStaNo());//鐩爣绔�
         wrkMast.setWmsWrkNo(param.getTaskNo());
         wrkMast.setBarcode(locMast.getBarcode());
-        wrkMast.setCrnNo(crnNo);
         wrkMast.setAppeTime(now);
         wrkMast.setModiTime(now);
+
+        if (findCrnResult.getCrnType().equals(SlaveType.Crn)) {
+            BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
+            if(basCrnp == null) {
+                throw new CoolException("鏈壘鍒板搴斿爢鍨涙満鏁版嵁");
+            }
+            List<WrkMast> outWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                    .eq("crn_no", crnNo)
+                    .eq("io_type", WrkIoType.OUT.id)
+            );
+            // 妫�鏌ユ槸鍚﹁秴杩囨渶澶у嚭搴撲换鍔℃暟
+            if(outWrkMasts.size() >= basCrnp.getMaxOutTask()){
+                News.info("鍫嗗灈鏈�:{} 宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:{}", basCrnp.getCrnNo(), outWrkMasts.size());
+                throw new CoolException("鍫嗗灈鏈�:" + basCrnp.getCrnNo() + "宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:" + outWrkMasts.size());
+            }
+
+            wrkMast.setCrnNo(findCrnResult.getCrnNo());
+        } else if (findCrnResult.getCrnType().equals(SlaveType.DualCrn)) {
+            BasDualCrnp basDualCrnp = basDualCrnpService.selectOne(new EntityWrapper<BasDualCrnp>().eq("crn_no", crnNo));
+            if(basDualCrnp == null) {
+                throw new CoolException("鏈壘鍒板搴斿弻宸ヤ綅鍫嗗灈鏈烘暟鎹�");
+            }
+            List<WrkMast> outWrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                    .eq("dual_crn_no", crnNo)
+                    .eq("io_type", WrkIoType.OUT.id)
+            );
+            // 妫�鏌ユ槸鍚﹁秴杩囨渶澶у嚭搴撲换鍔℃暟
+            if(outWrkMasts.size() >= basDualCrnp.getMaxOutTask()){
+                News.info("鍙屽伐浣嶅爢鍨涙満:{} 宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:{}", basDualCrnp.getCrnNo(), outWrkMasts.size());
+                throw new CoolException("鍙屽伐浣嶅爢鍨涙満:" + basDualCrnp.getCrnNo() + "宸茶揪鏈�澶у嚭搴撲换鍔℃暟锛屽綋鍓嶄换鍔℃暟:" + outWrkMasts.size());
+            }
+
+            wrkMast.setDualCrnNo(findCrnResult.getCrnNo());
+        }else {
+            throw new CoolException("鏈煡璁惧绫诲瀷");
+        }
+
         boolean res = wrkMastService.insert(wrkMast);
         if (!res) {
             News.error("鍑哄簱浠诲姟 --- 淇濆瓨宸ヤ綔妗eけ璐ワ紒");
@@ -309,27 +347,53 @@
         return true;
     }
 
-    public Integer findCrnNoByLocNo(String locNo) {
+    public FindCrnNoResult findCrnNoByLocNo(String locNo) {
         List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
         for (BasCrnp basCrnp : basCrnps) {
             List<List<Integer>> rowList = basCrnp.getControlRows$();
             for (List<Integer> rows : rowList) {
                 if(rows.contains(Utils.getRow(locNo))) {
-                    return basCrnp.getCrnNo();
+                    FindCrnNoResult result = new FindCrnNoResult();
+                    result.setCrnNo(basCrnp.getCrnNo());
+                    result.setCrnType(SlaveType.Crn);
+                    return result;
+                }
+            }
+        }
+
+        List<BasDualCrnp> basDualCrnps = basDualCrnpService.selectList(new EntityWrapper<>());
+        for (BasDualCrnp basDualCrnp : basDualCrnps) {
+            List<List<Integer>> rowList = basDualCrnp.getControlRows$();
+            for (List<Integer> rows : rowList) {
+                if(rows.contains(Utils.getRow(locNo))) {
+                    FindCrnNoResult result = new FindCrnNoResult();
+                    result.setCrnNo(basDualCrnp.getCrnNo());
+                    result.setCrnType(SlaveType.DualCrn);
+                    return result;
                 }
             }
         }
         return null;
     }
 
-    public Integer findInStationId(Integer crnNo, Integer sourceStationId) {
-        BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
-        if(basCrnp == null) {
-            return null;
+    public Integer findInStationId(FindCrnNoResult findCrnNoResult, Integer sourceStationId) {
+        List<StationObjModel> stationList = new ArrayList<>();
+        Integer crnNo = findCrnNoResult.getCrnNo();
+        if (findCrnNoResult.getCrnType().equals(SlaveType.Crn)) {
+            BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
+            if(basCrnp == null) {
+                return null;
+            }
+            stationList = basCrnp.getInStationList$();
+        } else if (findCrnNoResult.getCrnType().equals(SlaveType.DualCrn)) {
+            BasDualCrnp basDualCrnp = basDualCrnpService.selectOne(new EntityWrapper<BasDualCrnp>().eq("crn_no", crnNo));
+            if(basDualCrnp == null) {
+                return null;
+            }
+            stationList = basDualCrnp.getInStationList$();
         }
 
         Integer targetStationId = null;
-        List<StationObjModel> stationList = basCrnp.getInStationList$();
         for (StationObjModel stationObjModel : stationList) {
             try {
                 List<NavigateNode> navigateNodes = navigateUtils.calcByStationId(sourceStationId, stationObjModel.getStationId());
@@ -344,14 +408,24 @@
         return targetStationId;
     }
 
-    public Integer findOutStationId(Integer crnNo, Integer targetStationId) {
-        BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
-        if(basCrnp == null) {
-            return null;
+    public Integer findOutStationId(FindCrnNoResult findCrnNoResult, Integer targetStationId) {
+        List<StationObjModel> stationList = new ArrayList<>();
+        Integer crnNo = findCrnNoResult.getCrnNo();
+        if (findCrnNoResult.getCrnType().equals(SlaveType.Crn)) {
+            BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
+            if(basCrnp == null) {
+                return null;
+            }
+            stationList = basCrnp.getOutStationList$();
+        } else if (findCrnNoResult.getCrnType().equals(SlaveType.DualCrn)) {
+            BasDualCrnp basDualCrnp = basDualCrnpService.selectOne(new EntityWrapper<BasDualCrnp>().eq("crn_no", crnNo));
+            if(basDualCrnp == null) {
+                return null;
+            }
+            stationList = basDualCrnp.getOutStationList$();
         }
 
         Integer finalSourceStationId = null;
-        List<StationObjModel> stationList = basCrnp.getOutStationList$();
         for (StationObjModel stationObjModel : stationList) {
             try {
                 List<NavigateNode> navigateNodes = navigateUtils.calcByStationId(stationObjModel.getStationId(), targetStationId);
diff --git a/src/main/java/com/zy/core/enums/RedisKeyType.java b/src/main/java/com/zy/core/enums/RedisKeyType.java
index 4e0b75d..42dcfac 100644
--- a/src/main/java/com/zy/core/enums/RedisKeyType.java
+++ b/src/main/java/com/zy/core/enums/RedisKeyType.java
@@ -9,6 +9,8 @@
     LIFT_WORK_FLAG("lift_wrk_no_"),
     LIFT_FLAG("lift_"),
 
+    DUAL_CRN_COMMAND_("dual_crn_command_"),
+
     QUEUE_CRN("queue_crn_"),
     QUEUE_DEVP("queue_devp_"),
     QUEUE_RGV("queue_rgv_"),
@@ -31,11 +33,14 @@
     CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT("check_out_station_stay_time_out_limit_"),
     CHECK_IN_STATION_STAY_TIME_OUT_LIMIT("check_in_station_stay_time_out_limit_"),
     CRN_IO_EXECUTE_FINISH_LIMIT("crn_io_execute_finish_limit_"),
+    DUAL_CRN_IO_EXECUTE_FINISH_LIMIT("dual_crn_io_execute_finish_limit_"),
     STATION_IN_EXECUTE_LIMIT("station_in_execute_limit_"),
     STATION_OUT_EXECUTE_LIMIT("station_out_execute_limit_"),
     CHECK_STATION_RUN_BLOCK_LIMIT_("check_station_run_block_limit_"),
     CHECK_SHALLOW_LOC_STATUS_LIMIT("check_shallow_loc_status_limit_"),
 
+    DUAL_CRN_PICK_WAIT_NEXT_TASK("dual_crn_pick_wait_next_task_"),
+
     CURRENT_CIRCLE_TASK_CRN_NO("current_circle_task_crn_no_"),
     AI_CHAT_HISTORY("ai_chat_history_"),
     AI_CHAT_META("ai_chat_meta_"),
diff --git a/src/main/java/com/zy/core/network/fake/ZyDualCrnFakeConnect.java b/src/main/java/com/zy/core/network/fake/ZyDualCrnFakeConnect.java
index c19df2a..0d25fed 100644
--- a/src/main/java/com/zy/core/network/fake/ZyDualCrnFakeConnect.java
+++ b/src/main/java/com/zy/core/network/fake/ZyDualCrnFakeConnect.java
@@ -64,11 +64,15 @@
 
     private void commandTaskComplete(DualCrnCommand command) {
         if(command.getStation() == 1) {
-            this.crnStatus.setTaskNo(0);
+            if (crnStatus.getLoaded() == 0) {
+                this.crnStatus.setTaskNo(0);
+            }
             this.crnStatus.setTaskReceive(0);
             this.crnStatus.setStatus(CrnStatusType.IDLE.id);
         }else {
-            this.crnStatus.setTaskNoTwo(0);
+            if (crnStatus.getLoadedTwo() == 0) {
+                this.crnStatus.setTaskNoTwo(0);
+            }
             this.crnStatus.setTaskReceiveTwo(0);
             this.crnStatus.setStatusTwo(CrnStatusType.IDLE.id);
         }
diff --git a/src/main/java/com/zy/core/plugin/FakeProcess.java b/src/main/java/com/zy/core/plugin/FakeProcess.java
index f202acc..7aad282 100644
--- a/src/main/java/com/zy/core/plugin/FakeProcess.java
+++ b/src/main/java/com/zy/core/plugin/FakeProcess.java
@@ -8,6 +8,7 @@
 import com.zy.asrs.domain.param.CreateOutTaskParam;
 import com.zy.asrs.entity.*;
 import com.zy.asrs.service.*;
+import com.zy.common.entity.FindCrnNoResult;
 import com.zy.common.model.StartupDto;
 import com.zy.common.service.CommonService;
 import com.zy.common.utils.RedisUtil;
@@ -20,11 +21,14 @@
 import com.zy.core.model.command.CrnCommand;
 import com.zy.core.model.command.StationCommand;
 import com.zy.core.model.protocol.CrnProtocol;
+import com.zy.core.model.protocol.DualCrnProtocol;
 import com.zy.core.model.protocol.StationProtocol;
 import com.zy.core.properties.SystemProperties;
 import com.zy.core.thread.CrnThread;
+import com.zy.core.thread.DualCrnThread;
 import com.zy.core.thread.StationThread;
 import com.zy.core.utils.CrnOperateProcessUtils;
+import com.zy.core.utils.DualCrnOperateProcessUtils;
 import com.zy.core.utils.StationOperateProcessUtils;
 import com.zy.core.utils.WmsOperateUtils;
 import com.zy.system.entity.Config;
@@ -61,6 +65,8 @@
     @Autowired
     private BasCrnpService basCrnpService;
     @Autowired
+    private BasDualCrnpService basDualCrnpService;
+    @Autowired
     private RedisUtil redisUtil;
     @Autowired
     private CrnOperateProcessUtils crnOperateUtils;
@@ -68,6 +74,8 @@
     private StationOperateProcessUtils stationOperateProcessUtils;
     @Autowired
     private WmsOperateUtils wmsOperateUtils;
+    @Autowired
+    private DualCrnOperateProcessUtils dualCrnOperateProcessUtils;
 
     @Override
     public void run() {
@@ -84,6 +92,11 @@
         stationOperateProcessUtils.stationOutExecute();
         //妫�娴嬭緭閫佺珯鐐瑰嚭搴撲换鍔℃墽琛屽畬鎴�
         stationOperateProcessUtils.stationOutExecuteFinish();
+
+        //鎵ц鍙屽伐浣嶅爢鍨涙満浠诲姟
+        dualCrnOperateProcessUtils.dualRrnIoExecute();
+        //鍙屽伐浣嶅爢鍨涙満浠诲姟鎵ц瀹屾垚
+        dualCrnOperateProcessUtils.dualCrnIoExecuteFinish();
     }
 
     public void asyncRun() {
@@ -293,12 +306,12 @@
                     int nextInt = new Random().nextInt(locMastList.size());
                     LocMast locMast = locMastList.get(nextInt);
 
-                    Integer crnNo = commonService.findCrnNoByLocNo(locMast.getLocNo());
-                    if (crnNo == null) {
+                    FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locMast.getLocNo());
+                    if (findCrnNoResult == null) {
                         continue;
                     }
 
-                    Integer targetStationId = commonService.findInStationId(crnNo, stationId);
+                    Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId);
                     if (targetStationId == null) {
                         continue;
                     }
@@ -374,11 +387,6 @@
                     }
 
                     LocMast locMast = locMastList.get(0);
-
-                    Integer crnNo = commonService.findCrnNoByLocNo(locMast.getLocNo());
-                    if (crnNo == null) {
-                        continue;
-                    }
 
                     CreateOutTaskParam taskParam = new CreateOutTaskParam();
                     taskParam.setTaskNo(String.valueOf(commonService.getWorkNo(WrkIoType.OUT.id)));
@@ -541,38 +549,53 @@
                 News.info("鍫嗗灈鏈�:{} 鍏ュ簱绔欑偣鏈缃�", basCrnp.getCrnNo());
                 continue;
             }
+            checkInStationListCrnTake(inStationList);
+        }
 
-            for (StationObjModel stationObjModel : inStationList) {
-                Object lock = redisUtil.get(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId());
-                if(lock != null){
-                    continue;
-                }
+        List<BasDualCrnp> basDualCrnps = basDualCrnpService.selectList(new EntityWrapper<>());
+        for (BasDualCrnp basDualCrnp : basDualCrnps) {
+            List<StationObjModel> inStationList = basDualCrnp.getInStationList$();
+            if(inStationList.isEmpty()){
+                News.info("鍙屽伐浣嶅爢鍨涙満:{} 鍏ュ簱绔欑偣鏈缃�", basDualCrnp.getCrnNo());
+                continue;
+            }
+            checkInStationListCrnTake(inStationList);
+        }
+    }
 
-                StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
-                if(stationThread == null){
-                    continue;
-                }
+    private void checkInStationListCrnTake(List<StationObjModel> inStationList) {
+        for (StationObjModel stationObjModel : inStationList) {
+            Object lock = redisUtil.get(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId());
+            if(lock != null){
+                continue;
+            }
 
-                StationCommand command = stationThread.getMoveCommand(0, stationObjModel.getStationId(), 0, 0);
-                if(command == null){
-                    continue;
-                }
+            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
+            if(stationThread == null){
+                continue;
+            }
 
-                Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
-                StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId());
-                if (stationProtocol == null) {
-                    continue;
-                }
+            StationCommand command = stationThread.getMoveCommand(0, stationObjModel.getStationId(), 0, 0);
+            if(command == null){
+                continue;
+            }
 
-                if(stationProtocol.getTaskNo() > 0) {
-                    WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
-                    if (wrkMast == null) {
-                        MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
-                        redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), "lock",10);
-                        News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(task_over)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(), JSON.toJSONString(command));
-                    }else {
-                        if (wrkMast.getWrkSts() != WrkStsType.NEW_INBOUND.sts && wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) {
-                            Integer crnNo = wrkMast.getCrnNo();
+            Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
+            StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId());
+            if (stationProtocol == null) {
+                continue;
+            }
+
+            if(stationProtocol.getTaskNo() > 0) {
+                WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
+                if (wrkMast == null) {
+                    MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+                    redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), "lock",10);
+                    News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(task_over)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(), JSON.toJSONString(command));
+                }else {
+                    if (wrkMast.getWrkSts() != WrkStsType.NEW_INBOUND.sts && wrkMast.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts) {
+                        Integer crnNo = wrkMast.getCrnNo();
+                        if (crnNo != null) {
                             CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
                             if (crnThread == null) {
                                 continue;
@@ -585,6 +608,20 @@
                             MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
                             redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), "lock",10);
                             News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(crn_fetch)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(), JSON.toJSONString(command));
+                        }else {
+                            Integer dualCrnNo = wrkMast.getDualCrnNo();
+                            DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, dualCrnNo);
+                            if (dualCrnThread == null) {
+                                continue;
+                            }
+                            DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+                            if (!dualCrnProtocol.getStatusType().equals(DualCrnStatusType.PUT_MOVING) && !dualCrnProtocol.getStatusType().equals(DualCrnStatusType.PUTTING)) {
+                                continue;
+                            }
+
+                            MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+                            redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(), "lock",10);
+                            News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(crn_fetch)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(), JSON.toJSONString(command));
                         }
                     }
                 }
diff --git a/src/main/java/com/zy/core/thread/impl/ZySiemensDualCrnThread.java b/src/main/java/com/zy/core/thread/impl/ZySiemensDualCrnThread.java
index 3e354a4..9ac77c5 100644
--- a/src/main/java/com/zy/core/thread/impl/ZySiemensDualCrnThread.java
+++ b/src/main/java/com/zy/core/thread/impl/ZySiemensDualCrnThread.java
@@ -1,6 +1,8 @@
 package com.zy.core.thread.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.baomidou.mybatisplus.mapper.EntityWrapper;
 import com.core.common.DateUtils;
 import com.core.common.SpringUtils;
@@ -12,11 +14,10 @@
 import com.zy.asrs.service.BasDualCrnpService;
 import com.zy.asrs.utils.Utils;
 import com.zy.common.utils.RedisUtil;
+import com.zy.core.News;
 import com.zy.core.cache.MessageQueue;
 import com.zy.core.cache.OutputQueue;
-import com.zy.core.enums.DualCrnTaskModeType;
-import com.zy.core.enums.RedisKeyType;
-import com.zy.core.enums.SlaveType;
+import com.zy.core.enums.*;
 import com.zy.core.model.CommandResponse;
 import com.zy.core.model.Task;
 import com.zy.core.model.command.DualCrnCommand;
@@ -30,6 +31,8 @@
 
 import java.text.MessageFormat;
 import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ThreadFactory;
@@ -51,6 +54,7 @@
     private volatile boolean closed = false;
     private ScheduledExecutorService readExecutor;
     private ScheduledExecutorService processExecutor;
+    private ScheduledExecutorService commandExecutor;
 
     public ZySiemensDualCrnThread(DeviceConfig deviceConfig, RedisUtil redisUtil) {
         this.deviceConfig = deviceConfig;
@@ -102,13 +106,93 @@
                 if (task != null) {
                     step = task.getStep();
                 }
-                if (step == 2 && task != null) {
+
+                if (step == 2) {
+                    List<DualCrnCommand> commandList = (List<DualCrnCommand>) task.getData();
+                    DualCrnCommand command = commandList.get(0);
+
+                    HashMap<String, Object> map = new HashMap<>();
+                    map.put("commands", commandList);
+                    map.put("idx", 1);
+                    redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_.key + command.getTaskNo(), JSON.toJSONString(map, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24);
+                    sendCommand(command);
+                } else if (step == 3) {
                     sendCommand((DualCrnCommand) task.getData());
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             }
         }, 0, 200, TimeUnit.MILLISECONDS);
+
+        commandExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
+            @Override
+            public Thread newThread(Runnable r) {
+                Thread t = new Thread(r);
+                t.setName("DualCrnCommand-" + deviceConfig.getDeviceNo());
+                t.setDaemon(true);
+                return t;
+            }
+        });
+        commandExecutor.scheduleAtFixedRate(() -> {
+            if (closed || Thread.currentThread().isInterrupted()) {
+                return;
+            }
+            try {
+                if(crnProtocol.getMode() != DualCrnModeType.AUTO.id) {
+                    return;
+                }
+
+                if(crnProtocol.getAlarm() != 0) {
+                    return;
+                }
+
+                //绛夊緟涓嬩竴涓换鍔�
+                Object wait = redisUtil.get(RedisKeyType.DUAL_CRN_PICK_WAIT_NEXT_TASK.key + crnProtocol.getCrnNo());
+                if (wait != null) {
+                    return;
+                }
+
+                if(crnProtocol.getTaskNo() > 0 && crnProtocol.getStatus() == DualCrnStatusType.IDLE.id) {
+                    Integer taskNo = crnProtocol.getTaskNo();
+                    Object commandObj = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + taskNo);
+                    if (commandObj == null) {
+                        News.error("鍙屽伐浣嶅爢鍨涙満锛屽伐浣�1绌洪棽绛夊緟涓嬪彂鍛戒护锛屼絾鏈壘鍒板懡浠ゃ�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", crnProtocol.getCrnNo(), taskNo);
+                        return;
+                    }
+
+                    JSONObject commandMap = JSON.parseObject(commandObj.toString());
+                    Integer idx = commandMap.getInteger("idx");
+                    List<DualCrnCommand> commandList = commandMap.getJSONArray("commands").toJavaList(DualCrnCommand.class);
+                    DualCrnCommand dualCommand = commandList.get(idx);
+                    idx++;
+                    commandMap.put("idx", idx);
+                    sendCommand(dualCommand);
+                    redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_.key + taskNo, commandMap.toJSONString(), 60 * 60 * 24);
+                }
+
+                if(crnProtocol.getTaskNoTwo() > 0 && crnProtocol.getStatusTwo() == DualCrnStatusType.IDLE.id) {
+                    Integer taskNo = crnProtocol.getTaskNoTwo();
+                    Object commandObj = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + taskNo);
+                    if (commandObj == null) {
+                        News.error("鍙屽伐浣嶅爢鍨涙満锛屽伐浣�2绌洪棽绛夊緟涓嬪彂鍛戒护锛屼絾鏈壘鍒板懡浠ゃ�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", crnProtocol.getCrnNo(), taskNo);
+                        return;
+                    }
+
+                    JSONObject commandMap = JSON.parseObject(commandObj.toString());
+                    Integer idx = commandMap.getInteger("idx");
+                    List<DualCrnCommand> commandList = commandMap.getJSONArray("commands").toJavaList(DualCrnCommand.class);
+                    DualCrnCommand dualCommand = commandList.get(idx);
+                    idx++;
+                    commandMap.put("idx", idx);
+                    sendCommand(dualCommand);
+                    redisUtil.set(RedisKeyType.DUAL_CRN_COMMAND_.key + taskNo, commandMap.toJSONString(), 60 * 60 * 24);
+                }
+
+            } catch (Exception e) {
+                e.printStackTrace();
+                log.error("DualCrnCommandThread Fail", e);
+            }
+        }, 0, 200, TimeUnit.MILLISECONDS);
     }
 
     /**
diff --git a/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
new file mode 100644
index 0000000..68d3406
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
@@ -0,0 +1,495 @@
+package com.zy.core.utils;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.core.exception.CoolException;
+import com.zy.asrs.domain.param.CreateLocMoveTaskParam;
+import com.zy.asrs.entity.BasCrnp;
+import com.zy.asrs.entity.BasDualCrnp;
+import com.zy.asrs.entity.LocMast;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasDualCrnpService;
+import com.zy.asrs.service.LocMastService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.asrs.utils.Utils;
+import com.zy.common.model.StartupDto;
+import com.zy.common.service.CommonService;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.News;
+import com.zy.core.cache.MessageQueue;
+import com.zy.core.cache.SlaveConnection;
+import com.zy.core.enums.*;
+import com.zy.core.model.StationObjModel;
+import com.zy.core.model.Task;
+import com.zy.core.model.command.CrnCommand;
+import com.zy.core.model.command.DualCrnCommand;
+import com.zy.core.model.protocol.CrnProtocol;
+import com.zy.core.model.protocol.DualCrnProtocol;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.thread.CrnThread;
+import com.zy.core.thread.DualCrnThread;
+import com.zy.core.thread.StationThread;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class DualCrnOperateProcessUtils {
+
+    @Autowired
+    private WrkMastService wrkMastService;
+    @Autowired
+    private BasDualCrnpService basDualCrnpService;
+    @Autowired
+    private LocMastService locMastService;
+    @Autowired
+    private RedisUtil redisUtil;
+    @Autowired
+    private WmsOperateUtils wmsOperateUtils;
+    @Autowired
+    private CommonService commonService;
+
+    //鍏ュ嚭搴�  ===>>  鍙屽伐浣嶅爢鍨涙満鍏ュ嚭搴撲綔涓氫笅鍙�
+    public synchronized void dualRrnIoExecute() {
+        List<BasDualCrnp> basDualCrnps = basDualCrnpService.selectList(new EntityWrapper<>());
+        for (BasDualCrnp basDualCrnp : basDualCrnps) {
+            DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, basDualCrnp.getCrnNo());
+            if(dualCrnThread == null){
+                continue;
+            }
+
+            DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+            if(dualCrnProtocol == null){
+                continue;
+            }
+
+            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                    .eq("dual_crn_no", basDualCrnp.getCrnNo())
+                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts)
+            );
+            if(wrkMasts.size() >= 2){
+                continue;
+            }
+
+            if(dualCrnProtocol.getMode() != DualCrnModeType.AUTO.id) {
+                continue;
+            }
+
+            if(dualCrnProtocol.getAlarm() != 0) {
+                continue;
+            }
+
+            int executeTaskNo = 0;
+            if (dualCrnProtocol.getTaskNo() > 0) {
+                executeTaskNo = dualCrnProtocol.getTaskNo();
+            }
+            if (dualCrnProtocol.getTaskNoTwo() > 0) {
+                executeTaskNo = dualCrnProtocol.getTaskNoTwo();
+            }
+
+            if (executeTaskNo > 0) {
+                WrkMast wrkMast = wrkMastService.selectByWorkNo(executeTaskNo);
+                if (wrkMast != null) {
+                    if (wrkMast.getIoType().equals(WrkIoType.IN.id)) {
+                        this.crnExecuteIn(basDualCrnp, dualCrnThread); //  鍏ュ簱
+                    } else if (wrkMast.getIoType().equals(WrkIoType.OUT.id)) {
+                        this.crnExecuteOut(basDualCrnp, dualCrnThread); //  鍑哄簱
+                    }else {
+                        continue;
+                    }
+                }
+            }
+
+            // 濡傛灉鏈�杩戜竴娆℃槸鍏ュ簱妯″紡
+            if (dualCrnProtocol.getLastIo().equals("I")) {
+                if (basDualCrnp.getInEnable().equals("Y")) {
+                    this.crnExecuteIn(basDualCrnp, dualCrnThread); //  鍏ュ簱
+                    dualCrnProtocol.setLastIo("O");
+                } else if (basDualCrnp.getOutEnable().equals("Y")) {
+                    this.crnExecuteOut(basDualCrnp, dualCrnThread); //  鍑哄簱
+                    dualCrnProtocol.setLastIo("I");
+                }
+            }
+            // 濡傛灉鏈�杩戜竴娆℃槸鍑哄簱妯″紡
+            else if (dualCrnProtocol.getLastIo().equals("O")) {
+                if (basDualCrnp.getOutEnable().equals("Y")) {
+                    this.crnExecuteOut(basDualCrnp, dualCrnThread); //  鍑哄簱
+                    dualCrnProtocol.setLastIo("I");
+                } else if (basDualCrnp.getInEnable().equals("Y")) {
+                    this.crnExecuteIn(basDualCrnp, dualCrnThread); //  鍏ュ簱
+                    dualCrnProtocol.setLastIo("O");
+                }
+            }
+        }
+    }
+
+    private synchronized void crnExecuteIn(BasDualCrnp basDualCrnp, DualCrnThread dualCrnThread) {
+        DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+        if(dualCrnProtocol == null){
+            return;
+        }
+
+        if(!basDualCrnp.getInEnable().equals("Y")){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鍙叆淇″彿涓嶆弧瓒�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        List<StationObjModel> inStationList = basDualCrnp.getInStationList$();
+        if(inStationList.isEmpty()){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鍏ュ簱绔欑偣鏈缃�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        Integer crnNo = basDualCrnp.getCrnNo();
+        int station = calcStation(dualCrnProtocol);
+        if(station == 0){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鏃犲彲鐢ㄥ伐浣�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        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.getWrkSts() != WrkStsType.INBOUND_DEVICE_RUN.sts){
+                News.taskInfo(stationProtocol.getTaskNo(), "宸ヤ綔鍙�:{} 浠诲姟鐘舵�佸紓甯�", stationProtocol.getTaskNo());
+                continue;
+            }
+
+            // 鑾峰彇搴撲綅淇℃伅
+            LocMast locMast = locMastService.selectById(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());
+
+            List<DualCrnCommand> commandList = new ArrayList<>();
+            DualCrnCommand pickCommand = dualCrnThread.getPickCommand(sourceLocNo, wrkMast.getWrkNo(), crnNo, station);
+            DualCrnCommand putCommand = dualCrnThread.getPutCommand(wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo, station);
+            commandList.add(pickCommand);
+            commandList.add(putCommand);
+
+            wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts);
+            wrkMast.setDualCrnNo(crnNo);
+            wrkMast.setSystemMsg("");
+            wrkMast.setIoTime(new Date());
+            if (wrkMastService.updateById(wrkMast)) {
+                MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, commandList));
+                News.info("鍙屽伐浣嶅爢鍨涙満鍛戒护涓嬪彂鎴愬姛锛屽爢鍨涙満鍙�={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(commandList));
+            }
+        }
+    }
+
+    private synchronized void crnExecuteOut(BasDualCrnp basDualCrnp, DualCrnThread dualCrnThread) {
+        DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+        if(dualCrnProtocol == null){
+            return;
+        }
+
+        if(!basDualCrnp.getOutEnable().equals("Y")){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鍙嚭淇″彿涓嶆弧瓒�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        List<StationObjModel> outStationList = basDualCrnp.getOutStationList$();
+        if(outStationList.isEmpty()){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鍑哄簱绔欑偣鏈缃�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        Integer crnNo = basDualCrnp.getCrnNo();
+        int station = calcStation(dualCrnProtocol);
+        if(station == 0){
+            News.info("鍙屽伐浣嶅爢鍨涙満:{} 鏃犲彲鐢ㄥ伐浣�", basDualCrnp.getCrnNo());
+            return;
+        }
+
+        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
+                .eq("crn_no", crnNo)
+                .eq("wrk_sts", WrkStsType.NEW_OUTBOUND.sts)
+        );
+
+        for (WrkMast wrkMast : wrkMasts) {
+            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.selectById(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());
+
+                List<DualCrnCommand> commandList = new ArrayList<>();
+                DualCrnCommand pickCommand = dualCrnThread.getPickCommand(wrkMast.getSourceLocNo(), wrkMast.getWrkNo(), crnNo, station);
+                DualCrnCommand putCommand = dualCrnThread.getPutCommand(targetLocNo, wrkMast.getWrkNo(), crnNo, station);
+                commandList.add(pickCommand);
+                commandList.add(putCommand);
+
+                wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts);
+                wrkMast.setDualCrnNo(crnNo);
+                wrkMast.setSystemMsg("");
+                wrkMast.setIoTime(new Date());
+                if (wrkMastService.updateById(wrkMast)) {
+                    MessageQueue.offer(SlaveType.DualCrn, crnNo, new Task(2, commandList));
+                    //鍙栬揣鍚庣瓑寰呬笅涓�涓换鍔℃椂闀�
+                    redisUtil.set(RedisKeyType.DUAL_CRN_PICK_WAIT_NEXT_TASK.key + crnNo, "wait", 5);
+                    News.info("鍙屽伐浣嶅爢鍨涙満鍛戒护涓嬪彂鎴愬姛锛屽爢鍨涙満鍙�={}锛屼换鍔℃暟鎹�={}", crnNo, JSON.toJSON(commandList));
+                    return;
+                }
+            }
+        }
+    }
+
+    //鍙屽伐浣嶅爢鍨涙満浠诲姟鎵ц瀹屾垚
+    public synchronized void dualCrnIoExecuteFinish() {
+        List<BasDualCrnp> basDualCrnps = basDualCrnpService.selectList(new EntityWrapper<>());
+        for (BasDualCrnp basDualCrnp : basDualCrnps) {
+            DualCrnThread dualCrnThread = (DualCrnThread) SlaveConnection.get(SlaveType.DualCrn, basDualCrnp.getCrnNo());
+            if(dualCrnThread == null){
+                continue;
+            }
+
+            DualCrnProtocol dualCrnProtocol = dualCrnThread.getStatus();
+            if(dualCrnProtocol == null){
+                continue;
+            }
+
+            if(dualCrnProtocol.getMode() != DualCrnModeType.AUTO.id) {
+                continue;
+            }
+
+            if(dualCrnProtocol.getAlarm() != 0) {
+                continue;
+            }
+
+            if(dualCrnProtocol.getTaskNo() > 0 && dualCrnProtocol.getStatus() == DualCrnStatusType.WAITING.id) {
+                executeFinish(basDualCrnp, dualCrnThread, dualCrnProtocol, dualCrnProtocol.getTaskNo(), 1);
+                continue;
+            }
+
+            if(dualCrnProtocol.getTaskNoTwo() > 0 && dualCrnProtocol.getStatusTwo() == DualCrnStatusType.WAITING.id) {
+                executeFinish(basDualCrnp, dualCrnThread, dualCrnProtocol, dualCrnProtocol.getTaskNoTwo(), 2);
+                continue;
+            }
+        }
+    }
+
+    private void executeFinish(BasDualCrnp basDualCrnp, DualCrnThread dualCrnThread, DualCrnProtocol dualCrnProtocol, int taskNo, int station) {
+        Object lock = redisUtil.get(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo);
+        if (lock != null) {
+            return;
+        }
+
+        // 鑾峰彇寰呯‘璁ゅ伐浣滄。
+        WrkMast wrkMast = wrkMastService.selectByWorkNo(taskNo);
+        if (wrkMast == null) {
+            News.error("鍙屽伐浣嶅爢鍨涙満澶勪簬绛夊緟纭涓斾换鍔″畬鎴愮姸鎬侊紝浣嗘湭鎵惧埌宸ヤ綔妗c�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basDualCrnp.getCrnNo(), taskNo);
+            return;
+        }
+
+        Object commandObj = redisUtil.get(RedisKeyType.DUAL_CRN_COMMAND_.key + wrkMast.getWrkNo());
+        if (commandObj == null) {
+            News.error("鍙屽伐浣嶅爢鍨涙満澶勪簬绛夊緟纭涓斾换鍔″畬鎴愮姸鎬侊紝浣嗘湭鎵惧埌鍛戒护銆傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basDualCrnp.getCrnNo(), taskNo);
+            return;
+        }
+
+        JSONObject commandMap = JSON.parseObject(commandObj.toString());
+        Integer idx = commandMap.getInteger("idx");
+        List<DualCrnCommand> commandList = commandMap.getJSONArray("commands").toJavaList(DualCrnCommand.class);
+        if (idx >= commandList.size()) {
+            Long updateWrkSts = null;
+            if (wrkMast.getWrkSts() == WrkStsType.INBOUND_RUN.sts) {
+                updateWrkSts = WrkStsType.COMPLETE_INBOUND.sts;
+            } else if (wrkMast.getWrkSts() == WrkStsType.OUTBOUND_RUN.sts) {
+                updateWrkSts = WrkStsType.OUTBOUND_RUN_COMPLETE.sts;
+            } else if (wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts) {
+                updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
+            } else {
+                News.error("鍙屽伐浣嶅爢鍨涙満澶勪簬绛夊緟纭涓斾换鍔″畬鎴愮姸鎬侊紝浣嗗伐浣滅姸鎬佸紓甯搞�傚爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basDualCrnp.getCrnNo(), taskNo);
+                return;
+            }
+
+            wrkMast.setWrkSts(updateWrkSts);
+            wrkMast.setSystemMsg("");
+            wrkMast.setIoTime(new Date());
+            if (wrkMastService.updateById(wrkMast)) {
+                DualCrnCommand resetCommand = dualCrnThread.getResetCommand(dualCrnProtocol.getCrnNo(), station);
+                MessageQueue.offer(SlaveType.DualCrn, dualCrnProtocol.getCrnNo(), new Task(3, resetCommand));
+                News.info("鍙屽伐浣嶅爢鍨涙満浠诲姟鐘舵�佹洿鏂版垚鍔燂紝鍫嗗灈鏈哄彿={}锛屽伐浣滃彿={}", basDualCrnp.getCrnNo(), taskNo);
+            }
+            redisUtil.set(RedisKeyType.DUAL_CRN_IO_EXECUTE_FINISH_LIMIT.key + basDualCrnp.getCrnNo() + "_" + taskNo, "lock", 10);
+        }else {
+            DualCrnCommand resetCommand = dualCrnThread.getResetCommand(dualCrnProtocol.getCrnNo(), station);
+            MessageQueue.offer(SlaveType.DualCrn, dualCrnProtocol.getCrnNo(), new Task(3, resetCommand));
+            News.info("鍙屽伐浣嶅爢鍨涙満鍛戒护瀹屾垚纭鎴愬姛锛屽爢鍨涙満鍙�={}锛屽伐浣滃彿={}", basDualCrnp.getCrnNo(), taskNo);
+        }
+    }
+
+    //妫�娴嬫祬搴撲綅鐘舵��
+    public synchronized boolean checkShallowLocStatus(String locNo, Integer taskNo) {
+        String checkDeepLocOutTaskBlockReport = "Y";
+        Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
+        if (systemConfigMapObj != null) {
+            HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
+            checkDeepLocOutTaskBlockReport = systemConfigMap.get("checkDeepLocOutTaskBlockReport");
+        }
+
+        if (!checkDeepLocOutTaskBlockReport.equals("Y")) {
+            return true;
+        }
+
+        Object lock = redisUtil.get(RedisKeyType.CHECK_SHALLOW_LOC_STATUS_LIMIT.key + taskNo);
+        if (lock != null) {
+            return false;
+        }
+        redisUtil.set(RedisKeyType.CHECK_SHALLOW_LOC_STATUS_LIMIT.key + taskNo, "lock", 5);
+
+        Integer shallowRow = Utils.getShallowRowByDeepRow(Utils.getRow(locNo));
+        if (shallowRow == null) {
+            return true;
+        }
+
+        String shallowLocNo = Utils.getLocNo(shallowRow, Utils.getBay(locNo), Utils.getLev(locNo));
+        LocMast shallowLocMast = locMastService.queryByLoc(shallowLocNo);
+        if (shallowLocMast == null) {
+            News.taskInfo(taskNo, "娴呭簱浣�:{} 鏁版嵁涓嶅瓨鍦�", shallowLocNo);
+            return false;
+        }
+
+        if (shallowLocMast.getLocSts().equals("O")) {
+            return true;
+        }
+
+        if (shallowLocMast.getLocSts().equals("F")) {
+            //娴呭簱浣嶇姸鎬佹湁璐�,鐢宠鏇存崲搴撲綅
+            String response = wmsOperateUtils.applyChangeLocNo(shallowLocNo);
+            if (response == null) {
+                News.taskError(taskNo, "WCS鐢宠鍦ㄥ簱搴撲綅鏇存崲搴撲綅澶辫触锛學MS鎺ュ彛鏈搷搴旓紒锛侊紒response锛歿}", response);
+                return false;
+            }
+            JSONObject jsonObject = JSON.parseObject(response);
+            if (jsonObject.getInteger("code").equals(200)) {
+                StartupDto dto = jsonObject.getObject("data", StartupDto.class);
+                String moveLocNo = dto.getLocNo();
+
+                CreateLocMoveTaskParam moveTaskParam = new CreateLocMoveTaskParam();
+                moveTaskParam.setTaskNo(dto.getTaskNo());
+                moveTaskParam.setSourceLocNo(shallowLocNo);
+                moveTaskParam.setLocNo(moveLocNo);
+                try {
+                    boolean result = commonService.createLocMoveTask(moveTaskParam);
+                } catch (CoolException e) {
+                    News.taskInfo(taskNo, e.getMessage());
+                }
+            } else {
+                News.error("璇锋眰WMS鐢宠鏇存崲搴撲綅鎺ュ彛澶辫触锛侊紒锛乺esponse锛歿}", response);
+            }
+        }
+        return false;
+    }
+
+    private int calcStation(DualCrnProtocol dualCrnProtocol) {
+        int station = 0;
+        if(dualCrnProtocol.getTaskNo() == 0){
+            station = 1;
+        }else if (dualCrnProtocol.getTaskNoTwo() == 0){
+            station = 2;
+        }
+
+        return station;
+    }
+
+}
diff --git a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
index 4c037b5..c3c8516 100644
--- a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
@@ -3,8 +3,10 @@
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.core.exception.CoolException;
 import com.zy.asrs.entity.*;
 import com.zy.asrs.service.*;
+import com.zy.common.entity.FindCrnNoResult;
 import com.zy.common.model.StartupDto;
 import com.zy.common.service.CommonService;
 import com.zy.common.utils.RedisUtil;
@@ -91,13 +93,13 @@
                     }
 
                     String locNo = wrkMast.getLocNo();
-                    Integer crnNo = commonService.findCrnNoByLocNo(locNo);
-                    if (crnNo == null) {
+                    FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
+                    if (findCrnNoResult == null) {
                         News.taskInfo(wrkMast.getWrkNo(), "鏈尮閰嶅埌鍫嗗灈鏈�");
                         continue;
                     }
 
-                    Integer targetStationId = commonService.findInStationId(crnNo, stationId);
+                    Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId);
                     if (targetStationId == null) {
                         News.taskInfo(wrkMast.getWrkNo(), "鎼滅储鍏ュ簱绔欑偣澶辫触");
                         continue;
@@ -246,7 +248,7 @@
                     if (runBlockReassignLocStationList.contains(stationProtocol.getStationId())) {
                         //绔欑偣澶勪簬閲嶆柊鍒嗛厤搴撲綅鍖哄煙
                         //杩愯鍫靛锛岄噸鏂扮敵璇蜂换鍔�
-                        String response = wmsOperateUtils.applyReassignTaskLocNo(wrkMast.getWrkNo());
+                        String response = wmsOperateUtils.applyReassignTaskLocNo(wrkMast.getWrkNo(), stationProtocol.getStationId());
                         if (response == null) {
                             News.taskError(wrkMast.getWrkNo(), "璇锋眰WMS閲嶆柊鍒嗛厤搴撲綅鎺ュ彛澶辫触锛屾帴鍙f湭鍝嶅簲锛侊紒锛乺esponse锛歿}", response);
                             continue;
@@ -280,13 +282,14 @@
                                 continue;
                             }
 
-                            Integer crnNo = commonService.findCrnNoByLocNo(locNo);
-                            if (crnNo == null) {
+                            FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
+                            if (findCrnNoResult == null) {
                                 News.taskInfo(wrkMast.getWrkNo(), "鏈尮閰嶅埌鍫嗗灈鏈�");
                                 continue;
                             }
+                            Integer crnNo = findCrnNoResult.getCrnNo();
 
-                            Integer targetStationId = commonService.findInStationId(crnNo, stationProtocol.getStationId());
+                            Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationProtocol.getStationId());
                             if (targetStationId == null) {
                                 News.taskInfo(wrkMast.getWrkNo(), "鎼滅储鍏ュ簱绔欑偣澶辫触");
                                 continue;
@@ -310,8 +313,16 @@
 
                             //鏇存柊宸ヤ綔妗f暟鎹�
                             wrkMast.setLocNo(locNo);
-                            wrkMast.setCrnNo(crnNo);
                             wrkMast.setStaNo(targetStationId);
+
+                            if (findCrnNoResult.getCrnType().equals(SlaveType.Crn)) {
+                                wrkMast.setCrnNo(crnNo);
+                            } else if (findCrnNoResult.getCrnType().equals(SlaveType.DualCrn)) {
+                                wrkMast.setDualCrnNo(crnNo);
+                            }else {
+                                throw new CoolException("鏈煡璁惧绫诲瀷");
+                            }
+
                             if (wrkMastService.updateById(wrkMast)) {
                                 MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
                             }
diff --git a/src/main/java/com/zy/core/utils/WmsOperateUtils.java b/src/main/java/com/zy/core/utils/WmsOperateUtils.java
index 0f855a4..56453bb 100644
--- a/src/main/java/com/zy/core/utils/WmsOperateUtils.java
+++ b/src/main/java/com/zy/core/utils/WmsOperateUtils.java
@@ -3,16 +3,22 @@
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.mapper.EntityWrapper;
+import com.core.common.Cools;
+import com.core.exception.CoolException;
 import com.zy.asrs.entity.BasCrnp;
+import com.zy.asrs.entity.BasDualCrnp;
 import com.zy.asrs.entity.HttpRequestLog;
 import com.zy.asrs.entity.WrkMast;
 import com.zy.asrs.service.BasCrnpService;
+import com.zy.asrs.service.BasDualCrnpService;
 import com.zy.asrs.service.HttpRequestLogService;
 import com.zy.asrs.service.WrkMastService;
 import com.zy.asrs.utils.Utils;
+import com.zy.common.entity.FindCrnNoResult;
 import com.zy.common.service.CommonService;
 import com.zy.common.utils.HttpHandler;
 import com.zy.core.News;
+import com.zy.core.enums.SlaveType;
 import com.zy.system.entity.Config;
 import com.zy.system.service.ConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -37,6 +43,8 @@
     private CommonService commonService;
     @Autowired
     private BasCrnpService basCrnpService;
+    @Autowired
+    private BasDualCrnpService basDualCrnpService;
 
     //鐢宠鍏ュ簱浠诲姟
     public synchronized String applyInTask(String barcode, Integer sourceStaNo, Integer locType1) {
@@ -69,7 +77,7 @@
             requestParam.put("barcode", barcode);
             requestParam.put("sourceStaNo", sourceStaNo);
             requestParam.put("locType1", locType1);
-            requestParam.put("row", Utils.getInTaskEnableRow());
+            requestParam.put("row", Utils.getInTaskEnableRow(sourceStaNo));
 
             response = new HttpHandler.Builder()
                     .setUri(wmsUrl)
@@ -104,7 +112,7 @@
     }
 
     //鐢宠浠诲姟閲嶆柊鍒嗛厤搴撲綅
-    public synchronized String applyReassignTaskLocNo(Integer taskNo) {
+    public synchronized String applyReassignTaskLocNo(Integer taskNo, Integer stationId) {
         String wmsUrl = null;
         Config wmsSystemUriConfig = configService.selectOne(new EntityWrapper<Config>().eq("code", "wmsSystemUri"));
         if (wmsSystemUriConfig != null) {
@@ -122,7 +130,7 @@
             wmsSystemReassignInTaskUrl = wmsSystemReassignInTaskUrlConfig.getValue();
         }
 
-        if(wmsSystemReassignInTaskUrl == null){
+        if (wmsSystemReassignInTaskUrl == null) {
             News.error("鏈厤缃甒MS浠诲姟閲嶆柊鍒嗛厤鍏ュ簱搴撲綅鎺ュ彛鍦板潃锛岄厤缃枃浠禖ode缂栫爜锛歸msSystemReassignInTaskUrl");
             return null;
         }
@@ -137,8 +145,17 @@
         String response = null;
         int result = 0;
         try {
+            List<Integer> excludeCrnList = new ArrayList<>();
+            List<Integer> excludeDualCrnList = new ArrayList<>();
+            if (!Cools.isEmpty(wrkMast.getCrnNo())) {
+                excludeCrnList.add(wrkMast.getCrnNo());
+            }
+            if (!Cools.isEmpty(wrkMast.getDualCrnNo())) {
+                excludeDualCrnList.add(wrkMast.getDualCrnNo());
+            }
+
             requestParam.put("taskNo", wrkMast.getWmsWrkNo());
-            requestParam.put("row", Utils.getInTaskEnableRow(new ArrayList<>(wrkMast.getCrnNo()), false));
+            requestParam.put("row", Utils.getInTaskEnableRow(stationId, excludeCrnList, excludeDualCrnList, false));
 
             response = new HttpHandler.Builder()
                     .setUri(wmsUrl)
@@ -152,10 +169,10 @@
                 if (jsonObject.getInteger("code") == 200) {
                     result = 1;
                     News.info("璇锋眰鐢宠浠诲姟閲嶆柊鍒嗛厤鍏ュ簱鎺ュ彛鎴愬姛锛侊紒锛乽rl锛歿}锛況equest锛歿}锛況esponse锛歿}", wmsUrl + wmsSystemReassignInTaskUrl, JSON.toJSONString(requestParam), response);
-                }else {
+                } else {
                     News.info("璇锋眰鐢宠浠诲姟閲嶆柊鍒嗛厤鍏ュ簱鎺ュ彛澶辫触锛屾帴鍙h繑鍥濩ode寮傚父锛侊紒锛乽rl锛歿}锛況equest锛歿}锛況esponse锛歿}", wmsUrl + wmsSystemReassignInTaskUrl, JSON.toJSONString(requestParam), response);
                 }
-            }else {
+            } else {
                 News.info("璇锋眰鐢宠浠诲姟閲嶆柊鍒嗛厤鍏ュ簱鎺ュ彛澶辫触锛屾帴鍙f湭鍝嶅簲锛侊紒锛乽rl锛歿}锛況equest锛歿}锛況esponse锛歿}", wmsUrl + wmsSystemReassignInTaskUrl, JSON.toJSONString(requestParam), response);
             }
         } catch (Exception e) {
@@ -196,19 +213,33 @@
             return null;
         }
 
-        Integer crnNo = commonService.findCrnNoByLocNo(locNo);
-        if (crnNo == null) {
+        FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
+        if (findCrnNoResult == null) {
             return null;
         }
-        BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
-        if (basCrnp == null) {
-            return null;
-        }
-
+        Integer crnNo = findCrnNoResult.getCrnNo();
         List<Integer> crnRows = new ArrayList<>();
-        List<List<Integer>> rowList = basCrnp.getControlRows$();
-        for (List<Integer> list : rowList) {
-            crnRows.addAll(list);
+
+        if (findCrnNoResult.getCrnType().equals(SlaveType.Crn)) {
+            BasCrnp basCrnp = basCrnpService.selectOne(new EntityWrapper<BasCrnp>().eq("crn_no", crnNo));
+            if (basCrnp == null) {
+                return null;
+            }
+            List<List<Integer>> rowList = basCrnp.getControlRows$();
+            for (List<Integer> list : rowList) {
+                crnRows.addAll(list);
+            }
+        } else if (findCrnNoResult.getCrnType().equals(SlaveType.DualCrn)) {
+            BasDualCrnp basDualCrnp = basDualCrnpService.selectOne(new EntityWrapper<BasDualCrnp>().eq("crn_no", crnNo));
+            if (basDualCrnp == null) {
+                return null;
+            }
+            List<List<Integer>> rowList = basDualCrnp.getControlRows$();
+            for (List<Integer> list : rowList) {
+                crnRows.addAll(list);
+            }
+        }else {
+            throw new CoolException("鏈煡璁惧绫诲瀷");
         }
 
         HashMap<String, Object> requestParam = new HashMap<>();
diff --git a/src/main/resources/mapper/WrkMastLogMapper.xml b/src/main/resources/mapper/WrkMastLogMapper.xml
index cdb4eb7..fd421ef 100644
--- a/src/main/resources/mapper/WrkMastLogMapper.xml
+++ b/src/main/resources/mapper/WrkMastLogMapper.xml
@@ -23,8 +23,9 @@
         <result column="error_memo" property="errorMemo" />
         <result column="memo" property="memo" />
         <result column="barcode" property="barcode" />
-        <result column="lift_no" property="liftNo" />
-        <result column="shuttle_no" property="shuttleNo" />
+        <result column="crn_no" property="crnNo" />
+        <result column="dual_crn_no" property="dualCrnNo" />
+        <result column="rgv_no" property="rgvNo" />
         <result column="wms_wrk_no" property="wmsWrkNo" />
         <result column="system_msg" property="systemMsg" />
     </resultMap>
diff --git a/src/main/resources/mapper/WrkMastMapper.xml b/src/main/resources/mapper/WrkMastMapper.xml
index dbe06de..bdd8323 100644
--- a/src/main/resources/mapper/WrkMastMapper.xml
+++ b/src/main/resources/mapper/WrkMastMapper.xml
@@ -23,6 +23,7 @@
         <result column="memo" property="memo" />
         <result column="barcode" property="barcode" />
         <result column="crn_no" property="crnNo" />
+        <result column="dual_crn_no" property="dualCrnNo" />
         <result column="rgv_no" property="rgvNo" />
         <result column="wms_wrk_no" property="wmsWrkNo" />
         <result column="system_msg" property="systemMsg" />
diff --git a/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js b/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
index 13b93c6..83a6dd9 100644
--- a/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
+++ b/src/main/webapp/static/js/basDualCrnp/basDualCrnp.js
@@ -24,7 +24,7 @@
             {type: 'checkbox'}
             ,{field: 'crnNo', align: 'center',title: '缂栧彿'}
             ,{field: 'status$', align: 'center',title: '鐘舵��'}
-            ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
+            // ,{field: 'wrkNo', align: 'center',title: '宸ヤ綔鍙�'}
             ,{field: 'inEnable', align: 'center',title: '鍙叆(checkBox)'}
             ,{field: 'outEnable', align: 'center',title: '鍙嚭(checkBox)'}
             // ,{field: 'createBy', align: 'center',title: '鍒涘缓浜哄憳'}
diff --git a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
index 246da2d..2423335 100644
--- a/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
+++ b/src/main/webapp/static/js/wrkMastLog/wrkMastLog.js
@@ -31,6 +31,7 @@
             ,{field: 'sourceLocNo', align: 'center',title: '婧愬簱浣�'}
             ,{field: 'locNo', align: 'center',title: '鐩爣搴撲綅'}
             ,{field: 'crnNo', align: 'center',title: '鍫嗗灈鏈�'}
+            ,{field: 'dualCrnNo', align: 'center',title: '鍙屽伐浣嶅爢鍨涙満'}
             ,{field: 'modiUser$', align: 'center',title: '淇敼浜哄憳', hide:true}
             ,{field: 'modiTime$', align: 'center',title: '淇敼鏃堕棿', hide:true}
             // ,{field: 'appeUser$', align: 'center',title: '鍒涘缓鑰�',event: 'appeUser', style: 'cursor:pointer'}
diff --git a/src/main/webapp/views/basDualCrnp/basDualCrnp.html b/src/main/webapp/views/basDualCrnp/basDualCrnp.html
index d4eee1f..eb24329 100644
--- a/src/main/webapp/views/basDualCrnp/basDualCrnp.html
+++ b/src/main/webapp/views/basDualCrnp/basDualCrnp.html
@@ -69,7 +69,7 @@
 <!-- 琛ㄥ崟寮圭獥 -->
 <script type="text/html" id="editDialog">
     <form id="detail" lay-filter="detail" class="layui-form admin-form model-form">
-        <input name="id" type="hidden">
+        <input name="crnNo" type="hidden">
         <div class="layui-row">
             <div class="layui-col-md12">
                 <div class="layui-form-item">
@@ -82,12 +82,12 @@
                         </select>
                     </div>
                 </div>
-                <div class="layui-form-item">
-                    <label class="layui-form-label">宸ヤ綔鍙�: </label>
-                    <div class="layui-input-block">
-                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">
-                    </div>
-                </div>
+<!--                <div class="layui-form-item">-->
+<!--                    <label class="layui-form-label">宸ヤ綔鍙�: </label>-->
+<!--                    <div class="layui-input-block">-->
+<!--                        <input class="layui-input" name="wrkNo" placeholder="璇疯緭鍏ュ伐浣滃彿">-->
+<!--                    </div>-->
+<!--                </div>-->
                 <div class="layui-form-item">
                     <label class="layui-form-label">鍙叆(checkBox): </label>
                     <div class="layui-input-block">
diff --git a/src/main/webapp/views/wrkMast/wrkMast.html b/src/main/webapp/views/wrkMast/wrkMast.html
index aaf3864..b77331a 100644
--- a/src/main/webapp/views/wrkMast/wrkMast.html
+++ b/src/main/webapp/views/wrkMast/wrkMast.html
@@ -58,6 +58,8 @@
 						</el-table-column>
 						<el-table-column property="crnNo" label="鍫嗗灈鏈�">
 						</el-table-column>
+						<el-table-column property="dualCrnNo" label="鍙屽伐浣嶅爢鍨涙満">
+						</el-table-column>
 						<el-table-column property="systemMsg" label="绯荤粺娑堟伅">
 						</el-table-column>
 						<el-table-column label="鎿嶄綔" width="100">

--
Gitblit v1.9.1