自动化立体仓库 - WMS系统
chen.llin
9 天以前 65539f6fb4d836180f784c2c4e0bc441764cd23f
src/main/java/com/zy/asrs/task/AgvScheduler.java
@@ -1,21 +1,27 @@
package com.zy.asrs.task;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.core.common.Cools;
import com.zy.asrs.entity.Task;
import com.zy.asrs.entity.WrkMast;
import com.zy.asrs.mapper.WrkMastMapper;
import com.zy.asrs.entity.WrkMastLog;
import com.zy.asrs.service.TaskService;
import com.zy.asrs.service.WrkMastLogService;
import com.zy.asrs.service.WrkMastService;
import com.zy.asrs.task.handler.AgvHandler;
import com.zy.common.properties.SchedulerProperties;
import com.zy.system.entity.Config;
import com.zy.system.service.ConfigService;
import org.springframework.scheduling.TaskScheduler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
/**
@@ -23,6 +29,7 @@
 * @description AGV交互相关定时任务
 * @createDate 2025/11/18 14:18
 */
@Slf4j
@Component
public class AgvScheduler {
@@ -38,13 +45,29 @@
    @Resource
    private WrkMastMapper wrkMastMapper;
    @Resource
    private WrkMastService wrkMastService;
    @Resource
    private WrkMastLogService wrkMastLogService;
    @Resource
    private SchedulerProperties schedulerProperties;
    /**
     * 呼叫agv搬运
     */
    @Scheduled(cron = "0/5 * * * * ? ")
    private void callAgv() {
        // 查询待呼叫agv任务
        List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().eq("wrk_sts", 7));
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        // 查询待呼叫agv任务,按id升序排序(id最小的优先呼叫)
        List<Task> taskList = taskService.selectList(
            new EntityWrapper<Task>()
                .eq("wrk_sts", 7)
                .orderBy("id", true) // 按id升序,id最小的优先
        );
        if(taskList.isEmpty()) {
            return;
        }
@@ -56,6 +79,9 @@
     */
    @Scheduled(cron = "0/3 * * * * ? ")
    private void createAgvOutTasks() {
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        // 获取呼叫agv配置
        List<Config> configs = configService.selectList(new EntityWrapper<Config>().in("code", "eastCallAgvControl", "westCallAgvControl").eq("status", 1));
@@ -86,6 +112,9 @@
     */
    @Scheduled(cron = "0/10 * * * * ? ")
    private void moveTaskToHistory() {
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        List<Task> taskList = taskService.selectList(new EntityWrapper<Task>().eq("wrk_sts", 9));
        if(taskList.isEmpty()) {
            return;
@@ -93,5 +122,235 @@
        agvHandler.moveTaskToHistory(taskList);
    }
    /**
     * 检查入库成功的任务,完结对应的AGV呼叫单
     * 如果入库任务呼叫AGV后没有收到回调,但工作档已经入库成功,则完结AGV呼叫单
     */
    @Scheduled(cron = "0/10 * * * * ? ")
    private void checkInboundCompletedTasks() {
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        try {
            // 查询入库成功的工作档(状态4:入库完成,入库类型:1,10,53,57)
            List<WrkMast> completedWrkMasts = wrkMastService.selectList(
                new EntityWrapper<WrkMast>()
                    .eq("wrk_sts", 4L)  // 入库完成
                    .in("io_type", 1, 10, 53, 57)  // 入库类型
                    .isNotNull("wrk_no")
            );
            if (completedWrkMasts.isEmpty()) {
                return;
            }
            Date now = new Date();
            int completedCount = 0;
            List<Task> completedTasks = new ArrayList<>();
            for (WrkMast wrkMast : completedWrkMasts) {
                // 查找对应的AGV任务(优先通过wrk_no查询)
                Wrapper<Task> taskWrapper1 = new EntityWrapper<Task>()
                    .eq("task_type", "agv")
                    .eq("wrk_sts", 8L)  // 已呼叫AGV状态
                    .eq("wrk_no", wrkMast.getWrkNo());
                List<Task> agvTasks = taskService.selectList(taskWrapper1);
                // 如果通过wrk_no没找到,且有条码,则通过条码查询
                if (agvTasks.isEmpty() && !Cools.isEmpty(wrkMast.getBarcode())) {
                    Wrapper<Task> taskWrapper2 = new EntityWrapper<Task>()
                        .eq("task_type", "agv")
                        .eq("wrk_sts", 8L)
                        .eq("barcode", wrkMast.getBarcode());
                    agvTasks = taskService.selectList(taskWrapper2);
                }
                for (Task agvTask : agvTasks) {
                    // 确保是入库任务
                    if (agvTask.getIoType() != null &&
                        (agvTask.getIoType() == 1 || agvTask.getIoType() == 10 ||
                         agvTask.getIoType() == 53 || agvTask.getIoType() == 57)) {
                        // 更新AGV任务状态为完成
                        agvTask.setWrkSts(9L);
                        agvTask.setModiTime(now);
                        if (taskService.updateById(agvTask)) {
                            completedTasks.add(agvTask);
                            completedCount++;
                            log.info("入库任务工作档已入库成功,完结AGV呼叫单,taskId:{},wrkNo:{},barcode:{}",
                                agvTask.getId(), wrkMast.getWrkNo(), wrkMast.getBarcode());
                        }
                    }
                }
            }
            // 立即将完成的AGV任务转移到历史表,不保留在Task表中
            if (!completedTasks.isEmpty()) {
                try {
                    agvHandler.moveTaskToHistory(completedTasks);
                    log.info("入库完成,已将{}个AGV任务转移到历史表(不保留在Task表中)", completedTasks.size());
                } catch (Exception e) {
                    log.error("入库完成,转移AGV任务到历史表失败", e);
                }
            }
            if (completedCount > 0) {
                log.info("本次检查完结了{}个入库AGV呼叫单", completedCount);
            }
        } catch (Exception e) {
            log.error("检查入库成功任务并完结AGV呼叫单异常", e);
        }
    }
    /**
     * 检查AGV任务对应的工作档是否已完成或已转历史档并完结
     * 处理被跳过的AGV任务:
     * 1. 如果工作档已完成(wrk_sts=4,5,14,15)或已转历史档并完结,则完结AGV任务
     * 2. 如果工作档和历史档都没有数据,但是AGV已确认接收命令后超过1小时处于搬运中(状态8),也结束AGV任务
     *    注意:只有AGV确认接收命令后(plcStrTime不为空)才开始计时,如果AGV接受命令失败,会继续呼叫AGV
     */
    @Scheduled(cron = "0/10 * * * * ? ")
    private void checkCompletedTasksInHistory() {
        if (!schedulerProperties.isEnabled()) {
            return;
        }
        try {
            // 查询状态为8(已呼叫AGV)的AGV任务
            List<Task> agvTasks = taskService.selectList(
                new EntityWrapper<Task>()
                    .eq("task_type", "agv")
                    .eq("wrk_sts", 8L)  // 已呼叫AGV状态
            );
            if (agvTasks.isEmpty()) {
                return;
            }
            Date now = new Date();
            int completedCount = 0;
            List<Task> completedTasks = new ArrayList<>();
            // 1小时的毫秒数(从AGV确认接收命令开始计时)
            long oneHourInMillis = 60 * 60 * 1000L;
            for (Task agvTask : agvTasks) {
                boolean isCompleted = false;
                String reason = "";
                // 检查工作档是否存在
                WrkMast wrkMast = null;
                if (agvTask.getWrkNo() != null) {
                    wrkMast = wrkMastService.selectOne(
                        new EntityWrapper<WrkMast>().eq("wrk_no", agvTask.getWrkNo())
                    );
                }
                // 检查历史档是否存在(无论工作档是否存在都需要检查)
                WrkMastLog wrkMastLog = null;
                // 优先通过wrk_no查询历史档
                if (agvTask.getWrkNo() != null) {
                    wrkMastLog = wrkMastLogService.selectOne(
                        new EntityWrapper<WrkMastLog>().eq("wrk_no", agvTask.getWrkNo())
                    );
                }
                // 如果通过wrk_no没找到,且有条码,则通过条码查询
                if (wrkMastLog == null && !Cools.isEmpty(agvTask.getBarcode())) {
                    List<WrkMastLog> logList = wrkMastLogService.selectList(
                        new EntityWrapper<WrkMastLog>().eq("barcode", agvTask.getBarcode())
                    );
                    if (!logList.isEmpty()) {
                        wrkMastLog = logList.get(0); // 取第一个
                    }
                }
                // 如果工作档存在,检查是否已完成
                if (wrkMast != null) {
                    Long wrkSts = wrkMast.getWrkSts();
                    Integer ioType = agvTask.getIoType();
                    if (wrkSts != null && ioType != null) {
                        // 入库任务:状态4(入库完成)或5(库存更新完成)
                        if ((ioType == 1 || ioType == 10 || ioType == 53 || ioType == 57) &&
                            (wrkSts == 4L || wrkSts == 5L)) {
                            isCompleted = true;
                            reason = String.format("工作档已完成,状态:%d", wrkSts);
                        }
                        // 出库任务:状态14(已出库未确认)或15(出库更新完成)
                        else if ((ioType == 101 || ioType == 110 || ioType == 103 || ioType == 107) &&
                                 (wrkSts == 14L || wrkSts == 15L)) {
                            isCompleted = true;
                            reason = String.format("工作档已完成,状态:%d", wrkSts);
                        }
                    }
                }
                // 如果工作档不存在或未完成,检查历史档是否已完结
                if (!isCompleted && wrkMastLog != null) {
                    Integer ioType = agvTask.getIoType();
                    long logWrkSts = wrkMastLog.getWrkSts();
                    if (ioType != null) {
                        // 入库任务:状态5(库存更新完成)
                        if ((ioType == 1 || ioType == 10 || ioType == 53 || ioType == 57) &&
                            logWrkSts == 5L) {
                            isCompleted = true;
                            reason = String.format("工作档已转历史档并完结,历史档状态:%d", logWrkSts);
                        }
                        // 出库任务:状态15(出库更新完成)
                        else if ((ioType == 101 || ioType == 110 || ioType == 103 || ioType == 107) &&
                                 logWrkSts == 15L) {
                            isCompleted = true;
                            reason = String.format("工作档已转历史档并完结,历史档状态:%d", logWrkSts);
                        }
                    }
                }
                // 如果工作档和历史档都没有数据,检查是否超过1小时(从AGV确认接收命令开始计时)
                if (!isCompleted && wrkMast == null && wrkMastLog == null) {
                    // 只有AGV确认接收命令后(plcStrTime不为空)才开始计时
                    // 如果plcStrTime为空,说明AGV还没有确认接收命令,会继续呼叫,不结束任务
                    Date agvConfirmedTime = agvTask.getPlcStrTime();
                    if (agvConfirmedTime != null) {
                        long timeDiff = now.getTime() - agvConfirmedTime.getTime();
                        if (timeDiff >= oneHourInMillis) {
                            isCompleted = true;
                            long hours = timeDiff / (60 * 60 * 1000L);
                            long minutes = (timeDiff % (60 * 60 * 1000L)) / (60 * 1000L);
                            reason = String.format("工作档和历史档都不存在,AGV确认接收命令后超过1小时(实际:%d小时%d分钟)处于搬运中,自动结束", hours, minutes);
                        }
                    }
                    // 如果plcStrTime为空,说明AGV还没有确认接收命令,不结束任务,继续等待呼叫
                }
                // 如果已完成,更新AGV任务状态并收集到列表
                if (isCompleted) {
                    agvTask.setWrkSts(9L);
                    agvTask.setModiTime(now);
                    if (taskService.updateById(agvTask)) {
                        completedTasks.add(agvTask);
                        completedCount++;
                        log.info("{},完结AGV呼叫单,taskId:{},wrkNo:{},barcode:{},站点:{}",
                            reason, agvTask.getId(), agvTask.getWrkNo(), agvTask.getBarcode(), agvTask.getStaNo());
                    }
                }
            }
            // 立即将完成的AGV任务转移到历史表,不保留在Task表中
            if (!completedTasks.isEmpty()) {
                try {
                    agvHandler.moveTaskToHistory(completedTasks);
                    log.info("入库/出库完成,已将{}个AGV任务转移到历史表(不保留在Task表中)", completedTasks.size());
                } catch (Exception e) {
                    log.error("入库/出库完成,转移AGV任务到历史表失败", e);
                }
            }
            if (completedCount > 0) {
                log.info("本次检查完结了{}个AGV呼叫单(工作档已完成或已转历史档或确认接收后超时)", completedCount);
            }
        } catch (Exception e) {
            log.error("检查工作档已完成或历史档完结任务并完结AGV呼叫单异常", e);
        }
    }
}