Junjie
12 小时以前 5d5d6b55f439a9cb42d948e816a9db70e3fb2805
#堆垛机移动
3个文件已添加
11个文件已修改
1069 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/domain/enums/CrnStatusType.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/WrkAnalysisStationArrivalScanner.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/task/WrkMastScheduler.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/service/CommonService.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/enums/WrkIoType.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/enums/WrkStsType.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/plugin/FakeProcess.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/service/WrkCommandRollbackService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java 206 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260330_add_crn_move_task_type.sql 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/asrs/task/WrkAnalysisStationArrivalScannerTest.java 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/asrs/task/WrkMastSchedulerCrnMoveTest.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/test/java/com/zy/core/utils/CrnOperateProcessUtilsTest.java 397 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/domain/enums/CrnStatusType.java
@@ -1,5 +1,7 @@
package com.zy.asrs.domain.enums;
import com.zy.core.enums.WrkIoType;
/**
 * 堆垛机状态枚举
 */
@@ -37,6 +39,12 @@
    }
    public static CrnStatusType process(Integer ioType){
        if (ioType == null) {
            return MACHINE_ERROR;
        }
        if (ioType.equals(WrkIoType.CRN_MOVE.id)) {
            return MACHINE_SITE_MOVE;
        }
        if (ioType>100) {
            return MACHINE_PAKOUT;
        } else if (ioType < 100 && ioType!=3 && ioType!=6 && ioType!=11) {
src/main/java/com/zy/asrs/service/impl/WrkAnalysisServiceImpl.java
@@ -30,7 +30,8 @@
            WrkStsType.SETTLE_INBOUND.sts,
            WrkStsType.COMPLETE_OUTBOUND.sts,
            WrkStsType.SETTLE_OUTBOUND.sts,
            WrkStsType.COMPLETE_LOC_MOVE.sts
            WrkStsType.COMPLETE_LOC_MOVE.sts,
            WrkStsType.COMPLETE_CRN_MOVE.sts
    );
    private static final String MODE_TASK = "TASK";
    private static final String MODE_TIME = "TIME";
@@ -186,7 +187,9 @@
        entity.setRgvNo(wrkMast.getRgvNo());
        entity.setFinalWrkSts(wrkMast.getWrkSts());
        entity.setUpdateTime(time);
        if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) {
        if ((Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)
                || Objects.equals(wrkMast.getIoType(), WrkIoType.CRN_MOVE.id))
                && entity.getStationDurationMs() == null) {
            entity.setStationDurationMs(0L);
        }
        this.updateById(entity);
@@ -225,7 +228,9 @@
        if (entity.getAppeTime() != null) {
            entity.setTotalDurationMs(durationMs(entity.getAppeTime(), time));
        }
        if (Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id) && entity.getStationDurationMs() == null) {
        if ((Objects.equals(wrkMast.getIoType(), WrkIoType.LOC_MOVE.id)
                || Objects.equals(wrkMast.getIoType(), WrkIoType.CRN_MOVE.id))
                && entity.getStationDurationMs() == null) {
            entity.setStationDurationMs(0L);
        }
        FaultSummary faultSummary = buildFaultSummary(wrkMast.getWrkNo(), time);
@@ -265,6 +270,7 @@
        ioTypes.add(option("1", "IN", "入库", WrkIoType.IN.id));
        ioTypes.add(option("2", "OUT", "出库", WrkIoType.OUT.id));
        ioTypes.add(option("3", "LOC_MOVE", "移库", WrkIoType.LOC_MOVE.id));
        ioTypes.add(option("4", "CRN_MOVE", "堆垛机移动", WrkIoType.CRN_MOVE.id));
        List<Map<String, Object>> timeFields = new ArrayList<>();
        timeFields.add(option(TIME_FIELD_FINISH, TIME_FIELD_FINISH, "完成时间", TIME_FIELD_FINISH));
        timeFields.add(option(TIME_FIELD_APPE, TIME_FIELD_APPE, "创建时间", TIME_FIELD_APPE));
@@ -819,6 +825,9 @@
        if (Objects.equals(ioType, WrkIoType.LOC_MOVE.id)) {
            return "移库";
        }
        if (Objects.equals(ioType, WrkIoType.CRN_MOVE.id)) {
            return "堆垛机移动";
        }
        return String.valueOf(ioType);
    }
src/main/java/com/zy/asrs/task/WrkAnalysisStationArrivalScanner.java
@@ -1,17 +1,27 @@
package com.zy.asrs.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.core.common.Cools;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.BasCrnpService;
import com.zy.asrs.service.BasStationService;
import com.zy.asrs.service.WrkAnalysisService;
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.core.News;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.model.StationObjModel;
import com.zy.core.move.StationMoveCoordinator;
import com.zy.core.move.StationMoveSession;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.thread.StationThread;
import com.zy.core.utils.CrnOperateProcessUtils;
import com.zy.core.utils.StationOperateProcessUtils;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@@ -19,6 +29,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Component
public class WrkAnalysisStationArrivalScanner {
@@ -27,15 +38,27 @@
    private final BasStationService basStationService;
    private final WrkAnalysisService wrkAnalysisService;
    private final StationOperateProcessUtils stationOperateProcessUtils;
    private final StationMoveCoordinator stationMoveCoordinator;
    private final CommonService commonService;
    private final BasCrnpService basCrnpService;
    private final CrnOperateProcessUtils crnOperateProcessUtils;
    public WrkAnalysisStationArrivalScanner(WrkMastService wrkMastService,
                                            BasStationService basStationService,
                                            WrkAnalysisService wrkAnalysisService,
                                            StationOperateProcessUtils stationOperateProcessUtils) {
                                            StationOperateProcessUtils stationOperateProcessUtils,
                                            StationMoveCoordinator stationMoveCoordinator,
                                            CommonService commonService,
                                            BasCrnpService basCrnpService,
                                            CrnOperateProcessUtils crnOperateProcessUtils) {
        this.wrkMastService = wrkMastService;
        this.basStationService = basStationService;
        this.wrkAnalysisService = wrkAnalysisService;
        this.stationOperateProcessUtils = stationOperateProcessUtils;
        this.stationMoveCoordinator = stationMoveCoordinator;
        this.commonService = commonService;
        this.basCrnpService = basCrnpService;
        this.crnOperateProcessUtils = crnOperateProcessUtils;
    }
    @Scheduled(fixedDelay = 1000L)
@@ -66,6 +89,7 @@
            }
            Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
            StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(basStation.getStationId());
            tryDispatchInboundCrnMove(wrkMast, stationProtocol);
            boolean arrived = stationProtocol != null
                    && wrkMast.getWrkNo().equals(stationProtocol.getTaskNo())
                    && stationProtocol.isLoading();
@@ -78,4 +102,70 @@
            }
        }
    }
    private void tryDispatchInboundCrnMove(WrkMast wrkMast, StationProtocol targetStationProtocol) {
        if (wrkMast == null || wrkMast.getWrkNo() == null || wrkMast.getStaNo() == null || Cools.isEmpty(wrkMast.getLocNo())) {
            return;
        }
        if (targetStationProtocol != null
                && targetStationProtocol.isLoading()
                && targetStationProtocol.getTaskNo() > 0
                && targetStationProtocol.isInEnable()) {
            return;
        }
        StationMoveSession session = stationMoveCoordinator == null ? null : stationMoveCoordinator.loadSession(wrkMast.getWrkNo());
        if (!isInboundCrnMoveDispatchWindow(wrkMast, session)) {
            return;
        }
        FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(wrkMast.getLocNo());
        if (findCrnNoResult == null || !Objects.equals(findCrnNoResult.getCrnType(), SlaveType.Crn) || findCrnNoResult.getCrnNo() == null) {
            return;
        }
        BasCrnp basCrnp = basCrnpService.getOne(new QueryWrapper<BasCrnp>()
                .eq("crn_no", findCrnNoResult.getCrnNo())
                .last("limit 1"));
        if (basCrnp == null) {
            return;
        }
        for (StationObjModel stationObjModel : basCrnp.getInStationList$()) {
            if (stationObjModel == null || !Objects.equals(stationObjModel.getStationId(), wrkMast.getStaNo())) {
                continue;
            }
            if (stationObjModel.getDeviceRow() == null || stationObjModel.getDeviceBay() == null || stationObjModel.getDeviceLev() == null) {
                continue;
            }
            String inletLocNo = Utils.getLocNo(stationObjModel.getDeviceRow(), stationObjModel.getDeviceBay(), stationObjModel.getDeviceLev());
            boolean dispatched = crnOperateProcessUtils.dispatchCrnMove(findCrnNoResult.getCrnNo(), inletLocNo);
            if (dispatched) {
                News.info("入库任务即将到达入库口,已触发堆垛机预移车,工作号={},堆垛机号={},入库口位置={}",
                        wrkMast.getWrkNo(), findCrnNoResult.getCrnNo(), inletLocNo);
            }
            return;
        }
    }
    private boolean isInboundCrnMoveDispatchWindow(WrkMast wrkMast, StationMoveSession session) {
        if (wrkMast == null || session == null || !session.isActive() || wrkMast.getStaNo() == null) {
            return false;
        }
        List<Integer> fullPathStationIds = session.getFullPathStationIds();
        Integer currentStationId = session.getCurrentStationId();
        if (fullPathStationIds == null || fullPathStationIds.isEmpty() || currentStationId == null) {
            return false;
        }
        int currentIndex = fullPathStationIds.lastIndexOf(currentStationId);
        if (currentIndex < 0 || currentIndex >= fullPathStationIds.size() - 1) {
            return false;
        }
        int remainingStationCount = fullPathStationIds.size() - currentIndex - 1;
        if (remainingStationCount != 1) {
            return false;
        }
        Integer nextStationId = fullPathStationIds.get(currentIndex + 1);
        return Objects.equals(nextStationId, wrkMast.getStaNo());
    }
}
src/main/java/com/zy/asrs/task/WrkMastScheduler.java
@@ -205,6 +205,27 @@
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeCrnMove(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.COMPLETE_CRN_MOVE.sts));
        if (wrkMasts.isEmpty()) {
            return;
        }
        for (WrkMast wrkMast : wrkMasts) {
            if (!wrkMastLogService.save(wrkMast.getWrkNo())) {
                log.info("保存工作历史档[workNo={}]失败", wrkMast.getWrkNo());
            } else {
                wrkAnalysisService.finishTask(wrkMast, resolveFinishTime(wrkMast));
            }
            if (!wrkMastService.removeById(wrkMast.getWrkNo())) {
                log.info("删除工作主档[workNo={}]失败", wrkMast.getWrkNo());
            }
        }
    }
    @Scheduled(cron = "0/1 * * * * ? ")
    @Transactional
    public void executeCancelTask(){
        List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("mk", "taskCancel"));
        if (wrkMasts.isEmpty()) {
src/main/java/com/zy/common/service/CommonService.java
@@ -113,6 +113,8 @@
            wrkMast.setWrkSts(WrkStsType.COMPLETE_OUTBOUND.sts);
        } else if (wrkMast.getIoType() == WrkIoType.LOC_MOVE.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_LOC_MOVE.sts);
        } else if (wrkMast.getIoType() == WrkIoType.CRN_MOVE.id) {
            wrkMast.setWrkSts(WrkStsType.COMPLETE_CRN_MOVE.sts);
        }
        wrkMast.setModiTime(new Date());
@@ -144,6 +146,8 @@
            cancelSuccess = true;
        } else if (wrkMast.getIoType().equals(WrkIoType.LOC_MOVE.id) && !wrkMast.getWrkSts().equals(WrkStsType.NEW_LOC_MOVE.sts)) {
            cancelSuccess = true;
        } else if (wrkMast.getIoType().equals(WrkIoType.CRN_MOVE.id) && !wrkMast.getWrkSts().equals(WrkStsType.NEW_CRN_MOVE.sts)) {
            cancelSuccess = true;
        }
        if (cancelSuccess) {
src/main/java/com/zy/core/enums/WrkIoType.java
@@ -7,6 +7,7 @@
    IN(1, "入库"),
    OUT(101, "出库"),
    LOC_MOVE(201, "移库任务"),
    CRN_MOVE(301, "堆垛机移动任务"),
    FAKE_TASK_NO(9999, "仿真随机工作号"),
    ENABLE_IN(9998, "启动入库"),
    STATION_BACK(9997, "站点回退"),
src/main/java/com/zy/core/enums/WrkStsType.java
@@ -27,6 +27,12 @@
    LOC_MOVE_RUN_COMPLETE(503, "设备搬运完成"),
    LOC_MOVE_MANUAL(506, "移库待人工回滚"),
    COMPLETE_LOC_MOVE(509, "移库完成"),
    NEW_CRN_MOVE(601, "生成堆垛机移动任务"),
    CRN_MOVE_RUN(602, "堆垛机移动中"),
    CRN_MOVE_RUN_COMPLETE(603, "堆垛机移动完成"),
    CRN_MOVE_MANUAL(606, "堆垛机移动待人工回滚"),
    COMPLETE_CRN_MOVE(609, "堆垛机移动任务完成"),
    ;
src/main/java/com/zy/core/plugin/FakeProcess.java
@@ -704,6 +704,8 @@
                    }
                } else if (wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts) {
                    updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
                } else if (wrkMast.getWrkSts() == WrkStsType.CRN_MOVE_RUN.sts) {
                    updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
                } else {
                    News.error("堆垛机处于等待确认且任务完成状态,但工作状态异常。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                    continue;
src/main/java/com/zy/core/service/WrkCommandRollbackService.java
@@ -168,7 +168,8 @@
    private boolean isSingleCrnRunStatus(Long wrkSts) {
        return Long.valueOf(WrkStsType.INBOUND_RUN.sts).equals(wrkSts)
                || Long.valueOf(WrkStsType.OUTBOUND_RUN.sts).equals(wrkSts)
                || Long.valueOf(WrkStsType.LOC_MOVE_RUN.sts).equals(wrkSts);
                || Long.valueOf(WrkStsType.LOC_MOVE_RUN.sts).equals(wrkSts)
                || Long.valueOf(WrkStsType.CRN_MOVE_RUN.sts).equals(wrkSts);
    }
    private Long getRollbackStatus(Long wrkSts) {
@@ -180,6 +181,9 @@
        }
        if (Long.valueOf(WrkStsType.LOC_MOVE_RUN.sts).equals(wrkSts)) {
            return WrkStsType.NEW_LOC_MOVE.sts;
        }
        if (Long.valueOf(WrkStsType.CRN_MOVE_RUN.sts).equals(wrkSts)) {
            return WrkStsType.NEW_CRN_MOVE.sts;
        }
        return null;
    }
@@ -194,6 +198,9 @@
        if (Long.valueOf(WrkStsType.LOC_MOVE_RUN.sts).equals(wrkSts)) {
            return WrkStsType.LOC_MOVE_MANUAL.sts;
        }
        if (Long.valueOf(WrkStsType.CRN_MOVE_RUN.sts).equals(wrkSts)) {
            return WrkStsType.CRN_MOVE_MANUAL.sts;
        }
        return null;
    }
@@ -207,6 +214,9 @@
        if (Long.valueOf(WrkStsType.LOC_MOVE_MANUAL.sts).equals(wrkSts)) {
            return WrkStsType.NEW_LOC_MOVE.sts;
        }
        if (Long.valueOf(WrkStsType.CRN_MOVE_MANUAL.sts).equals(wrkSts)) {
            return WrkStsType.NEW_CRN_MOVE.sts;
        }
        return null;
    }
}
src/main/java/com/zy/core/utils/CrnOperateProcessUtils.java
@@ -40,8 +40,11 @@
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@Component
public class CrnOperateProcessUtils {
@@ -83,11 +86,16 @@
    //入出库  ===>>  堆垛机入出库作业下发
    public synchronized void crnIoExecuteNormal() {
        Set<Integer> crnMoveBlockedCrnNos = executeCrnMoveTask();
        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
        Map<Integer, BasCrnp> dispatchCrnMap = new HashMap<>();
        Map<Integer, CrnThread> dispatchThreadMap = new HashMap<>();
        Map<Integer, CrnProtocol> dispatchProtocolMap = new HashMap<>();
        for (BasCrnp basCrnp : basCrnps) {
            if (basCrnp == null || basCrnp.getCrnNo() == null || crnMoveBlockedCrnNos.contains(basCrnp.getCrnNo())) {
                continue;
            }
            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, basCrnp.getCrnNo());
            if (crnThread == null) {
                continue;
@@ -100,7 +108,7 @@
            long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
                    .eq("crn_no", basCrnp.getCrnNo())
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts));
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts, WrkStsType.CRN_MOVE_RUN.sts));
            if (runningCount > 0) {
                continue;
            }
@@ -655,6 +663,8 @@
                }else if(wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts){
                    updateWrkSts = WrkStsType.COMPLETE_LOC_MOVE.sts;
                    notifyUtils.notify(String.valueOf(SlaveType.Crn), crnProtocol.getCrnNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.CRN_TRANSFER_TASK_COMPLETE, null);
                }else if(wrkMast.getWrkSts() == WrkStsType.CRN_MOVE_RUN.sts){
                    updateWrkSts = WrkStsType.COMPLETE_CRN_MOVE.sts;
                }else{
                    News.error("堆垛机处于等待确认且任务完成状态,但工作状态异常。堆垛机号={},工作号={}", basCrnp.getCrnNo(), crnProtocol.getTaskNo());
                    continue;
@@ -677,9 +687,14 @@
    }
    public synchronized void plannerExecute() {
        Set<Integer> crnMoveBlockedCrnNos = executeCrnMoveTask();
        int nowSec = (int) (System.currentTimeMillis() / 1000);
        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
        for (BasCrnp basCrnp : basCrnps) {
            if (basCrnp == null || basCrnp.getCrnNo() == null || crnMoveBlockedCrnNos.contains(basCrnp.getCrnNo())) {
                continue;
            }
            String key = RedisKeyType.PLANNER_SCHEDULE.key + "CRN-" + basCrnp.getCrnNo();
            List<Object> items = redisUtil.lGet(key, 0, -1);
            if (items == null || items.isEmpty()) {
@@ -696,7 +711,7 @@
            }
            List<WrkMast> running = wrkMastService.list(new QueryWrapper<WrkMast>()
                    .eq("crn_no", basCrnp.getCrnNo())
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts)
                    .in("wrk_sts", WrkStsType.INBOUND_RUN.sts, WrkStsType.OUTBOUND_RUN.sts, WrkStsType.LOC_MOVE_RUN.sts, WrkStsType.CRN_MOVE_RUN.sts)
            );
            if (!running.isEmpty()) {
                continue;
@@ -827,6 +842,114 @@
        return false;
    }
    private Set<Integer> executeCrnMoveTask() {
        List<WrkMast> pendingTaskQueue = wrkMastService.list(new QueryWrapper<WrkMast>()
                .eq("io_type", WrkIoType.CRN_MOVE.id)
                .eq("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts)
                .orderByAsc("appe_time")
                .orderByAsc("wrk_no"));
        Set<Integer> blockedCrnNoSet = new HashSet<>();
        for (WrkMast wrkMast : pendingTaskQueue) {
            if (wrkMast != null && wrkMast.getCrnNo() != null) {
                blockedCrnNoSet.add(wrkMast.getCrnNo());
            }
        }
        if (blockedCrnNoSet.isEmpty()) {
            return blockedCrnNoSet;
        }
        List<BasCrnp> basCrnps = basCrnpService.list(new QueryWrapper<>());
        Map<Integer, CrnThread> dispatchThreadMap = new HashMap<>();
        Map<Integer, CrnProtocol> dispatchProtocolMap = new HashMap<>();
        for (BasCrnp basCrnp : basCrnps) {
            if (basCrnp == null || basCrnp.getCrnNo() == null) {
                continue;
            }
            Integer crnNo = basCrnp.getCrnNo();
            CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
            if (crnThread == null) {
                continue;
            }
            CrnProtocol crnProtocol = crnThread.getStatus();
            if (crnProtocol == null) {
                continue;
            }
            long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
                    .eq("crn_no", crnNo)
                    .in("wrk_sts",
                            WrkStsType.INBOUND_RUN.sts,
                            WrkStsType.OUTBOUND_RUN.sts,
                            WrkStsType.LOC_MOVE_RUN.sts,
                            WrkStsType.CRN_MOVE_RUN.sts));
            if (runningCount > 0) {
                continue;
            }
            if (!Objects.equals(crnProtocol.getMode(), CrnModeType.AUTO.id)
                    || !Objects.equals(crnProtocol.getTaskNo(), 0)
                    || !Objects.equals(crnProtocol.getStatus(), CrnStatusType.IDLE.id)
                    || !Objects.equals(crnProtocol.getLoaded(), 0)
                    || !Objects.equals(crnProtocol.getForkPos(), 0)
                    || !Objects.equals(crnProtocol.getAlarm(), 0)) {
                continue;
            }
            Object clearLock = redisUtil.get(RedisKeyType.CLEAR_CRN_TASK_LIMIT.key + crnNo);
            if (clearLock != null) {
                continue;
            }
            dispatchThreadMap.put(crnNo, crnThread);
            dispatchProtocolMap.put(crnNo, crnProtocol);
        }
        if (dispatchThreadMap.isEmpty()) {
            return blockedCrnNoSet;
        }
        List<WrkMast> taskQueue = wrkMastService.list(new QueryWrapper<WrkMast>()
                .in("crn_no", new ArrayList<>(dispatchThreadMap.keySet()))
                .eq("io_type", WrkIoType.CRN_MOVE.id)
                .eq("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts)
                .orderByAsc("appe_time")
                .orderByAsc("wrk_no"));
        for (WrkMast wrkMast : taskQueue) {
            if (wrkMast == null || wrkMast.getCrnNo() == null || Cools.isEmpty(wrkMast.getLocNo())) {
                continue;
            }
            Integer crnNo = wrkMast.getCrnNo();
            CrnThread crnThread = dispatchThreadMap.get(crnNo);
            if (crnThread == null || dispatchProtocolMap.get(crnNo) == null) {
                continue;
            }
            CrnCommand moveCommand = crnThread.getMoveCommand(wrkMast.getLocNo(), wrkMast.getWrkNo(), crnNo);
            if (moveCommand == null) {
                continue;
            }
            Date now = new Date();
            wrkMast.setWrkSts(WrkStsType.CRN_MOVE_RUN.sts);
            wrkMast.setCrnNo(crnNo);
            wrkMast.setSystemMsg("");
            wrkMast.setIoTime(now);
            wrkMast.setModiTime(now);
            if (wrkMastService.updateById(wrkMast)) {
                wrkAnalysisService.markCraneStart(wrkMast, now);
                MessageQueue.offer(SlaveType.Crn, crnNo, new Task(2, moveCommand));
                News.info("堆垛机移动命令下发成功,堆垛机号={},工作号={},目标位={},任务数据={}",
                        crnNo, wrkMast.getWrkNo(), wrkMast.getLocNo(), JSON.toJSON(moveCommand));
                return blockedCrnNoSet;
            }
        }
        return blockedCrnNoSet;
    }
    //检测浅库位状态
    public synchronized boolean checkShallowLocStatus(String locNo, Integer taskNo) {
        String checkDeepLocOutTaskBlockReport = "Y";
@@ -889,4 +1012,83 @@
        return false;
    }
    //调度堆垛机移动
    public synchronized boolean dispatchCrnMove(Integer crnNo, String targetLocNo) {
        if (crnNo == null || Cools.isEmpty(targetLocNo)) {
            return false;
        }
        int targetRow = Utils.getRow(targetLocNo);
        int targetBay = Utils.getBay(targetLocNo);
        int targetLev = Utils.getLev(targetLocNo);
        try {
            targetRow = Utils.getRow(targetLocNo);
            targetBay = Utils.getBay(targetLocNo);
            targetLev = Utils.getLev(targetLocNo);
        } catch (Exception e) {
            News.error("生成堆垛机移动任务失败,目标位:{} 解析异常", targetLocNo);
            return false;
        }
        CrnThread crnThread = (CrnThread) SlaveConnection.get(SlaveType.Crn, crnNo);
        if (crnThread == null) {
            return false;
        }
        CrnProtocol crnProtocol = crnThread.getStatus();
        if (crnProtocol == null) {
            return false;
        }
        if (crnProtocol.getBay() == targetBay) {
            return false;
        }
        if (crnProtocol.getLevel() == targetLev) {
            return false;
        }
        long runningCount = wrkMastService.count(new QueryWrapper<WrkMast>()
                .eq("crn_no", crnNo)
                .in("wrk_sts",
                        WrkStsType.INBOUND_RUN.sts,
                        WrkStsType.OUTBOUND_RUN.sts,
                        WrkStsType.LOC_MOVE_RUN.sts,
                        WrkStsType.CRN_MOVE_RUN.sts));
        if (runningCount > 0) {
            News.info("堆垛机:{} 存在执行中的任务,暂不生成移动任务", crnNo);
            return false;
        }
        WrkMast activeTask = wrkMastService.getOne(new QueryWrapper<WrkMast>()
                .eq("crn_no", crnNo)
                .eq("io_type", WrkIoType.CRN_MOVE.id)
                .in("wrk_sts", WrkStsType.NEW_CRN_MOVE.sts, WrkStsType.CRN_MOVE_RUN.sts, WrkStsType.CRN_MOVE_MANUAL.sts)
                .orderByDesc("appe_time")
                .last("limit 1"));
        if (activeTask != null) {
            News.info("堆垛机:{} 已存在未完成移动任务,工作号={}", crnNo, activeTask.getWrkNo());
            return false;
        }
        Date now = new Date();
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(commonService.getWorkNo(WrkIoType.CRN_MOVE.id));
        wrkMast.setIoTime(now);
        wrkMast.setWrkSts(WrkStsType.NEW_CRN_MOVE.sts);
        wrkMast.setIoType(WrkIoType.CRN_MOVE.id);
        wrkMast.setIoPri(0D);
        wrkMast.setLocNo(targetLocNo);
        wrkMast.setCrnNo(crnNo);
        wrkMast.setAppeTime(now);
        wrkMast.setModiTime(now);
        wrkMast.setMemo("dispatchCrnMove");
        if (!wrkMastService.save(wrkMast)) {
            News.info("生成堆垛机移动任务失败,工作号={},任务数据={}", wrkMast.getWrkNo(), JSON.toJSON(wrkMast));
            return false;
        }
        wrkAnalysisService.initForTask(wrkMast);
        return true;
    }
}
src/main/resources/sql/20260330_add_crn_move_task_type.sql
New file
@@ -0,0 +1,27 @@
INSERT INTO `asr_bas_wrk_iotype` (`io_type`, `io_pri`, `io_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 301, NULL, '301.堆垛机移动任务', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_iotype` WHERE `io_type` = 301);
INSERT INTO `asr_wrk_lastno` (`wrk_mk`, `wrk_no`, `modi_user`, `modi_time`, `appe_user`, `appe_time`, `s_no`, `e_no`, `memo_m`)
SELECT 301, 30000, NULL, NOW(), NULL, NOW(), 30001, 39999, '堆垛机移动任务号段'
WHERE NOT EXISTS (SELECT 1 FROM `asr_wrk_lastno` WHERE `wrk_mk` = 301);
INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 601, '601.生成堆垛机移动任务', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_status` WHERE `wrk_sts` = 601);
INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 602, '602.堆垛机移动中', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_status` WHERE `wrk_sts` = 602);
INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 603, '603.堆垛机移动完成', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_status` WHERE `wrk_sts` = 603);
INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 606, '606.堆垛机移动待人工回滚', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_status` WHERE `wrk_sts` = 606);
INSERT INTO `asr_bas_wrk_status` (`wrk_sts`, `wrk_desc`, `modi_user`, `modi_time`, `appe_user`, `appe_time`)
SELECT 609, '609.堆垛机移动任务完成', NULL, NOW(), NULL, NOW()
WHERE NOT EXISTS (SELECT 1 FROM `asr_bas_wrk_status` WHERE `wrk_sts` = 609);
src/test/java/com/zy/asrs/task/WrkAnalysisStationArrivalScannerTest.java
@@ -2,21 +2,29 @@
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.BasStationService;
import com.zy.asrs.service.BasCrnpService;
import com.zy.asrs.service.WrkAnalysisService;
import com.zy.asrs.service.WrkMastService;
import com.zy.common.entity.FindCrnNoResult;
import com.zy.common.service.CommonService;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.move.StationMoveCoordinator;
import com.zy.core.move.StationMoveSession;
import com.zy.core.model.CommandResponse;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.thread.StationThread;
import com.zy.core.utils.CrnOperateProcessUtils;
import com.zy.core.utils.StationOperateProcessUtils;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
@@ -25,6 +33,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -36,12 +45,20 @@
        BasStationService basStationService = mock(BasStationService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        StationOperateProcessUtils stationOperateProcessUtils = mock(StationOperateProcessUtils.class);
        StationMoveCoordinator stationMoveCoordinator = mock(StationMoveCoordinator.class);
        CommonService commonService = mock(CommonService.class);
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        CrnOperateProcessUtils crnOperateProcessUtils = mock(CrnOperateProcessUtils.class);
        WrkAnalysisStationArrivalScanner scanner = new WrkAnalysisStationArrivalScanner(
                wrkMastService,
                basStationService,
                wrkAnalysisService,
                stationOperateProcessUtils
                stationOperateProcessUtils,
                stationMoveCoordinator,
                commonService,
                basCrnpService,
                crnOperateProcessUtils
        );
        WrkMast wrkMast = new WrkMast();
@@ -75,6 +92,211 @@
        verify(wrkAnalysisService).completeInboundStationRun(any(WrkMast.class), any(Date.class));
    }
    @Test
    void scanInboundStationArrival_dispatchesCrnMoveWhenOnlyFinalInletStationRemains() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        BasStationService basStationService = mock(BasStationService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        StationOperateProcessUtils stationOperateProcessUtils = mock(StationOperateProcessUtils.class);
        StationMoveCoordinator stationMoveCoordinator = mock(StationMoveCoordinator.class);
        CommonService commonService = mock(CommonService.class);
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        CrnOperateProcessUtils crnOperateProcessUtils = mock(CrnOperateProcessUtils.class);
        WrkAnalysisStationArrivalScanner scanner = new WrkAnalysisStationArrivalScanner(
                wrkMastService,
                basStationService,
                wrkAnalysisService,
                stationOperateProcessUtils,
                stationMoveCoordinator,
                commonService,
                basCrnpService,
                crnOperateProcessUtils
        );
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(1002);
        wrkMast.setIoType(1);
        wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
        wrkMast.setStaNo(12);
        wrkMast.setLocNo("5-6-7");
        BasStation basStation = new BasStation();
        basStation.setStationId(12);
        basStation.setDeviceNo(3);
        StationMoveSession session = new StationMoveSession();
        session.setStatus(StationMoveSession.STATUS_RUNNING);
        session.setCurrentStationId(11);
        session.setFullPathStationIds(new ArrayList<>(List.of(10, 11, 12)));
        FindCrnNoResult findCrnNoResult = new FindCrnNoResult();
        findCrnNoResult.setCrnNo(1);
        findCrnNoResult.setCrnType(SlaveType.Crn);
        BasCrnp basCrnp = new BasCrnp();
        basCrnp.setCrnNo(1);
        basCrnp.setInStationList("[{\"stationId\":12,\"deviceRow\":2,\"deviceBay\":1,\"deviceLev\":1}]");
        when(wrkMastService.list(any(QueryWrapper.class))).thenReturn(List.of(wrkMast));
        when(basStationService.getOne(any())).thenReturn(basStation);
        when(stationMoveCoordinator.loadSession(1002)).thenReturn(session);
        when(commonService.findCrnNoByLocNo("5-6-7")).thenReturn(findCrnNoResult);
        when(basCrnpService.getOne(any())).thenReturn(basCrnp);
        when(crnOperateProcessUtils.dispatchCrnMove(1, "2-1-1")).thenReturn(true);
        ArrivalAwareStationThread stationThread = new ArrivalAwareStationThread(false);
        StationProtocol stationProtocol = new StationProtocol();
        stationProtocol.setStationId(12);
        stationProtocol.setTaskNo(0);
        stationProtocol.setLoading(false);
        stationThread.putStatus(stationProtocol);
        SlaveConnection.put(SlaveType.Devp, 3, stationThread);
        try {
            scanner.scanInboundStationArrival();
        } finally {
            SlaveConnection.remove(SlaveType.Devp, 3);
        }
        verify(crnOperateProcessUtils).dispatchCrnMove(1, "2-1-1");
        verify(wrkAnalysisService, never()).completeInboundStationRun(any(WrkMast.class), any(Date.class));
    }
    @Test
    void scanInboundStationArrival_skipsCrnMoveWhenTargetInletHasLoadTaskAndInEnable() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        BasStationService basStationService = mock(BasStationService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        StationOperateProcessUtils stationOperateProcessUtils = mock(StationOperateProcessUtils.class);
        StationMoveCoordinator stationMoveCoordinator = mock(StationMoveCoordinator.class);
        CommonService commonService = mock(CommonService.class);
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        CrnOperateProcessUtils crnOperateProcessUtils = mock(CrnOperateProcessUtils.class);
        WrkAnalysisStationArrivalScanner scanner = new WrkAnalysisStationArrivalScanner(
                wrkMastService,
                basStationService,
                wrkAnalysisService,
                stationOperateProcessUtils,
                stationMoveCoordinator,
                commonService,
                basCrnpService,
                crnOperateProcessUtils
        );
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(1003);
        wrkMast.setIoType(1);
        wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
        wrkMast.setStaNo(12);
        wrkMast.setLocNo("5-6-7");
        BasStation basStation = new BasStation();
        basStation.setStationId(12);
        basStation.setDeviceNo(3);
        StationMoveSession session = new StationMoveSession();
        session.setStatus(StationMoveSession.STATUS_RUNNING);
        session.setCurrentStationId(11);
        session.setFullPathStationIds(new ArrayList<>(List.of(10, 11, 12)));
        when(wrkMastService.list(any(QueryWrapper.class))).thenReturn(List.of(wrkMast));
        when(basStationService.getOne(any())).thenReturn(basStation);
        when(stationMoveCoordinator.loadSession(1003)).thenReturn(session);
        ArrivalAwareStationThread stationThread = new ArrivalAwareStationThread(false);
        StationProtocol stationProtocol = new StationProtocol();
        stationProtocol.setStationId(12);
        stationProtocol.setTaskNo(9999);
        stationProtocol.setLoading(true);
        stationProtocol.setInEnable(true);
        stationThread.putStatus(stationProtocol);
        SlaveConnection.put(SlaveType.Devp, 3, stationThread);
        try {
            scanner.scanInboundStationArrival();
        } finally {
            SlaveConnection.remove(SlaveType.Devp, 3);
        }
        verify(crnOperateProcessUtils, never()).dispatchCrnMove(any(), any());
        verify(commonService, never()).findCrnNoByLocNo(any());
        verify(wrkAnalysisService, never()).completeInboundStationRun(any(WrkMast.class), any(Date.class));
    }
    @Test
    void scanInboundStationArrival_dispatchesCrnMoveWhenTargetInletOnlyHasLoad() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        BasStationService basStationService = mock(BasStationService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        StationOperateProcessUtils stationOperateProcessUtils = mock(StationOperateProcessUtils.class);
        StationMoveCoordinator stationMoveCoordinator = mock(StationMoveCoordinator.class);
        CommonService commonService = mock(CommonService.class);
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        CrnOperateProcessUtils crnOperateProcessUtils = mock(CrnOperateProcessUtils.class);
        WrkAnalysisStationArrivalScanner scanner = new WrkAnalysisStationArrivalScanner(
                wrkMastService,
                basStationService,
                wrkAnalysisService,
                stationOperateProcessUtils,
                stationMoveCoordinator,
                commonService,
                basCrnpService,
                crnOperateProcessUtils
        );
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(1004);
        wrkMast.setIoType(1);
        wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
        wrkMast.setStaNo(12);
        wrkMast.setLocNo("5-6-7");
        BasStation basStation = new BasStation();
        basStation.setStationId(12);
        basStation.setDeviceNo(3);
        StationMoveSession session = new StationMoveSession();
        session.setStatus(StationMoveSession.STATUS_RUNNING);
        session.setCurrentStationId(11);
        session.setFullPathStationIds(new ArrayList<>(List.of(10, 11, 12)));
        FindCrnNoResult findCrnNoResult = new FindCrnNoResult();
        findCrnNoResult.setCrnNo(1);
        findCrnNoResult.setCrnType(SlaveType.Crn);
        BasCrnp basCrnp = new BasCrnp();
        basCrnp.setCrnNo(1);
        basCrnp.setInStationList("[{\"stationId\":12,\"deviceRow\":2,\"deviceBay\":1,\"deviceLev\":1}]");
        when(wrkMastService.list(any(QueryWrapper.class))).thenReturn(List.of(wrkMast));
        when(basStationService.getOne(any())).thenReturn(basStation);
        when(stationMoveCoordinator.loadSession(1004)).thenReturn(session);
        when(commonService.findCrnNoByLocNo("5-6-7")).thenReturn(findCrnNoResult);
        when(basCrnpService.getOne(any())).thenReturn(basCrnp);
        when(crnOperateProcessUtils.dispatchCrnMove(1, "2-1-1")).thenReturn(true);
        ArrivalAwareStationThread stationThread = new ArrivalAwareStationThread(false);
        StationProtocol stationProtocol = new StationProtocol();
        stationProtocol.setStationId(12);
        stationProtocol.setTaskNo(0);
        stationProtocol.setLoading(true);
        stationProtocol.setInEnable(false);
        stationThread.putStatus(stationProtocol);
        SlaveConnection.put(SlaveType.Devp, 3, stationThread);
        try {
            scanner.scanInboundStationArrival();
        } finally {
            SlaveConnection.remove(SlaveType.Devp, 3);
        }
        verify(crnOperateProcessUtils).dispatchCrnMove(1, "2-1-1");
        verify(wrkAnalysisService, never()).completeInboundStationRun(any(WrkMast.class), any(Date.class));
    }
    private static class ArrivalAwareStationThread implements StationThread {
        private final boolean recentArrival;
src/test/java/com/zy/asrs/task/WrkMastSchedulerCrnMoveTest.java
New file
@@ -0,0 +1,54 @@
package com.zy.asrs.task;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.LocMastService;
import com.zy.asrs.service.WrkAnalysisService;
import com.zy.asrs.service.WrkMastLogService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.utils.NotifyUtils;
import com.zy.core.enums.WrkIoType;
import com.zy.core.enums.WrkStsType;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class WrkMastSchedulerCrnMoveTest {
    @Test
    void executeCrnMove_archivesAndRemovesCompletedCrnMoveTask() {
        WrkMastService wrkMastService = mock(WrkMastService.class);
        WrkMastLogService wrkMastLogService = mock(WrkMastLogService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        LocMastService locMastService = mock(LocMastService.class);
        NotifyUtils notifyUtils = mock(NotifyUtils.class);
        WrkMastScheduler scheduler = new WrkMastScheduler(
                wrkMastService,
                wrkMastLogService,
                wrkAnalysisService,
                locMastService,
                notifyUtils
        );
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(30001);
        wrkMast.setIoType(WrkIoType.CRN_MOVE.id);
        wrkMast.setWrkSts(WrkStsType.COMPLETE_CRN_MOVE.sts);
        when(wrkMastService.list(any(QueryWrapper.class))).thenReturn(List.of(wrkMast));
        when(wrkMastLogService.save(30001)).thenReturn(true);
        when(wrkMastService.removeById(30001)).thenReturn(true);
        scheduler.executeCrnMove();
        verify(wrkMastLogService).save(30001);
        verify(wrkAnalysisService).finishTask(any(WrkMast.class), any());
        verify(wrkMastService).removeById(30001);
    }
}
src/test/java/com/zy/core/utils/CrnOperateProcessUtilsTest.java
New file
@@ -0,0 +1,397 @@
package com.zy.core.utils;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.zy.asrs.entity.BasCrnp;
import com.zy.asrs.entity.LocMast;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.service.BasCrnpService;
import com.zy.asrs.service.LocMastService;
import com.zy.asrs.service.WrkAnalysisService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.utils.NotifyUtils;
import com.zy.common.service.CommonService;
import com.zy.common.utils.RedisUtil;
import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.CrnModeType;
import com.zy.core.enums.CrnStatusType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.WrkIoType;
import com.zy.core.enums.WrkStsType;
import com.zy.core.model.Task;
import com.zy.core.model.command.CrnCommand;
import com.zy.core.model.protocol.CrnProtocol;
import com.zy.core.thread.CrnThread;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class CrnOperateProcessUtilsTest {
    @AfterEach
    void tearDown() {
        safeClearQueue(1);
        safeClearQueue(2);
        SlaveConnection.remove(SlaveType.Crn, 1);
        SlaveConnection.remove(SlaveType.Crn, 2);
        SlaveConnection.remove(SlaveType.Devp, 101);
    }
    @Test
    void dispatchCrnMove_createsFormalTaskOnly() {
        CrnOperateProcessUtils utils = new CrnOperateProcessUtils();
        WrkMastService wrkMastService = mock(WrkMastService.class);
        CommonService commonService = mock(CommonService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        CrnThread crnThread = mock(CrnThread.class);
        ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(utils, "commonService", commonService);
        ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
        when(wrkMastService.count(any())).thenReturn(0L);
        when(wrkMastService.getOne(any())).thenReturn(null);
        when(commonService.getWorkNo(WrkIoType.CRN_MOVE.id)).thenReturn(30001);
        AtomicReference<WrkMast> savedSnapshot = new AtomicReference<>();
        when(wrkMastService.save(any(WrkMast.class))).thenAnswer(invocation -> {
            WrkMast source = invocation.getArgument(0);
            WrkMast snapshot = new WrkMast();
            snapshot.setWrkNo(source.getWrkNo());
            snapshot.setIoType(source.getIoType());
            snapshot.setWrkSts(source.getWrkSts());
            snapshot.setLocNo(source.getLocNo());
            snapshot.setCrnNo(source.getCrnNo());
            savedSnapshot.set(snapshot);
            return true;
        });
        MessageQueue.init(SlaveType.Crn, 1);
        CrnProtocol protocol = buildProtocol(1, CrnStatusType.IDLE.id, 0);
        protocol.setBay(8);
        protocol.setLevel(3);
        when(crnThread.getStatus()).thenReturn(protocol);
        SlaveConnection.put(SlaveType.Crn, 1, crnThread);
        boolean dispatched = utils.dispatchCrnMove(1, "2-1-1");
        assertTrue(dispatched);
        verify(wrkMastService).save(any(WrkMast.class));
        WrkMast saved = savedSnapshot.get();
        assertNotNull(saved);
        assertEquals(30001, saved.getWrkNo());
        assertEquals(WrkIoType.CRN_MOVE.id, saved.getIoType());
        assertEquals(WrkStsType.NEW_CRN_MOVE.sts, saved.getWrkSts());
        assertEquals("2-1-1", saved.getLocNo());
        assertEquals(1, saved.getCrnNo());
        verify(wrkMastService, never()).updateById(any(WrkMast.class));
        verify(wrkAnalysisService, never()).markCraneStart(any(WrkMast.class), any());
        assertNull(MessageQueue.peek(SlaveType.Crn, 1));
    }
    @Test
    void dispatchCrnMove_returnsFalseWhenOtherTaskRunning() {
        CrnOperateProcessUtils utils = new CrnOperateProcessUtils();
        WrkMastService wrkMastService = mock(WrkMastService.class);
        CommonService commonService = mock(CommonService.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(utils, "commonService", commonService);
        ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
        when(wrkMastService.count(any())).thenReturn(1L);
        when(wrkMastService.getOne(any())).thenReturn(null);
        when(wrkMastService.save(any(WrkMast.class))).thenReturn(true);
        when(commonService.getWorkNo(WrkIoType.CRN_MOVE.id)).thenReturn(30001);
        boolean dispatched = utils.dispatchCrnMove(1, "2-1-1");
        assertFalse(dispatched);
        verify(wrkMastService, never()).save(any(WrkMast.class));
        verify(commonService, never()).getWorkNo(anyInt());
        verify(wrkAnalysisService, never()).initForTask(any(WrkMast.class));
    }
    @Test
    void crnIoExecuteNormal_prioritizesCrnMoveTask() {
        CrnOperateProcessUtils utils = new CrnOperateProcessUtils();
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        WrkMastService wrkMastService = mock(WrkMastService.class);
        RedisUtil redisUtil = mock(RedisUtil.class);
        LocMastService locMastService = mock(LocMastService.class);
        NotifyUtils notifyUtils = mock(NotifyUtils.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        CrnThread crnThread = mock(CrnThread.class);
        ReflectionTestUtils.setField(utils, "basCrnpService", basCrnpService);
        ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
        ReflectionTestUtils.setField(utils, "locMastService", locMastService);
        ReflectionTestUtils.setField(utils, "notifyUtils", notifyUtils);
        ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
        when(basCrnpService.list(anyWrapper())).thenReturn(List.of(buildBasCrnp(1, "[[2,3]]", "[2]")));
        when(redisUtil.get(anyString())).thenReturn(null);
        when(wrkMastService.count(any())).thenReturn(0L);
        when(wrkMastService.updateById(any(WrkMast.class))).thenReturn(true);
        WrkMast crnMoveTask = new WrkMast();
        crnMoveTask.setWrkNo(30001);
        crnMoveTask.setCrnNo(1);
        crnMoveTask.setIoType(WrkIoType.CRN_MOVE.id);
        crnMoveTask.setWrkSts(WrkStsType.NEW_CRN_MOVE.sts);
        crnMoveTask.setLocNo("2-1-1");
        crnMoveTask.setIoPri(999D);
        WrkMast locMoveTask = new WrkMast();
        locMoveTask.setWrkNo(20001);
        locMoveTask.setCrnNo(1);
        locMoveTask.setIoType(WrkIoType.LOC_MOVE.id);
        locMoveTask.setWrkSts(WrkStsType.NEW_LOC_MOVE.sts);
        locMoveTask.setSourceLocNo("3-1-1");
        locMoveTask.setLocNo("4-1-1");
        locMoveTask.setIoPri(1D);
        when(wrkMastService.list(anyWrapper()))
                .thenReturn(new ArrayList<>(List.of(crnMoveTask)))
                .thenReturn(new ArrayList<>(List.of(locMoveTask, crnMoveTask)));
        LocMast sourceLoc = new LocMast();
        sourceLoc.setLocNo("3-1-1");
        sourceLoc.setLocSts("R");
        LocMast targetLoc = new LocMast();
        targetLoc.setLocNo("4-1-1");
        targetLoc.setLocSts("S");
        when(locMastService.getById("3-1-1")).thenReturn(sourceLoc);
        when(locMastService.getById("4-1-1")).thenReturn(targetLoc);
        CrnProtocol protocol = buildProtocol(1, CrnStatusType.IDLE.id, 0);
        protocol.setBay(1);
        protocol.setLevel(1);
        when(crnThread.getStatus()).thenReturn(protocol);
        CrnCommand moveCommand = new CrnCommand();
        moveCommand.setCrnNo(1);
        moveCommand.setTaskNo(30001);
        when(crnThread.getMoveCommand("2-1-1", 30001, 1)).thenReturn(moveCommand);
        CrnCommand locMoveCommand = new CrnCommand();
        locMoveCommand.setCrnNo(1);
        locMoveCommand.setTaskNo(20001);
        when(crnThread.getPickAndPutCommand("3-1-1", "4-1-1", 20001, 1)).thenReturn(locMoveCommand);
        MessageQueue.init(SlaveType.Crn, 1);
        SlaveConnection.put(SlaveType.Crn, 1, crnThread);
        utils.crnIoExecuteNormal();
        ArgumentCaptor<WrkMast> updateCaptor = ArgumentCaptor.forClass(WrkMast.class);
        verify(wrkMastService).updateById(updateCaptor.capture());
        assertEquals(30001, updateCaptor.getValue().getWrkNo());
        assertEquals(WrkStsType.CRN_MOVE_RUN.sts, updateCaptor.getValue().getWrkSts());
        verify(wrkAnalysisService).markCraneStart(any(WrkMast.class), any());
        Task task = MessageQueue.peek(SlaveType.Crn, 1);
        assertNotNull(task);
        assertSame(moveCommand, task.getData());
    }
    @Test
    void crnIoExecuteNormal_allowsOtherCraneToRunWhenOneCraneHasMoveTask() {
        CrnOperateProcessUtils utils = new CrnOperateProcessUtils();
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        WrkMastService wrkMastService = mock(WrkMastService.class);
        RedisUtil redisUtil = mock(RedisUtil.class);
        LocMastService locMastService = mock(LocMastService.class);
        NotifyUtils notifyUtils = mock(NotifyUtils.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        CrnThread crnThread1 = mock(CrnThread.class);
        CrnThread crnThread2 = mock(CrnThread.class);
        ReflectionTestUtils.setField(utils, "basCrnpService", basCrnpService);
        ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
        ReflectionTestUtils.setField(utils, "locMastService", locMastService);
        ReflectionTestUtils.setField(utils, "notifyUtils", notifyUtils);
        ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
        when(basCrnpService.list(anyWrapper())).thenReturn(List.of(
                buildBasCrnp(1, "[[2,3]]", "[2]"),
                buildBasCrnp(2, "[[4,5]]", "[4]")
        ));
        when(redisUtil.get(anyString())).thenReturn(null);
        when(wrkMastService.count(any())).thenReturn(0L);
        when(wrkMastService.updateById(any(WrkMast.class))).thenReturn(true);
        WrkMast crnMoveTask = new WrkMast();
        crnMoveTask.setWrkNo(30001);
        crnMoveTask.setCrnNo(1);
        crnMoveTask.setIoType(WrkIoType.CRN_MOVE.id);
        crnMoveTask.setWrkSts(WrkStsType.NEW_CRN_MOVE.sts);
        crnMoveTask.setLocNo("2-1-1");
        WrkMast locMoveTask = new WrkMast();
        locMoveTask.setWrkNo(20002);
        locMoveTask.setCrnNo(2);
        locMoveTask.setIoType(WrkIoType.LOC_MOVE.id);
        locMoveTask.setWrkSts(WrkStsType.NEW_LOC_MOVE.sts);
        locMoveTask.setSourceLocNo("4-1-1");
        locMoveTask.setLocNo("5-1-1");
        locMoveTask.setIoPri(1D);
        when(wrkMastService.list(anyWrapper()))
                .thenReturn(new ArrayList<>(List.of(crnMoveTask)))
                .thenReturn(new ArrayList<>(List.of(crnMoveTask)))
                .thenReturn(new ArrayList<>(List.of(locMoveTask)));
        LocMast sourceLoc = new LocMast();
        sourceLoc.setLocNo("4-1-1");
        sourceLoc.setLocSts("R");
        LocMast targetLoc = new LocMast();
        targetLoc.setLocNo("5-1-1");
        targetLoc.setLocSts("S");
        when(locMastService.getById("4-1-1")).thenReturn(sourceLoc);
        when(locMastService.getById("5-1-1")).thenReturn(targetLoc);
        CrnProtocol protocol1 = buildProtocol(1, CrnStatusType.IDLE.id, 0);
        when(crnThread1.getStatus()).thenReturn(protocol1);
        CrnProtocol protocol2 = buildProtocol(2, CrnStatusType.IDLE.id, 0);
        when(crnThread2.getStatus()).thenReturn(protocol2);
        CrnCommand moveCommand = new CrnCommand();
        moveCommand.setCrnNo(1);
        moveCommand.setTaskNo(30001);
        when(crnThread1.getMoveCommand("2-1-1", 30001, 1)).thenReturn(moveCommand);
        CrnCommand locMoveCommand = new CrnCommand();
        locMoveCommand.setCrnNo(2);
        locMoveCommand.setTaskNo(20002);
        when(crnThread2.getPickAndPutCommand("4-1-1", "5-1-1", 20002, 2)).thenReturn(locMoveCommand);
        MessageQueue.init(SlaveType.Crn, 1);
        MessageQueue.init(SlaveType.Crn, 2);
        SlaveConnection.put(SlaveType.Crn, 1, crnThread1);
        SlaveConnection.put(SlaveType.Crn, 2, crnThread2);
        utils.crnIoExecuteNormal();
        ArgumentCaptor<WrkMast> updateCaptor = ArgumentCaptor.forClass(WrkMast.class);
        verify(wrkMastService, times(2)).updateById(updateCaptor.capture());
        List<WrkMast> updatedList = updateCaptor.getAllValues();
        assertEquals(30001, updatedList.get(0).getWrkNo());
        assertEquals(WrkStsType.CRN_MOVE_RUN.sts, updatedList.get(0).getWrkSts());
        assertEquals(20002, updatedList.get(1).getWrkNo());
        assertEquals(WrkStsType.LOC_MOVE_RUN.sts, updatedList.get(1).getWrkSts());
        Task task1 = MessageQueue.peek(SlaveType.Crn, 1);
        assertNotNull(task1);
        assertSame(moveCommand, task1.getData());
        Task task2 = MessageQueue.peek(SlaveType.Crn, 2);
        assertNotNull(task2);
        assertSame(locMoveCommand, task2.getData());
    }
    @Test
    void crnIoExecuteFinish_marksCrnMoveTaskComplete() {
        CrnOperateProcessUtils utils = new CrnOperateProcessUtils();
        BasCrnpService basCrnpService = mock(BasCrnpService.class);
        WrkMastService wrkMastService = mock(WrkMastService.class);
        RedisUtil redisUtil = mock(RedisUtil.class);
        WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
        CrnThread crnThread = mock(CrnThread.class);
        ReflectionTestUtils.setField(utils, "basCrnpService", basCrnpService);
        ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
        ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
        ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
        when(basCrnpService.list(anyWrapper())).thenReturn(List.of(buildBasCrnp(1, "[[2,3]]", "[2]")));
        when(redisUtil.get(anyString())).thenReturn(null);
        when(wrkMastService.updateById(any(WrkMast.class))).thenReturn(true);
        CrnProtocol protocol = buildProtocol(1, CrnStatusType.WAITING.id, 30001);
        when(crnThread.getStatus()).thenReturn(protocol);
        WrkMast wrkMast = new WrkMast();
        wrkMast.setWrkNo(30001);
        wrkMast.setIoType(WrkIoType.CRN_MOVE.id);
        wrkMast.setWrkSts(WrkStsType.CRN_MOVE_RUN.sts);
        wrkMast.setCrnNo(1);
        when(wrkMastService.selectByWorkNo(30001)).thenReturn(wrkMast);
        CrnCommand resetCommand = new CrnCommand();
        resetCommand.setCrnNo(1);
        resetCommand.setTaskNo(30001);
        when(crnThread.getResetCommand(30001, 1)).thenReturn(resetCommand);
        MessageQueue.init(SlaveType.Crn, 1);
        SlaveConnection.put(SlaveType.Crn, 1, crnThread);
        utils.crnIoExecuteFinish();
        ArgumentCaptor<WrkMast> updateCaptor = ArgumentCaptor.forClass(WrkMast.class);
        verify(wrkMastService, times(1)).updateById(updateCaptor.capture());
        assertEquals(WrkStsType.COMPLETE_CRN_MOVE.sts, updateCaptor.getValue().getWrkSts());
        verify(wrkAnalysisService).markCraneComplete(any(WrkMast.class), any(), eq(WrkStsType.COMPLETE_CRN_MOVE.sts));
        Task task = MessageQueue.peek(SlaveType.Crn, 1);
        assertNotNull(task);
        assertSame(resetCommand, task.getData());
    }
    private BasCrnp buildBasCrnp(int crnNo, String controlRows, String deepRows) {
        BasCrnp basCrnp = new BasCrnp();
        basCrnp.setCrnNo(crnNo);
        basCrnp.setControlRows(controlRows);
        basCrnp.setDeepRows(deepRows);
        return basCrnp;
    }
    private CrnProtocol buildProtocol(int crnNo, int status, int taskNo) {
        CrnProtocol protocol = new CrnProtocol();
        protocol.setCrnNo(crnNo);
        protocol.setMode(CrnModeType.AUTO.id);
        protocol.setTaskNo(taskNo);
        protocol.setStatus(status);
        protocol.setLoaded(0);
        protocol.setForkPos(0);
        protocol.setAlarm(0);
        return protocol;
    }
    private void safeClearQueue(int crnNo) {
        try {
            MessageQueue.clear(SlaveType.Crn, crnNo);
        } catch (Exception ignore) {
        }
    }
    @SuppressWarnings("unchecked")
    private <T> Wrapper<T> anyWrapper() {
        return any(Wrapper.class);
    }
}