#
Administrator
2026-04-25 b67a6892ccfe6d71dc0ad2b75b2c66c18c4f6628
src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
@@ -27,6 +27,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;
@@ -56,8 +58,10 @@
    private NotifyUtils notifyUtils;
    @Autowired
    private StationOperateProcessUtils stationOperateProcessUtils;
    @Autowired
    private MainProcessTaskSubmitter mainProcessTaskSubmitter;
    public synchronized void crnIoExecute() {
    public void crnIoExecute() {
        Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
        if (systemConfigMapObj != null) {
            HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
@@ -69,85 +73,107 @@
        }
    }
    //入出库  ===>>  堆垛机入出库作业下发
    public synchronized void crnIoExecuteNormal() {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
            if(crnThread == null){
                continue;
    public void crnIoExecute(BasCrnp basCrnp) {
        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 ("solver".equals(systemConfigMap.get("crnRunMethod"))) {
                plannerExecute(basCrnp);
            } else {
                crnIoExecuteNormal(basCrnp);
            }
            CrnProtocol crnProtocol = crnThread.getStatus();
            if(crnProtocol == null){
                continue;
            }
            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                    .eq("crn_no", basCrnp.getCrnNo())
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts)
                    .orderBy("batch_seq", false)
            );
            if(!wrkMasts.isEmpty()){
                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;
                }
                // 如果最近一次是入库模式
                if (crnProtocol.getLastIo().equals("I")) {
                    if (basCrnp.getInEnable().equals("Y")) {
                        boolean result = this.crnExecuteIn(basCrnp, crnThread);//  入库
                        crnProtocol.setLastIo("O");
                        if (result) {
                            break;
                        }
                    } else if (basCrnp.getOutEnable().equals("Y")) {
                        boolean result = this.crnExecuteOut(basCrnp, crnThread);//  出库
                        crnProtocol.setLastIo("I");
                        if (result) {
                            break;
                        }
                    }
                }
                // 如果最近一次是出库模式
                else if (crnProtocol.getLastIo().equals("O")) {
                    if (basCrnp.getOutEnable().equals("Y")) {
                        boolean result = this.crnExecuteOut(basCrnp, crnThread);//  出库
                        crnProtocol.setLastIo("I");
                        if (result) {
                            break;
                        }
                    } else if (basCrnp.getInEnable().equals("Y")) {
                        boolean result = this.crnExecuteIn(basCrnp, crnThread);//  入库
                        crnProtocol.setLastIo("O");
                        if (result) {
                            break;
                        }
                    }
                }
                //库位移转
                boolean transfer = this.crnExecuteLocTransfer(basCrnp, crnThread);
                if (transfer) {
                    break;
                }
            }
        } else {
            crnIoExecuteNormal(basCrnp);
        }
    }
    private synchronized boolean crnExecuteIn(BasCrnp basCrnp, CrnThread crnThread) {
    //入出库  ===>>  堆垛机入出库作业下发
    public void crnIoExecuteNormal() {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            crnIoExecuteNormal(basCrnp);
        }
    }
    public void crnIoExecuteNormal(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;
        }
        List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                .eq("crn_no", basCrnp.getCrnNo())
                .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts)
                .orderBy("batch_seq", false)
        );
        if(!wrkMasts.isEmpty()){
            return;
        }
        // 只有当堆垛机空闲 并且 无任务时才继续执行
        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) {
                return;
            }
            // 如果最近一次是入库模式
            if (crnProtocol.getLastIo().equals("I")) {
                if (basCrnp.getInEnable().equals("Y")) {
                    boolean result = this.crnExecuteIn(basCrnp, crnThread);//  入库
                    crnProtocol.setLastIo("O");
                    if (result) {
                        return;
                    }
                } else if (basCrnp.getOutEnable().equals("Y")) {
                    boolean result = this.crnExecuteOut(basCrnp, crnThread);//  出库
                    crnProtocol.setLastIo("I");
                    if (result) {
                        return;
                    }
                }
            }
            // 如果最近一次是出库模式
            else if (crnProtocol.getLastIo().equals("O")) {
                if (basCrnp.getOutEnable().equals("Y")) {
                    boolean result = this.crnExecuteOut(basCrnp, crnThread);//  出库
                    crnProtocol.setLastIo("I");
                    if (result) {
                        return;
                    }
                } else if (basCrnp.getInEnable().equals("Y")) {
                    boolean result = this.crnExecuteIn(basCrnp, crnThread);//  入库
                    crnProtocol.setLastIo("O");
                    if (result) {
                        return;
                    }
                }
            }
            //库位移转
            this.crnExecuteLocTransfer(basCrnp, crnThread);
        }
    }
    private boolean crnExecuteIn(BasCrnp basCrnp, CrnThread crnThread) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if(crnProtocol == null){
            return false;
@@ -192,6 +218,12 @@
            if (!stationProtocol.isInEnable()) {
                News.taskInfo(stationProtocol.getTaskNo(), "取货站点:{} 没有可入信号", stationObjModel.getStationId());
                logTraceLimited("crn_in_wait_signal_" + crnNo + "_" + stationObjModel.getStationId(), 3,
                        "[WCS Trace][堆垛机入库] 取货站点不可入。crnNo={},stationId={},stationTaskNo={},autoing={},loading={},inEnable={},outEnable={},runBlock={},barcode={},targetStaNo={},crnStatus={},crnTaskNo={},crnAlarm={}",
                        crnNo, stationObjModel.getStationId(), stationProtocol.getTaskNo(),
                        stationProtocol.isAutoing(), stationProtocol.isLoading(), stationProtocol.isInEnable(),
                        stationProtocol.isOutEnable(), stationProtocol.isRunBlock(), stationProtocol.getBarcode(),
                        stationProtocol.getTargetStaNo(), crnProtocol.getStatus(), crnProtocol.getTaskNo(), crnProtocol.getAlarm());
                continue;
            }
@@ -228,6 +260,10 @@
            String sourceLocNo = Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
            CrnCommand command = crnThread.getPickAndPutCommand(sourceLocNo, wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
            logTraceLimited("crn_in_dispatch_" + wrkMast.getWrkNo(), 3,
                    "[WCS Trace][堆垛机入库] 准备下发堆垛机入库命令。crnNo={},wrkNo={},sourceStationId={},sourceLocNo={},targetLocNo={},stationTaskNo={},crnStatus={},crnTaskNo={},crnAlarm={}",
                    crnNo, wrkMast.getWrkNo(), stationObjModel.getStationId(), sourceLocNo, wrkMast.getLocNo(),
                    stationProtocol.getTaskNo(), crnProtocol.getStatus(), crnProtocol.getTaskNo(), crnProtocol.getAlarm());
            wrkMast.setWrkSts(WrkStsType.INBOUND_RUN.sts);
            wrkMast.setCrnNo(crnNo);
@@ -243,7 +279,7 @@
        return false;
    }
    private synchronized boolean crnExecuteOut(BasCrnp basCrnp, CrnThread crnThread) {
    private boolean crnExecuteOut(BasCrnp basCrnp, CrnThread crnThread) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if(crnProtocol == null){
            return false;
@@ -310,6 +346,12 @@
                if (!stationProtocol.isOutEnable()) {
                    News.info("放货站点:{} 没有可出信号", stationObjModel.getStationId());
                    logTraceLimited("crn_out_wait_signal_" + crnNo + "_" + stationObjModel.getStationId(), 3,
                            "[WCS Trace][堆垛机出库] 放货站点不可出。crnNo={},stationId={},wrkNo={},autoing={},loading={},inEnable={},outEnable={},runBlock={},stationTaskNo={},targetStaNo={},crnStatus={},crnTaskNo={},crnAlarm={}",
                            crnNo, stationObjModel.getStationId(), wrkMast.getWrkNo(),
                            stationProtocol.isAutoing(), stationProtocol.isLoading(), stationProtocol.isInEnable(),
                            stationProtocol.isOutEnable(), stationProtocol.isRunBlock(), stationProtocol.getTaskNo(),
                            stationProtocol.getTargetStaNo(), crnProtocol.getStatus(), crnProtocol.getTaskNo(), crnProtocol.getAlarm());
                    continue;
                }
@@ -335,6 +377,11 @@
                String targetLocNo = Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
                CrnCommand command = crnThread.getPickAndPutCommand(wrkMast.getSourceLocNo(), targetLocNo, wrkMast.getWrkNo(), crnNo);
                logTraceLimited("crn_out_dispatch_" + wrkMast.getWrkNo(), 3,
                        "[WCS Trace][堆垛机出库] 准备下发堆垛机出库命令。crnNo={},wrkNo={},targetStationId={},sourceLocNo={},targetLocNo={},crnStatus={},crnTaskNo={},crnAlarm={}",
                        crnNo, wrkMast.getWrkNo(), stationObjModel.getStationId(),
                        wrkMast.getSourceLocNo(), targetLocNo, crnProtocol.getStatus(),
                        crnProtocol.getTaskNo(), crnProtocol.getAlarm());
                wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN.sts);
                wrkMast.setCrnNo(crnNo);
@@ -351,7 +398,7 @@
        return false;
    }
    private synchronized boolean crnExecuteInPlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
    private boolean crnExecuteInPlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if (crnProtocol == null) {
            return false;
@@ -444,7 +491,7 @@
        return false;
    }
    private synchronized boolean crnExecuteOutPlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
    private boolean crnExecuteOutPlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if (crnProtocol == null) {
            return false;
@@ -529,7 +576,7 @@
        return false;
    }
    private synchronized boolean crnExecuteLocTransfer(BasCrnp basCrnp, CrnThread crnThread) {
    private boolean crnExecuteLocTransfer(BasCrnp basCrnp, CrnThread crnThread) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if(crnProtocol == null){
            return false;
@@ -584,177 +631,193 @@
    }
    //堆垛机任务执行完成
    public synchronized void crnIoExecuteFinish() {
    public void crnIoExecuteFinish() {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        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;
            }
            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;
                }
                // 获取待确认工作档
                WrkMast wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo());
                if (wrkMast == null) {
                    News.error("堆垛机处于等待确认且任务完成状态,但未找到工作档。堆垛机号={},工作号={}", 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;
                        }
                    }
                    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);
            }
            crnIoExecuteFinish(basCrnp);
        }
    }
    public synchronized void plannerExecute() {
    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;
            }
            // 获取待确认工作档
            WrkMast wrkMast = wrkMastService.selectByWorkNo(crnProtocol.getTaskNo());
            if (wrkMast == null) {
                News.error("堆垛机处于等待确认且任务完成状态,但未找到工作档。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                return;
            }
            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;
                    }
                }
                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());
                return;
            }
            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);
        }
    }
    public void plannerExecute() {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            plannerExecute(basCrnp);
        }
    }
    public void plannerExecute(BasCrnp basCrnp) {
        if (basCrnp == null || basCrnp.getCrnNo() == null) {
            return;
        }
        int nowSec = (int) (System.currentTimeMillis() / 1000);
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            String key = RedisKeyType.PLANNER_SCHEDULE.key + "CRN-" + basCrnp.getCrnNo();
            List<Object> items = redisUtil.lGet(key, 0, -1);
            if (items == null || items.isEmpty()) {
        String key = RedisKeyType.PLANNER_SCHEDULE.key + "CRN-" + basCrnp.getCrnNo();
        List<Object> items = redisUtil.lGet(key, 0, -1);
        if (items == null || items.isEmpty()) {
            return;
        }
        CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
        if (crnThread == null) {
            return;
        }
        CrnProtocol crnProtocol = crnThread.getStatus();
        if (crnProtocol == null) {
            return;
        }
        List<WrkMast> running = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                .eq("crn_no", basCrnp.getCrnNo())
                .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts)
        );
        if (!running.isEmpty()) {
            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;
        }
        for (Object v : items) {
            String s = String.valueOf(v);
            JSONObject obj = null;
            try { obj = JSON.parseObject(s); } catch (Exception ignore) {}
            if (obj == null) {
                continue;
            }
            Integer startEpochSec = obj.getInteger("startEpochSec");
            Integer endEpochSec = obj.getInteger("endEpochSec");
            Integer taskId = obj.getInteger("taskId");
            String taskType = obj.getString("taskType");
            if (startEpochSec == null || taskId == null || taskType == null) {
                continue;
            }
            int earlySlackSec = 5;
            int lateSlackSec = 10;
            Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
            if (systemConfigMapObj != null) {
                try {
                    HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
                    String es = systemConfigMap.getOrDefault("plannerEarlySlackSec", "60");
                    String ls = systemConfigMap.getOrDefault("plannerLateSlackSec", "10");
                    earlySlackSec = Integer.parseInt(es);
                    lateSlackSec = Integer.parseInt(ls);
                } catch (Exception ignore) {}
            }
            if (nowSec < startEpochSec - earlySlackSec) {
                continue;
            }
            if (endEpochSec != null && nowSec > endEpochSec + lateSlackSec) {
                redisUtil.lRemove(key, 1, s);
                continue;
            }
            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
            if (crnThread == null) {
                continue;
            }
            CrnProtocol crnProtocol = crnThread.getStatus();
            if (crnProtocol == null) {
                continue;
            }
            List<WrkMast> running = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                    .eq("crn_no", basCrnp.getCrnNo())
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts)
            );
            if (!running.isEmpty()) {
                continue;
            }
            if (!(crnProtocol.getMode() == CrnModeType.AUTO.id
                    && crnProtocol.getTaskNo() == 0
                    && crnProtocol.getStatus() == CrnStatusType.IDLE.id
                    && crnProtocol.getLoaded() == 0
                    && crnProtocol.getForkPos() == 0
                    && crnProtocol.getAlarm() == 0)) {
            WrkMast wrkMast = wrkMastService.selectByWorkNo(taskId);
            if (wrkMast == null) {
                redisUtil.lRemove(key, 1, s);
                continue;
            }
            for (Object v : items) {
                String s = String.valueOf(v);
                JSONObject obj = null;
                try { obj = JSON.parseObject(s); } catch (Exception ignore) {}
                if (obj == null) {
                    continue;
                }
                Integer startEpochSec = obj.getInteger("startEpochSec");
                Integer endEpochSec = obj.getInteger("endEpochSec");
                Integer taskId = obj.getInteger("taskId");
                String taskType = obj.getString("taskType");
                if (startEpochSec == null || taskId == null || taskType == null) {
                    continue;
                }
                int earlySlackSec = 5;
                int lateSlackSec = 10;
                Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
                if (systemConfigMapObj != null) {
                    try {
                        HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj;
                        String es = systemConfigMap.getOrDefault("plannerEarlySlackSec", "60");
                        String ls = systemConfigMap.getOrDefault("plannerLateSlackSec", "10");
                        earlySlackSec = Integer.parseInt(es);
                        lateSlackSec = Integer.parseInt(ls);
                    } catch (Exception ignore) {}
                }
                if (nowSec < startEpochSec - earlySlackSec) {
                    continue;
                }
                if (endEpochSec != null && nowSec > endEpochSec + lateSlackSec) {
            if ("IN".equalsIgnoreCase(taskType)) {
                boolean result = this.crnExecuteInPlanner(basCrnp, crnThread, wrkMast);//入库
                if (result) {
                    redisUtil.lRemove(key, 1, s);
                    continue;
                    return;
                }
                WrkMast wrkMast = wrkMastService.selectByWorkNo(taskId);
                if (wrkMast == null) {
            } else if ("OUT".equalsIgnoreCase(taskType)) {
                boolean result = this.crnExecuteOutPlanner(basCrnp, crnThread, wrkMast);//出库
                if (result) {
                    redisUtil.lRemove(key, 1, s);
                    continue;
                    return;
                }
                if ("IN".equalsIgnoreCase(taskType)) {
                    boolean result = this.crnExecuteInPlanner(basCrnp, crnThread, wrkMast);//入库
                    if (result) {
                        redisUtil.lRemove(key, 1, s);
                        break;
                    }
                } else if ("OUT".equalsIgnoreCase(taskType)) {
                    boolean result = this.crnExecuteOutPlanner(basCrnp, crnThread, wrkMast);//出库
                    if (result) {
                        redisUtil.lRemove(key, 1, s);
                        break;
                    }
                } else if ("MOVE".equalsIgnoreCase(taskType)) {
                    boolean result = this.crnExecuteMovePlanner(basCrnp, crnThread, wrkMast);//移库
                    if (result) {
                        redisUtil.lRemove(key, 1, s);
                        break;
                    }
            } else if ("MOVE".equalsIgnoreCase(taskType)) {
                boolean result = this.crnExecuteMovePlanner(basCrnp, crnThread, wrkMast);//移库
                if (result) {
                    redisUtil.lRemove(key, 1, s);
                    return;
                }
            }
        }
    }
    private synchronized boolean crnExecuteMovePlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
    private boolean crnExecuteMovePlanner(BasCrnp basCrnp, CrnThread crnThread, WrkMast wrkMast) {
        CrnProtocol crnProtocol = crnThread.getStatus();
        if (crnProtocol == null) {
            return false;
@@ -805,8 +868,82 @@
        return false;
    }
    public void submitCrnIoTasks(long minIntervalMs) {
        submitCrnIoTasks(MainProcessLane.CRN_IO, minIntervalMs);
    }
    public void submitCrnIoTasks(MainProcessLane lane, long minIntervalMs) {
        mainProcessTaskSubmitter.submitSerialTask(
                MainProcessLane.CRN_SCAN,
                "submitCrnIoTasks",
                minIntervalMs,
                () -> submitCrnIoTasksInternal(lane, minIntervalMs)
        );
    }
    private void submitCrnIoTasksInternal(MainProcessLane lane, long minIntervalMs) {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            if (basCrnp == null || basCrnp.getCrnNo() == null) {
                continue;
            }
            final BasCrnp currentCrn = basCrnp;
            mainProcessTaskSubmitter.submitKeyedSerialTask(
                    lane,
                    currentCrn.getCrnNo(),
                    "crnIoExecute",
                    minIntervalMs,
                    () -> crnIoExecute(currentCrn)
            );
        }
    }
    public void submitCrnIoExecuteFinishTasks(long minIntervalMs) {
        submitCrnIoExecuteFinishTasks(MainProcessLane.CRN_IO_FINISH, minIntervalMs);
    }
    public void submitCrnIoExecuteFinishTasks(MainProcessLane lane, long minIntervalMs) {
        mainProcessTaskSubmitter.submitSerialTask(
                MainProcessLane.CRN_SCAN,
                "submitCrnIoExecuteFinishTasks",
                minIntervalMs,
                () -> submitCrnIoExecuteFinishTasksInternal(lane, minIntervalMs)
        );
    }
    private void submitCrnIoExecuteFinishTasksInternal(MainProcessLane lane, long minIntervalMs) {
        List<BasCrnp> basCrnps = basCrnpService.selectList(new EntityWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            if (basCrnp == null || basCrnp.getCrnNo() == null) {
                continue;
            }
            final BasCrnp currentCrn = basCrnp;
            mainProcessTaskSubmitter.submitKeyedSerialTask(
                    lane,
                    currentCrn.getCrnNo(),
                    "crnIoExecuteFinish",
                    minIntervalMs,
                    () -> crnIoExecuteFinish(currentCrn)
            );
        }
    }
    private void logTraceLimited(String lockKey, int seconds, String format, Object... arguments) {
        String redisKey = RedisKeyType.LOG_LIMIT.key + "wcs_trace_" + lockKey;
        try {
            Object lock = redisUtil.get(redisKey);
            if (lock != null) {
                return;
            }
            redisUtil.set(redisKey, "lock", seconds);
        } catch (Exception e) {
            // 诊断日志不能影响主流程。
        }
        News.info(format, arguments);
    }
    //检测浅库位状态
    public synchronized boolean checkShallowLocStatus(String locNo, Integer taskNo) {
    public boolean checkShallowLocStatus(String locNo, Integer taskNo) {
        String checkDeepLocOutTaskBlockReport = "Y";
        Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
        if (systemConfigMapObj != null) {