package com.zy.acs.manager.core.service; 
 | 
  
 | 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 
 | 
import com.zy.acs.common.utils.RedisSupport; 
 | 
import com.zy.acs.framework.common.R; 
 | 
import com.zy.acs.manager.manager.entity.Agv; 
 | 
import com.zy.acs.manager.manager.entity.AgvDetail; 
 | 
import com.zy.acs.manager.manager.entity.Code; 
 | 
import com.zy.acs.manager.manager.entity.Task; 
 | 
import com.zy.acs.manager.manager.enums.StatusType; 
 | 
import com.zy.acs.manager.manager.enums.TaskStsType; 
 | 
import com.zy.acs.manager.manager.enums.TaskTypeType; 
 | 
import com.zy.acs.manager.manager.service.AgvDetailService; 
 | 
import com.zy.acs.manager.manager.service.AgvService; 
 | 
import com.zy.acs.manager.manager.service.CodeService; 
 | 
import com.zy.acs.manager.manager.service.TaskService; 
 | 
import com.zy.acs.manager.system.service.ConfigService; 
 | 
import lombok.extern.slf4j.Slf4j; 
 | 
import org.springframework.beans.factory.annotation.Autowired; 
 | 
import org.springframework.stereotype.Service; 
 | 
  
 | 
import javax.annotation.PostConstruct; 
 | 
import javax.annotation.PreDestroy; 
 | 
import java.util.ArrayList; 
 | 
import java.util.Collections; 
 | 
import java.util.List; 
 | 
import java.util.Map; 
 | 
import java.util.concurrent.*; 
 | 
  
 | 
/** 
 | 
 * Created by vincent on 11/9/2024 
 | 
 */ 
 | 
@Slf4j 
 | 
@Service 
 | 
public class PatrolService { 
 | 
  
 | 
    private static final int SCHEDULE_TIME_INTERVAL = 5; 
 | 
  
 | 
    private static final Map<String, ScheduledFuture<?>> AGV_PATROL_MAP = new ConcurrentHashMap<>(); 
 | 
  
 | 
    private final RedisSupport redis = RedisSupport.defaultRedisSupport; 
 | 
  
 | 
    private ScheduledExecutorService scheduler = null; 
 | 
  
 | 
    @Autowired 
 | 
    private AgvService agvService; 
 | 
    @Autowired 
 | 
    private AgvDetailService agvDetailService; 
 | 
    @Autowired 
 | 
    private TaskService taskService; 
 | 
    @Autowired 
 | 
    private MainLockWrapService mainLockWrapService; 
 | 
    @Autowired 
 | 
    private CodeService codeService; 
 | 
    @Autowired 
 | 
    private MapService mapService; 
 | 
    @Autowired 
 | 
    private LaneService laneService; 
 | 
    @Autowired 
 | 
    private AllocateService allocateService; 
 | 
    @Autowired 
 | 
    private ConfigService configService; 
 | 
  
 | 
    private void executePatrolLogic(String agvNo) { 
 | 
        this.patrolOfMove(agvNo); 
 | 
    } 
 | 
  
 | 
    private void patrolOfMove(String agvNo) { 
 | 
        Agv agv = agvService.selectByUuid(agvNo); 
 | 
        AgvDetail agvDetail = agvDetailService.selectByAgvId(agv.getId()); 
 | 
        if (taskService.count(new LambdaQueryWrapper<Task>() 
 | 
                .eq(Task::getAgvId, agv.getId()) 
 | 
                .and(i -> { 
 | 
                    i.eq(Task::getTaskSts, TaskStsType.WAITING.val()) 
 | 
                            .or().eq(Task::getTaskSts, TaskStsType.ASSIGN.val()) 
 | 
                            .or().eq(Task::getTaskSts, TaskStsType.PROGRESS.val()); 
 | 
                })) > 0) { 
 | 
            return; 
 | 
        } 
 | 
        if (!agvService.judgeEnable(agv.getId())) { 
 | 
            return; 
 | 
        } 
 | 
        Code destinationCode = this.getDestinationCode(agv, agvDetail); 
 | 
        if (null == destinationCode) { 
 | 
            return; 
 | 
        } 
 | 
        if (mainLockWrapService.buildMinorTask(agv, TaskTypeType.MOVE, destinationCode.getData(), null)) { 
 | 
            log.info(agv.getUuid() + "开始走行演示..."); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * 4个地方调用了buildMinorTask,在什么时候、哪里设置task的lane 
 | 
     * ( 
 | 
         * HandlerController, 手动  (手动是否需要判断lane) 
 | 
         * MaintainScheduler, 自动  (一般不需要考虑 lane) 
 | 
         * PatrolService,     自动  (需要预处理 lane) ✔ 
 | 
         * TrafficService,    自动  (寻址时已经处理过 lane) ✔ 
 | 
     * ) 
 | 
     * 评估HandlerController没有调用buildMajorTask,手动创建task的可行性 
 | 
     * agv地图图标变化 
 | 
     */ 
 | 
    public Code getDestinationCode(Agv agv, AgvDetail agvDetail) { 
 | 
        Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class); 
 | 
  
 | 
        Code startCode = codeService.getById(agvDetail.getRecentCode()); 
 | 
  
 | 
        List<String> notInCodeList = new ArrayList<>(); 
 | 
        notInCodeList.add("00000151"); 
 | 
        List<Code> list = codeService.list(new LambdaQueryWrapper<Code>().notIn(Code::getData, notInCodeList)); 
 | 
  
 | 
        Collections.shuffle(list); 
 | 
  
 | 
        for (Code endCode : list) { 
 | 
            // valid lane 
 | 
            if (!allocateService.validCapacityOfLane(agv, endCode)) { 
 | 
                continue; 
 | 
            } 
 | 
  
 | 
            // valid path length 
 | 
            List<String> pathList = mapService.validFeasibility(startCode, endCode); 
 | 
            if (pathList.size() >= 5) { 
 | 
                return endCode; 
 | 
            } 
 | 
        } 
 | 
  
 | 
        return list.stream().findFirst().orElse(null); 
 | 
    } 
 | 
  
 | 
  
 | 
    // --------------------------------------------------------------------------- 
 | 
  
 | 
    public boolean isPatrolling(String agvNo) { 
 | 
        ScheduledFuture<?> scheduledFuture = AGV_PATROL_MAP.get(agvNo); 
 | 
        if (scheduledFuture == null) { 
 | 
            return false; 
 | 
        } 
 | 
        return !scheduledFuture.isCancelled() && !scheduledFuture.isDone(); 
 | 
    } 
 | 
  
 | 
    public R startupPatrol(String agvNo) { 
 | 
        if (AGV_PATROL_MAP.containsKey(agvNo)) { 
 | 
            return R.error("AGV " + agvNo + " 的跑库任务已经在运行中。"); 
 | 
        } 
 | 
  
 | 
        Runnable patrolTask = () -> { 
 | 
            try { 
 | 
                executePatrolLogic(agvNo); 
 | 
            } catch (Exception e) { 
 | 
                log.error("执行AGV " + agvNo + " 跑库任务时发生异常: " + e.getMessage()); 
 | 
                e.printStackTrace(); 
 | 
            } 
 | 
        }; 
 | 
  
 | 
        ScheduledFuture<?> scheduledFuture = scheduler.scheduleAtFixedRate(patrolTask, 0, SCHEDULE_TIME_INTERVAL, TimeUnit.SECONDS); 
 | 
  
 | 
        AGV_PATROL_MAP.put(agvNo, scheduledFuture); 
 | 
        log.info("已启动AGV " + agvNo + " 的跑库任务。"); 
 | 
        return R.ok(); 
 | 
    } 
 | 
  
 | 
  
 | 
    public R shutdownPatrol(String agvNo) { 
 | 
        ScheduledFuture<?> scheduledFuture = AGV_PATROL_MAP.get(agvNo); 
 | 
        if (scheduledFuture == null) { 
 | 
            return R.error("AGV " + agvNo + " 没有正在运行的跑库任务。"); 
 | 
        } 
 | 
  
 | 
        boolean cancelled = scheduledFuture.cancel(true); 
 | 
        if (cancelled) { 
 | 
            AGV_PATROL_MAP.remove(agvNo); 
 | 
            log.info("已停止AGV " + agvNo + " 的跑库任务。"); 
 | 
            return R.ok("已停止AGV " + agvNo + " 的跑库任务。"); 
 | 
        } else { 
 | 
            log.error("未能成功停止AGV " + agvNo + " 的跑库任务。"); 
 | 
            return R.error("未能成功停止AGV " + agvNo + " 的跑库任务。"); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    @PostConstruct 
 | 
    public void init() { 
 | 
        int count = agvService.count(new LambdaQueryWrapper<Agv>().eq(Agv::getStatus, StatusType.ENABLE.val)); 
 | 
        if (count > 0) { 
 | 
            this.scheduler = Executors.newScheduledThreadPool(count); 
 | 
        } 
 | 
    } 
 | 
  
 | 
    @PreDestroy 
 | 
    public void destroy() throws InterruptedException { 
 | 
        for (Map.Entry<String, ScheduledFuture<?>> entry : AGV_PATROL_MAP.entrySet()) { 
 | 
            entry.getValue().cancel(true); 
 | 
        } 
 | 
        scheduler.shutdown(); 
 | 
        if (!scheduler.awaitTermination(5, TimeUnit.SECONDS)) { 
 | 
            scheduler.shutdownNow(); 
 | 
        } 
 | 
    } 
 | 
  
 | 
} 
 |