自动化立体仓库 - WMS系统
chen.llin
2 天以前 d7020a8f1ec6f55167b4ad14941de0e28ce3d8c1
src/main/java/com/zy/asrs/task/handler/WorkMastHandler.java
@@ -9,6 +9,9 @@
import com.zy.asrs.service.impl.BasStationServiceImpl;
import com.zy.asrs.task.AbstractHandler;
import com.zy.asrs.task.core.ReturnT;
import com.zy.common.model.enums.WorkNoType;
import com.zy.common.properties.AgvProperties;
import com.zy.common.service.CommonService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -16,6 +19,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@@ -59,6 +63,15 @@
    @Autowired
    private LocCacheDetlService locCacheDetlService;
    @Autowired
    private CommonService commonService;
    @Autowired
    private AgvProperties agvProperties;
    @Autowired
    private WrkMastLogService wrkMastLogService;
    public ReturnT<String> start(WrkMast wrkMast) {
        // 4.入库完成
@@ -590,6 +603,9 @@
            // 14.出库完成
        } else if (task.getWrkSts() == 14) {
            return agvDoOut(task);
            // 15.出库更新完成 - 生成空托/满托出库任务
        } else if (task.getWrkSts() == 15) {
            return generateEmptyOrFullPalletOutTaskForCompleted(task);
        }
        return SUCCESS;
    }
@@ -636,6 +652,7 @@
            if (!taskService.updateById(task)) {
                throw new CoolException("任务状态修改失败!!");
            }
            // 注意:生成空托/满托出库任务的逻辑已移至状态15的处理方法中
        } else if(task.getIoType().equals(53) || task.getIoType().equals(54) || task.getIoType().equals(57)){
            LocCache locCache = locCacheService.selectOne(new EntityWrapper<LocCache>().eq("loc_no", task.getSourceLocNo()));
            if (Objects.isNull(locCache)) {
@@ -669,6 +686,182 @@
        return SUCCESS;
    }
    /**
     * 状态15(出库更新完成)时,生成空托出库或满托出库任务,将托盘放入缓存库位
     * @param completedTask 已完成出库更新的任务(状态15)
     * @return 处理结果
     */
    @Transactional(rollbackFor = Exception.class)
    public ReturnT<String> generateEmptyOrFullPalletOutTaskForCompleted(Task completedTask) {
        // 只处理ioType=101的全板出库任务
        if (!completedTask.getIoType().equals(101)) {
            return SUCCESS;
        }
        // 检查是否已经生成过空托/满托出库任务(避免重复生成)
        List<Task> existingTasks = taskService.selectList(new EntityWrapper<Task>()
                .eq("barcode", completedTask.getBarcode())
                .in("io_type", 110, 101) // 空板出库或全板出库
                .eq("wrk_sts", 7) // 待呼叫AGV状态
        );
        if (!existingTasks.isEmpty()) {
            log.info("任务ID:{}的托盘码:{}已存在空托/满托出库任务,跳过生成", completedTask.getId(), completedTask.getBarcode());
            return SUCCESS;
        }
        try {
            generateEmptyOrFullPalletOutTask(completedTask, null);
            return SUCCESS;
        } catch (Exception e) {
            log.error("状态15时生成空托/满托出库任务失败,任务ID:{},错误:{}", completedTask.getId(), e.getMessage(), e);
            return FAIL.setMsg("生成空托/满托出库任务失败:" + e.getMessage());
        }
    }
    /**
     * 出库完成后,生成空托出库或满托出库任务,将托盘放入缓存库位
     * @param outTask 出库任务
     * @param sourceLocCache 源库位(可为null)
     */
    private void generateEmptyOrFullPalletOutTask(Task outTask, LocCache sourceLocCache) {
        // 判断托盘类型:空托或满托
        boolean isEmptyPallet = "Y".equals(outTask.getEmptyMk());
        Integer ioType = isEmptyPallet ? 110 : 101; // 110=空板出库,101=全板出库(满托出库)
        log.info("出库任务完成,生成{}任务,任务ID:{},托盘码:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId(), outTask.getBarcode());
        // 分配缓存库位(whs_type=2)
        LocCache cacheLoc = locCacheService.selectOne(new EntityWrapper<LocCache>()
                .eq("whs_type", agvProperties.getWhsTypeMapping().getCacheArea()) // whs_type=2 缓存区
                .eq("frozen", 0)
                .eq("loc_sts", LocStsType.LOC_STS_TYPE_O.type) // O.闲置
                .ne("full_plt", isEmptyPallet ? "Y" : "N") // 空托不选满板库位,满托不选空板库位
                .orderAsc(Arrays.asList("row1", "bay1", "lev1"))
                .last("OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY"));
        if (cacheLoc == null) {
            log.warn("没有可用的缓存库位,无法生成{}任务,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId());
            return;
        }
        // 获取出库站点(出库任务的staNo是出库站点,将作为空托/满托出库任务的源站点)
        String outboundStaNo = outTask.getStaNo();
        if (outboundStaNo == null || outboundStaNo.isEmpty()) {
            log.warn("出库任务没有出库站点,无法生成{}任务,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId());
            return;
        }
        // 根据缓存区配置选择站点和机器人组(西侧)
        List<String> cacheStations = agvProperties.getWestStations();
        String robotGroup = agvProperties.getRobotGroupWest();
        if (cacheStations.isEmpty()) {
            log.warn("缓存区没有配置站点,无法生成{}任务,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId());
            return;
        }
        // 检查工作档是否已完成或已转历史档
        boolean workCompleted = false;
        if (outTask.getWrkNo() != null) {
            // 检查工作档是否存在且已完成
            WrkMast wrkMast = wrkMastService.selectOne(
                new EntityWrapper<WrkMast>().eq("wrk_no", outTask.getWrkNo())
            );
            if (wrkMast != null) {
                Long wrkSts = wrkMast.getWrkSts();
                // 出库任务完成状态:14(已出库未确认)或15(出库更新完成)
                if (wrkSts != null && (wrkSts == 14L || wrkSts == 15L)) {
                    workCompleted = true;
                    log.debug("工作档{}已完成,状态:{}", outTask.getWrkNo(), wrkSts);
                }
            } else {
                // 如果工作档不存在,检查历史档
                WrkMastLog wrkMastLog = wrkMastLogService.selectOne(
                    new EntityWrapper<WrkMastLog>().eq("wrk_no", outTask.getWrkNo())
                );
                if (wrkMastLog != null) {
                    long logWrkSts = wrkMastLog.getWrkSts();
                    // 出库任务历史档完成状态:15(出库更新完成)
                    if (logWrkSts == 15L) {
                        workCompleted = true;
                        log.debug("工作档{}已转历史档并完结,历史档状态:{}", outTask.getWrkNo(), logWrkSts);
                    }
                }
            }
        }
        // 检查是否有从该出库站点到缓存区的正在搬运任务(状态8:已呼叫AGV,正在搬运)
        // 出库到缓存区的任务类型:101(全板出库)或110(空板出库)
        List<Task> transportingTasks = taskService.selectList(
            new EntityWrapper<Task>()
                .eq("source_sta_no", outboundStaNo) // 源站点是出库站点
                .in("sta_no", cacheStations) // 目标站点是缓存区站点
                .eq("task_type", "agv")
                .eq("wrk_sts", 8L) // 只检查正在搬运状态的任务
                .in("io_type", 101, 110) // 出库到缓存区的任务类型
        );
        // 如果有正在搬运的任务,且工作档未完成,则不分配缓存库位
        if (!transportingTasks.isEmpty() && !workCompleted) {
            log.info("出库站点{}到缓存区有{}个正在搬运的AGV任务,且工作档未完成,暂不分配缓存库位,等待搬运完成。出库任务ID:{}",
                outboundStaNo, transportingTasks.size(), outTask.getId());
            return; // 有正在搬运的任务且工作档未完成,不分配缓存库位,等待下次检查
        }
        if (!transportingTasks.isEmpty() && workCompleted) {
            log.info("出库站点{}到缓存区有{}个正在搬运的AGV任务,但工作档已完成或已转历史档,允许分配缓存库位。出库任务ID:{}",
                outboundStaNo, transportingTasks.size(), outTask.getId());
        }
        // 选择缓存区目标站点(使用第一个可用站点,或可以优化为选择任务最少的站点)
        String cacheStaNo = cacheStations.get(0);
        // 生成工作号
        int workNo = commonService.getWorkNo(WorkNoType.PAKOUT.type);
        // 创建空托出库/满托出库任务
        Task cacheTask = new Task();
        Date now = new Date();
        cacheTask.setWrkNo(workNo)
                .setIoTime(now)
                .setWrkSts(7L) // 工作状态:7.待呼叫AGV
                .setIoType(ioType) // 110=空板出库,101=全板出库
                .setTaskType("agv")
                .setIoPri(10D)
                .setStaNo(cacheStaNo) // 目标站点(缓存区站点)
                .setSourceStaNo(outboundStaNo) // 源站点(出库站点)
                .setInvWh(robotGroup) // 机器人组(西侧)
                .setFullPlt(isEmptyPallet ? "N" : "Y") // 满板:空托=N,满托=Y
                .setPicking("N")
                .setExitMk("N")
                .setSourceLocNo(null) // 出库任务不需要源库位
                .setLocNo(cacheLoc.getLocNo()) // 目标库位(缓存库位)
                .setEmptyMk(isEmptyPallet ? "Y" : "N") // 空板标记
                .setBarcode(outTask.getBarcode()) // 托盘码
                .setLinkMis("N")
                .setAppeTime(now)
                .setModiTime(now);
        if (!taskService.insert(cacheTask)) {
            log.error("生成{}任务失败,任务ID:{}", isEmptyPallet ? "空托出库" : "满托出库", outTask.getId());
            return;
        }
        // 更新缓存库位状态:O.闲置 → S.入库预约
        cacheLoc.setLocSts(LocStsType.LOC_STS_TYPE_S.type);
        cacheLoc.setBarcode(outTask.getBarcode());
        cacheLoc.setModiTime(now);
        if (!locCacheService.updateById(cacheLoc)) {
            log.error("更新缓存库位状态失败,库位:{}", cacheLoc.getLocNo());
            // 回滚任务
            taskService.deleteById(cacheTask.getId());
            return;
        }
        log.info("成功生成{}任务,任务ID:{},工作号:{},源站点:{},目标站点:{},缓存库位:{}",
                isEmptyPallet ? "空托出库" : "满托出库", cacheTask.getId(), workNo, outboundStaNo, cacheStaNo, cacheLoc.getLocNo());
    }
    @Transactional(rollbackFor = Exception.class)
    public ReturnT<String> agvDoIn(Task wrkMast) {