| package com.zy.acs.manager.core.service; | 
|   | 
| import com.alibaba.fastjson.JSON; | 
| import com.alibaba.fastjson.JSONObject; | 
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 
| import com.zy.acs.framework.common.Cools; | 
| import com.zy.acs.framework.common.SnowflakeIdWorker; | 
| import com.zy.acs.framework.exception.CoolException; | 
| import com.zy.acs.common.domain.AgvAction; | 
| import com.zy.acs.common.domain.AgvActionItem; | 
| import com.zy.acs.common.domain.AgvProtocol; | 
| import com.zy.acs.common.domain.BaseResult; | 
| import com.zy.acs.common.domain.protocol.AGV_11_UP; | 
| import com.zy.acs.common.domain.protocol.AGV_70_UP; | 
| import com.zy.acs.common.domain.protocol.IMessageBody; | 
| import com.zy.acs.common.domain.protocol.action.*; | 
| import com.zy.acs.common.enums.*; | 
| import com.zy.acs.common.utils.Utils; | 
| import com.zy.acs.manager.common.domain.TaskDto; | 
| import com.zy.acs.manager.common.exception.BusinessException; | 
| import com.zy.acs.manager.core.domain.AgvBackpackDto; | 
| import com.zy.acs.manager.core.domain.TaskPosDto; | 
| import com.zy.acs.manager.core.service.astart.MapDataDispatcher; | 
| import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam; | 
| import com.zy.acs.manager.manager.entity.*; | 
| import com.zy.acs.manager.manager.enums.*; | 
| import com.zy.acs.manager.manager.service.*; | 
| import com.zy.acs.manager.manager.service.impl.WebsocketServiceImpl; | 
| import com.zy.acs.manager.system.service.ConfigService; | 
| import lombok.extern.slf4j.Slf4j; | 
| import org.springframework.beans.factory.annotation.Autowired; | 
| import org.springframework.stereotype.Component; | 
| import org.springframework.transaction.annotation.Transactional; | 
| import org.springframework.transaction.interceptor.TransactionAspectSupport; | 
|   | 
| import java.util.*; | 
| import java.util.concurrent.TimeUnit; | 
| import java.util.concurrent.locks.ReentrantLock; | 
| import java.util.stream.Collectors; | 
|   | 
| /** | 
|  * Created by vincent on 2023/6/14 | 
|  */ | 
| @Slf4j | 
| @Component("mainService") | 
| public class MainService { | 
|   | 
|     private static final int LOCK_TIMEOUT = 5; | 
|     private final ReentrantLock lock = new ReentrantLock(Boolean.TRUE); | 
|   | 
|     @Autowired | 
|     private BusService busService; | 
|     @Autowired | 
|     private TaskService taskService; | 
|     @Autowired | 
|     private ActionService actionService; | 
|     @Autowired | 
|     private StaService staService; | 
|     @Autowired | 
|     private LocService locService; | 
|     @Autowired | 
|     private AgvService agvService; | 
|     @Autowired | 
|     private AgvDetailService agvDetailService; | 
|     @Autowired | 
|     private ConfigService configService; | 
|     @Autowired | 
|     private ValidService validService; | 
|     @Autowired | 
|     private CodeService codeService; | 
|     @Autowired | 
|     private MapService mapService; | 
|     @Autowired | 
|     private SnowflakeIdWorker snowflakeIdWorker; | 
|     @Autowired | 
|     private CodeGapService codeGapService; | 
|     @Autowired | 
|     private AgvCmdService agvCmdService; | 
|     @Autowired | 
|     private FuncStaService funcStaService; | 
|     @Autowired | 
|     private MapDataDispatcher mapDataDispatcher; | 
|     @Autowired | 
|     private TravelService travelService; | 
|     @Autowired | 
|     private SegmentService segmentService; | 
|     @Autowired | 
|     private TrafficService trafficService; | 
|     @Autowired | 
|     private AgvModelService agvModelService; | 
|     @Autowired | 
|     private MissionAssignService missionAssignService; | 
|   | 
|     @SuppressWarnings("all") | 
|     @Transactional | 
|     public void generateBusAndTask(OpenBusSubmitParam busSubmitParam, String memo) { | 
|         String batch = busSubmitParam.getBatch(); | 
|         List<TaskDto> taskDtoList = busSubmitParam.getTaskList(); | 
|         if (Cools.isEmpty(taskDtoList)) { | 
|             throw new BusinessException("taskDtoList 不能为空"); | 
|         } | 
|   | 
|         // 优先级排序 | 
|         taskDtoList.sort((o1, o2) -> o2.getPriority() - o1.getPriority()); | 
|   | 
|         // 校验 | 
|         List<Task> taskList = validService.validBusDto(taskDtoList); | 
|   | 
|         // 保存总线 | 
|         Date now = new Date(); | 
|         Bus bus = new Bus(); | 
|         bus.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|         bus.setSeqNum(batch); | 
|         bus.setStartTime(now); | 
|         bus.setBusSts(BusStsType.RECEIVE.val()); | 
|         bus.setMemo(memo); | 
|   | 
|         if (!busService.save(bus)) { | 
|             throw new BusinessException("任务保存失败"); | 
|         } | 
|   | 
|         // 保存任务 | 
|         for (Task task : taskList) { | 
|             task.setBusId(bus.getId()); | 
|             task.setTaskSts(TaskStsType.INIT.val()); | 
|             if (!taskService.save(task)) { | 
|                 throw new BusinessException(task.getSeqNum() + "任务保存失败"); | 
|             } | 
|   | 
|             // 修改库位状态 | 
|             Loc oriLoc = null; | 
|             Loc destLoc = null; | 
|             Sta oriSta = null; | 
|             Sta destSta = null; | 
|             switch (TaskTypeType.get(task.getTaskTypeEl())) { | 
|                 case LOC_TO_LOC: | 
|                     oriLoc = locService.getById(task.getOriLoc()); | 
|                     if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) { | 
|                         throw new BusinessException("oriLoc:" + task.getOriLoc$() + " 不是在库状态"); | 
|                     } | 
|                     oriLoc.setLocSts(LocStsType.PAKOUT.val()); | 
|                     oriLoc.setUpdateTime(now); | 
|                     if (!locService.updateById(oriLoc)) { | 
|                         throw new BusinessException("oriLoc:" + task.getOriLoc$() + " 修改库位状态失败"); | 
|                     } | 
|   | 
|                     destLoc = locService.getById(task.getDestLoc()); | 
|                     if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) { | 
|                         throw new BusinessException("destLoc:" + task.getDestLoc$() + " 不是空闲状态"); | 
|                     } | 
|                     destLoc.setLocSts(LocStsType.PAKIN.val()); | 
|                     destLoc.setUpdateTime(now); | 
|                     if (!locService.updateById(destLoc)) { | 
|                         throw new BusinessException("destLoc:" + task.getDestLoc$() + " 修改库位状态失败"); | 
|                     } | 
|                     break; | 
|                 case LOC_TO_STA: | 
|                     oriLoc = locService.getById(task.getOriLoc()); | 
|                     if (!oriLoc.getLocSts().equals(LocStsType.STOCK.val())) { | 
|                         throw new BusinessException("oriLoc:" + task.getOriLoc$() + " 不是在库状态"); | 
|                     } | 
|                     oriLoc.setLocSts(LocStsType.PAKOUT.val()); | 
|                     oriLoc.setUpdateTime(now); | 
|                     if (!locService.updateById(oriLoc)) { | 
|                         throw new BusinessException("oriLoc:" + task.getOriLoc$() + " 修改库位状态失败"); | 
|                     } | 
|                     break; | 
|                 case STA_TO_LOC: | 
|                     destLoc = locService.getById(task.getDestLoc()); | 
|                     if (!destLoc.getLocSts().equals(LocStsType.IDLE.val())) { | 
|                         throw new BusinessException("destLoc:" + task.getDestLoc$() + " 不是空闲状态"); | 
|                     } | 
|                     destLoc.setLocSts(LocStsType.PAKIN.val()); | 
|                     destLoc.setUpdateTime(now); | 
|                     if (!locService.updateById(destLoc)) { | 
|                         throw new BusinessException("destLoc:" + task.getDestLoc$() + " 修改库位状态失败"); | 
|                     } | 
|                     break; | 
|                 case STA_TO_STA: | 
|                     break; | 
|                 default: | 
|                     break; | 
|             } | 
|   | 
|         } | 
|   | 
|     } | 
|   | 
|     /** | 
|      * 任务分配给车辆 ( 车辆此时是空闲且静止的 ) | 
|      */ | 
|     @Transactional | 
|     public synchronized void infuseAgvForTask(Bus bus) { | 
|         try { | 
|             Date now = new Date(); | 
|             List<Task> taskList = taskService.list(new LambdaQueryWrapper<Task>() | 
|                     .eq(Task::getBusId, bus.getId()) | 
|                     .eq(Task::getTaskSts, TaskStsType.INIT.val()) | 
|                     .orderByDesc(Task::getPriority) | 
|             ); | 
|   | 
|             if (Cools.isEmpty(taskList)) { | 
|                 bus.setBusSts(BusStsType.PROGRESS.val()); | 
|                 bus.setUpdateTime(now); | 
|                 if (!busService.updateById(bus)) { | 
|                     log.error("Bus [{}] 更新失败 !!!", bus.getUuid()); | 
|                 } | 
|                 return; | 
|             } | 
|             List<Long> taskIds = taskList.stream().map(Task::getId).distinct().collect(Collectors.toList()); | 
|   | 
|             Map<String, List<Long>> taskAllot = new HashMap<>(); | 
|             for (Task task : taskList) { | 
|                 Agv agv = missionAssignService.execute(task, taskAllot, taskIds); | 
|                 if (null == agv) { | 
|                     log.warn("{}任务异常,无法检索空闲Agv...", task.getSeqNum()); | 
|                     continue; | 
|                 } | 
|   | 
|   | 
|                 task.setAgvId(agv.getId()); | 
|                 task.setTaskSts(TaskStsType.WAITING.val()); | 
|                 task.setIoTime(now); | 
|                 task.setUpdateTime(now); | 
|                 if (!taskService.updateById(task)) { | 
|                     throw new BusinessException(task.getSeqNum() + "任务更新失败"); | 
|                 } | 
|   | 
|                 if (taskAllot.containsKey(agv.getUuid())) { | 
|                     taskAllot.get(agv.getUuid()).add(task.getId()); | 
|                 } else { | 
|                     taskAllot.put(agv.getUuid(), Utils.singletonList(task.getId())); | 
|                 } | 
|             } | 
|   | 
| //            bus.setUpdateTime(now); | 
| //            if (!busService.updateById(bus)) { | 
| //                throw new BusinessException(bus.getSeqNum() + "总线更新失败"); | 
| //            } | 
|         } catch (Exception e) { | 
|             log.error("mainService.infuseAgvForTask", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * 解析取放货集合任务,进行最优的排列组合顺序 ( 车辆此时是空闲且静止的 ) | 
|      */ | 
|     @Transactional | 
|     public synchronized void mergeMajorTask(Long agvId, List<Task> taskList) { | 
|         if (Cools.isEmpty(taskList)) { return; } | 
|         boolean lockAcquired = false; | 
|         try { | 
|             if (!(lockAcquired = this.lock.tryLock(LOCK_TIMEOUT, TimeUnit.SECONDS))) { | 
|                 throw new CoolException("generate [task] action fail, cause can not acquire lock ..."); | 
|             } | 
|             Date now = new Date(); | 
|             final String sameGroupXy = configService.getVal( "sameGroupXy", String.class); | 
|   | 
|             Agv agv = agvService.getById(agvId); | 
|             if (!agvService.judgeEnable(agv.getId(), agvDetail -> agvDetail.getVol() > agv.getChargeLine())) { | 
|                 throw new CoolException("AGV[" + agv.getUuid() + "]当前不可用..."); | 
|             } | 
|   | 
|             if (!Cools.isEmpty(taskService.selectInSts(agvId, TaskStsType.ASSIGN, TaskStsType.PROGRESS))) { | 
|                 throw new CoolException("AGV[" + agv.getUuid() + "]分配任务失败,已存在执行任务..."); | 
|             } | 
|   | 
|             AgvDetail agvDetail = agvDetailService.selectByAgvId(agvId); | 
|             assert agvDetail != null; | 
|             if (agvDetail.getPos() == 0) { | 
|                 if (!agvDetail.getAgvStatus().equals(AgvStatusType.CHARGE)) { | 
|                     throw new CoolException("AGV[" + agv.getUuid() + "]当前不在定位..."); | 
|                 } | 
|             } | 
|   | 
|             // sort and sub | 
|             taskList.sort(new Comparator<Task>() { | 
|                 @Override | 
|                 public int compare(Task o1, Task o2) { | 
|                     return 0; | 
|                 } | 
|             }); | 
|             if (taskList.size() > agv.getStage()) { | 
|                 taskList = taskList.subList(0, agv.getStage()); | 
|             } | 
|   | 
|             /** | 
|              * single agvId | 
|              * | 
|              * key: y + TaskPosDto.PosType.ORI_LOC / ORI_STA / DEST_LOC / DEST_STA | 
|              * val: new TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType) | 
|              */ | 
|             Map<String, List<TaskPosDto>> groups = new HashMap<>(); | 
|   | 
|             List<AgvBackpackDto> backpackDtoList = new ArrayList<>(); | 
|   | 
|             int backpackLev = 0; | 
|             for (Task task : taskList) { | 
|   | 
|                 backpackLev ++; | 
|                 Code startCode = null; | 
|                 Code endCode = null; | 
|                 Loc oriLoc = null; Loc destLoc = null; | 
|                 Sta oriSta = null; Sta destSta = null; | 
|                 switch (Objects.requireNonNull(TaskTypeType.get(task.getTaskTypeEl()))) { | 
|                     case LOC_TO_LOC: | 
|                         oriLoc = locService.getById(task.getOriLoc()); | 
|                         destLoc = locService.getById(task.getDestLoc()); | 
|   | 
|                         startCode = codeService.getById(oriLoc.getCode()); | 
|                         endCode = codeService.getById(destLoc.getCode()); | 
|   | 
|                         TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_LOC, sameGroupXy); | 
|                         TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_LOC, sameGroupXy); | 
|                         break; | 
|                     case LOC_TO_STA: | 
|                         oriLoc = locService.getById(task.getOriLoc()); | 
|                         destSta = staService.getById(task.getDestSta()); | 
|   | 
|                         startCode = codeService.getById(oriLoc.getCode()); | 
|                         endCode = codeService.getById(destSta.getCode()); | 
|   | 
|                         TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_LOC, sameGroupXy); | 
|                         TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_STA, sameGroupXy); | 
|                         break; | 
|                     case STA_TO_LOC: | 
|                         oriSta = staService.getById(task.getOriSta()); | 
|                         destLoc = locService.getById(task.getDestLoc()); | 
|   | 
|                         startCode = codeService.getById(oriSta.getCode()); | 
|                         endCode = codeService.getById(destLoc.getCode()); | 
|   | 
|                         TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_STA, sameGroupXy); | 
|                         TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_LOC, sameGroupXy); | 
|                         break; | 
|                     case STA_TO_STA: | 
|                         oriSta = staService.getById(task.getOriSta()); | 
|                         destSta = staService.getById(task.getDestSta()); | 
|   | 
|                         startCode = codeService.getById(oriSta.getCode()); | 
|                         endCode = codeService.getById(destSta.getCode()); | 
|   | 
|                         TaskPosDto.packagePosGroup(groups, task, startCode, TaskPosDto.PosType.ORI_STA, sameGroupXy); | 
|                         TaskPosDto.packagePosGroup(groups, task, endCode, TaskPosDto.PosType.DEST_STA, sameGroupXy); | 
|                         break; | 
|                     default: | 
|                         throw new BusinessException(task.getSeqNum() + "任务类型错误"); | 
|   | 
|                 } | 
|   | 
|                 if (backpackLev > agv.getStage()) { | 
|                     throw new BusinessException("解析Task失败,AGV背篓已满......"); | 
|                 } | 
|   | 
|                 backpackDtoList.add(new AgvBackpackDto(backpackLev, task.getId())); | 
|             } | 
|   | 
|             /** | 
|              *  List<TaskPosDto>: task list on the same channel | 
|              *  ArrayList<List<TaskPosDto>>: all the task list by one agv | 
|              */ | 
|             ArrayList<List<TaskPosDto>> list = new ArrayList<>(groups.values()); | 
|             list.sort((o1, o2) -> { | 
|                 double o1CompVal = (o1.get(0).getFirstWeight(sameGroupXy) * 100) + o1.get(0).getPosType().compOffset; | 
|                 double o2CompVal = (o2.get(0).getFirstWeight(sameGroupXy) * 100) + o2.get(0).getPosType().compOffset; | 
|                 return (int) (o1CompVal - o2CompVal); | 
|             }); | 
|   | 
|             for (List<TaskPosDto> taskPosDtoList : list) { | 
|                 taskPosDtoList.sort((o1, o2) -> (int) (o1.getSecondWeight(sameGroupXy) * 100 - o2.getSecondWeight(sameGroupXy) * 100)); | 
|             } | 
|   | 
|             // generate travel | 
|             Travel travel = new Travel(); | 
|             travel.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             travel.setTravelId(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             travel.setAgvId(agvId); | 
|             travel.setTaskContent(JSON.toJSONString(list)); | 
|             travel.setTaskIds(JSON.toJSONString(taskList.stream().map(Task::getId).collect(Collectors.toList()))); | 
|             travel.setState(TravelStateType.RUNNING.toString()); | 
|             if (!travelService.save(travel)) { | 
|                 throw new BusinessException("任务组保存失败"); | 
|             } | 
|   | 
|             // generate segment | 
|             int segSerial = 0; | 
|             List<Segment> segmentList = new ArrayList<>(); | 
|             for (List<TaskPosDto> dtoList : list) { | 
|                 for (TaskPosDto taskPosDto : dtoList) { | 
|                     segSerial ++; | 
|                     AgvBackpackType backpackType = AgvBackpackDto.find(backpackDtoList, taskPosDto.getTaskId()); | 
|                     assert null != backpackType; | 
|   | 
|                     Segment segment = new Segment(); | 
|                     segment.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|                     segment.setTravelId(travel.getId()); | 
|                     segment.setAgvId(agvId); | 
|                     segment.setTaskId(taskPosDto.getTaskId()); | 
|                     segment.setSerial(segSerial); | 
|                     segment.setEndNode(taskPosDto.getCodeId()); | 
|                     segment.setPosType(taskPosDto.getPosType().toString()); | 
|                     segment.setBackpack(backpackType.lev); | 
|                     segment.setState(SegmentStateType.INIT.toString()); | 
|                     segmentList.add(segment); | 
|                 } | 
|             } | 
|             for (int i = 0; i < segmentList.size(); i++) { | 
|                 Segment segment = segmentList.get(i); | 
|                 if (i == 0) { | 
|                     segment.setState(SegmentStateType.WAITING.toString()); | 
|                 } | 
|                 if (!segmentService.save(segment)) { | 
|                     throw new BusinessException("任务组保存失败"); | 
|                 } | 
|             } | 
|   | 
|             // task | 
|             for (Task task : taskList) { | 
|                 task.setTaskSts(TaskStsType.ASSIGN.val()); | 
|                 task.setUpdateTime(now); | 
|                 if (!taskService.updateById(task)) { | 
|                     throw new BusinessException(task.getUuid() + "任务更新失败"); | 
|                 } | 
|             } | 
|   | 
|         } catch (Exception e) { | 
|   | 
|             log.error("mainService.mergeMajorTask[task]", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|         } finally { | 
|   | 
|             if (lockAcquired) { | 
|                 this.lock.unlock(); | 
|             } | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * 充电 回待机位任务 | 
|      */ | 
|     @Transactional | 
|     public synchronized boolean buildMinorTask(Agv agv, AgvDetail agvDetail, TaskTypeType taskType, String destination) { | 
|         if (Cools.isEmpty(agv, taskType)) { return false; } | 
|         boolean lockAcquired = false; | 
|         try { | 
|             if (!(lockAcquired = this.lock.tryLock(LOCK_TIMEOUT, TimeUnit.SECONDS))) { | 
|                 throw new CoolException("generate [task] action fail, cause can not acquire lock ..."); | 
|             } | 
|   | 
|             if (null == agvDetail) { | 
|                 agvDetail = agvDetailService.selectByAgvId(agv.getId()); | 
|             } | 
|             if (!agvService.judgeEnable(agv.getId())) { | 
|                 return false; | 
|             } | 
|   | 
|             Date now = new Date(); | 
|             Code endCode = null; | 
|             switch (taskType) { | 
|                 case TO_CHARGE: | 
|                 case TO_STANDBY: | 
|                     List<FuncSta> funcStaList = funcStaService.list(new LambdaQueryWrapper<FuncSta>() | 
|                                     .eq(FuncSta::getType, FuncStaType.query(taskType).toString()) | 
|                                     .eq(FuncSta::getAgvId, agv.getId()) | 
|                                     .eq(FuncSta::getState, FuncStaStateType.IDLE.toString()) | 
|                                     .eq(FuncSta::getStatus, StatusType.ENABLE.val) | 
|                     ); | 
|                     if (!Cools.isEmpty(funcStaList)) { | 
|                         FuncSta funcSta = funcStaList.get(0); | 
|                         endCode = codeService.getById(funcSta.getCode()); | 
|                     } | 
|                     break; | 
|                 case MOVE: | 
|                     endCode = codeService.selectByData(destination); | 
|                     break; | 
|                 default: | 
|                     return false; | 
|             } | 
|   | 
|             if (null == endCode) { | 
|                 log.error("{}号车辆建立功能任务失败,无法检索到目标定位,TaskTypeType:{}", agv.getUuid(), taskType.toString()); | 
|                 return false; | 
|             } | 
|   | 
|             Task task = new Task(); | 
|             task.setAgvId(agv.getId()); | 
|             task.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             List<Task> lastTasks = taskService.list(new LambdaQueryWrapper<Task>().orderByDesc(Task::getId)); | 
|             task.setSeqNum(Utils.generateSeqNum(Cools.isEmpty(lastTasks)?null:lastTasks.get(0).getSeqNum())); | 
|             task.setOriCode(agvDetail.getCode()); | 
|             task.setDestCode(endCode.getId()); | 
|             task.setPriority(taskType.equals(TaskTypeType.TO_CHARGE)?2:1); | 
|             task.setTaskSts(TaskStsType.ASSIGN.val()); | 
|             task.setTaskType(taskType.val()); | 
|             task.setIoTime(now); | 
|             task.setStartTime(now); | 
|             if (!taskService.save(task)) { | 
|                 throw new BusinessException(task.getSeqNum() + "任务保存失败"); | 
|             } | 
|   | 
|             // generate travel | 
|             Travel travel = new Travel(); | 
|             travel.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             travel.setTravelId(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             travel.setAgvId(agv.getId()); | 
| //            travel.setTaskContent(JSON.toJSONString(list)); | 
|             travel.setTaskIds(JSON.toJSONString(Collections.singletonList(task.getId()))); | 
|             travel.setState(TravelStateType.RUNNING.toString()); | 
|             if (!travelService.save(travel)) { | 
|                 throw new BusinessException("任务组保存失败"); | 
|             } | 
|   | 
|             // generate segment | 
|             int segSerial = 0;     segSerial ++; | 
|             List<Segment> segmentList = new ArrayList<>(); | 
|   | 
|             String posType = ""; | 
|             switch (taskType){ | 
|                 case TO_CHARGE: | 
|                     posType = TaskPosDto.PosType.TO_CHARGE.toString(); | 
|                     break; | 
|                 case TO_STANDBY: | 
|                     posType = TaskPosDto.PosType.TO_STANDBY.toString(); | 
|                     break; | 
|                 case MOVE: | 
|                     posType = TaskPosDto.PosType.MOVE.toString(); | 
|                     break; | 
|                 default: | 
|                     break; | 
|             } | 
|   | 
|             Segment segment = new Segment(); | 
|             segment.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             segment.setTravelId(travel.getId()); | 
|             segment.setAgvId(agv.getId()); | 
|             segment.setTaskId(task.getId()); | 
|             segment.setSerial(segSerial); | 
|             segment.setEndNode(endCode.getId()); | 
|             segment.setPosType(posType); | 
|             segment.setState(SegmentStateType.INIT.toString()); | 
|             segmentList.add(segment); | 
|   | 
|             for (int i = 0; i < segmentList.size(); i++) { | 
|                 Segment next = segmentList.get(i); | 
|                 if (i == 0) { | 
|                     next.setState(SegmentStateType.WAITING.toString()); | 
|                 } | 
|                 if (!segmentService.save(next)) { | 
|                     throw new BusinessException("任务组保存失败"); | 
|                 } | 
|             } | 
|   | 
|             switch (taskType) { | 
|                 case TO_CHARGE: | 
|                 case TO_STANDBY: | 
|                     // funcSta | 
|                     FuncSta destFuncSta = funcStaService.getByCodeAndType(task.getDestCode(), FuncStaType.query(taskType).toString()); | 
|                     destFuncSta.setState(FuncStaStateType.OCCUPIED.toString()); | 
|                     destFuncSta.setUpdateTime(now); | 
|                     if (!funcStaService.updateById(destFuncSta)) { | 
|                         log.error("FuncSta [{}] 更新状态失败 !!!", destFuncSta.getName()); | 
|                     } | 
|                     break; | 
|                 case MOVE: | 
|                     break; | 
|                 default: | 
|                     break; | 
|             } | 
|   | 
|             return true; | 
|         } catch (Exception e) { | 
|             log.error("mainService.buildMinorTask[task]", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|   | 
|             return false; | 
|         } finally { | 
|   | 
|             if (lockAcquired) { | 
|                 this.lock.unlock(); | 
|             } | 
|         } | 
|     } | 
|   | 
|     /** | 
|      * 根据分片生成动作 ( 车辆可能已经做过一些任务了,正在等待下一段任务 ) | 
|      */ | 
|     @Transactional | 
|     public synchronized void generateAction(Long agvId, List<Segment> segmentList, List<String> pathList) { | 
|         try { | 
|             if (Cools.isEmpty(agvId, segmentList)) { return; } | 
|             Date now = new Date(); | 
|             JSONObject storeDirection = configService.getVal("storeDirection", JSONObject.class); | 
|             Agv agv = agvService.getById(agvId); | 
|             if (!agvService.judgeEnable(agv.getId())) { | 
|                 throw new CoolException("AGV[" + agv.getUuid() + "]当前不可用..."); | 
|             } | 
|   | 
|             AgvModel agvModel = agvModelService.getById(agv.getAgvModel()); | 
|             Double workDirection = agvModel.getWorkDirection(); | 
|             AgvSpeedType agvSpeedType = AgvSpeedType.query(agvModel.getTravelSpeed()); | 
|             assert agvSpeedType != null; | 
|   | 
|             AgvDetail agvDetail = agvDetailService.selectByAgvId(agvId); | 
|             assert agvDetail != null; | 
|             if (agvDetail.getPos() == 0) { | 
|                 if (!agvDetail.getAgvStatus().equals(AgvStatusType.CHARGE)) { | 
|                     throw new CoolException("AGV[" + agv.getUuid() + "]当前不在定位..."); | 
|                 } | 
|             } | 
|   | 
|             List<Action> actionList = new ArrayList<>(); | 
|             // start node | 
|             Code lastCode = codeService.getById(agvDetail.getRecentCode()); | 
|             if (!lastCode.getData().equals(pathList.get(0))) { | 
|                 throw new CoolException("AGV[" + agv.getUuid() + "]定位偏移..."); | 
|             } | 
|   | 
|             Double lastDirection = agvDetail.getAgvAngle(); | 
|             boolean first = true; | 
|   | 
|             for (Segment segment : segmentList) { | 
|   | 
|                 // 分段所属的Task | 
|                 Task task = taskService.getById(segment.getTaskId()); | 
|   | 
|                 // 节点条码 | 
|                 Code code = codeService.getById(segment.getEndNode()); | 
|   | 
|                 // 需要走行 | 
|                 if (!lastCode.getData().equals(code.getData())) { | 
|   | 
|                     // 走行路径节点 | 
| //                    List<String> pathList = mapService.checkoutPath(agv.getUuid(), lastCode, code); | 
|                     List<String> pathListPart = pathList.subList(pathList.indexOf(lastCode.getData()), pathList.indexOf(code.getData()) + 1); | 
|   | 
|                     for (int i = 0; i < pathListPart.size(); i++) { | 
|                         if (i == 0) { continue; } | 
|   | 
|                         String next = pathListPart.get(i); | 
|   | 
|                         Code nextCode = codeService.selectByData(next); | 
|                         Double nextDirection = mapService.calculateDirection(lastCode, nextCode); | 
|   | 
|                         boolean reverse = false; | 
|                         if (nextDirection.equals((workDirection + 180) % 360)) { | 
|                             nextDirection = workDirection; | 
|                             reverse = true; | 
|                         } | 
|                         if (!lastDirection.equals(nextDirection) || actionList.size() == 0) { | 
|                             // turn | 
|                             actionList.add(new Action( | 
|                                     null,    // 编号 | 
|                                     task.getBusId(),    // 总线 | 
|                                     task.getId(),    // 任务 | 
|                                     null,    // 动作号 | 
|                                     null,    // 优先级 | 
|                                     ActionTypeType.TurnCorner.desc,    // 名称 | 
|                                     mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值 | 
|                                     lastCode.getData(),    // 地面码 | 
|                                     String.valueOf(nextDirection),   // 动作参数 | 
|                                     ActionTypeType.TurnCorner.val(),    // 动作类型 | 
|                                     ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                     agvId,    // AGV | 
|                                     now    // 工作时间 | 
|                             )); | 
|   | 
|                             lastDirection = nextDirection; | 
|   | 
|                         } | 
|   | 
|                         // run | 
|                         ActionTypeType actionType = ActionTypeType.StraightAheadTurnable; | 
|                         if (reverse) { | 
|                             actionType = ActionTypeType.StraightBackTurnable; | 
|                         } | 
|                         CodeGap gap = codeGapService.findByCodeOfBoth(lastCode.getId(), nextCode.getId()); | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 actionType.desc,    // 名称 | 
|                                 (double) agvSpeedType.val,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(gap.getDistance()),   // 动作参数 | 
|                                 actionType.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|   | 
|                         lastCode = nextCode; | 
|   | 
|                     } | 
|   | 
|                 } | 
|   | 
|                 // 初始方向值补丁 | 
|                 if (first) { | 
|                     if (Cools.isEmpty(actionList) || !actionList.get(0).getActionType().equals(ActionTypeType.TurnCorner.val())) { | 
|                         // turn | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 ActionTypeType.TurnCorner.desc,    // 名称 | 
|                                 mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(workDirection),   // 动作参数 | 
|                                 ActionTypeType.TurnCorner.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|   | 
|                         lastDirection = workDirection; | 
|   | 
|                     } | 
|                     first = false; | 
|                 } | 
|   | 
|                 // 作业点动作 | 
|                 AgvDirectionType agvDirectionType; | 
|                 AgvBackpackType backpackType = AgvBackpackType.query(segment.getBackpack()); | 
|                 switch (Objects.requireNonNull(TaskPosDto.queryPosType(segment.getPosType()))) { | 
|                     case ORI_LOC: | 
|                         assert backpackType != null; | 
|                         // 检验方向 | 
|                         if (!lastDirection.equals(workDirection)) { | 
|                             // turn | 
|                             actionList.add(new Action( | 
|                                     null,    // 编号 | 
|                                     task.getBusId(),    // 总线 | 
|                                     task.getId(),    // 任务 | 
|                                     null,    // 动作号 | 
|                                     null,    // 优先级 | 
|                                     ActionTypeType.TurnCorner.desc,    // 名称 | 
|                                     mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值 | 
|                                     lastCode.getData(),    // 地面码 | 
|                                     String.valueOf(workDirection),   // 动作参数 | 
|                                     ActionTypeType.TurnCorner.val(),    // 动作类型 | 
|                                     ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                     agvId,    // AGV | 
|                                     now    // 工作时间 | 
|                             )); | 
|                             lastDirection = workDirection; | 
|                         } | 
|                         // 货架取货 | 
|                         Loc oriLoc = locService.getById(task.getOriLoc()); | 
|                         // 计算左右方向 | 
|                         agvDirectionType = mapService.calculateAgvWorkDirection(storeDirection, oriLoc, lastCode); | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 ActionTypeType.ReadyTakeFromShelvesLoc.desc,    // 名称 | 
|                                 (double) agvDirectionType.val,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(oriLoc.getOffset()),   // 动作参数 | 
|                                 ActionTypeType.ReadyTakeFromShelvesLoc.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|                         // 暂存点放货 | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 ActionTypeType.ReadyReleaseToAgvSite.desc,    // 名称 | 
|                                 (double) backpackType.lev,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(backpackType.height),   // 动作参数 | 
|                                 ActionTypeType.ReadyReleaseToAgvSite.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|                         break; | 
|                     case DEST_LOC: | 
|                         assert backpackType != null; | 
|                         // 检验方向 | 
|                         if (!lastDirection.equals(workDirection)) { | 
|                             // turn | 
|                             actionList.add(new Action( | 
|                                     null,    // 编号 | 
|                                     task.getBusId(),    // 总线 | 
|                                     task.getId(),    // 任务 | 
|                                     null,    // 动作号 | 
|                                     null,    // 优先级 | 
|                                     ActionTypeType.TurnCorner.desc,    // 名称 | 
|                                     mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值 | 
|                                     lastCode.getData(),    // 地面码 | 
|                                     String.valueOf(workDirection),   // 动作参数 | 
|                                     ActionTypeType.TurnCorner.val(),    // 动作类型 | 
|                                     ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                     agvId,    // AGV | 
|                                     now    // 工作时间 | 
|                             )); | 
|                             lastDirection = workDirection; | 
|                         } | 
|                         // 暂存点取货货 | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 ActionTypeType.ReadyTakeFromAgvSite.desc,    // 名称 | 
|                                 (double) backpackType.lev,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(backpackType.height),   // 动作参数 | 
|                                 ActionTypeType.ReadyTakeFromAgvSite.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|   | 
|                         // 货架放货 | 
|                         Loc destLoc = locService.getById(task.getDestLoc()); | 
|                         // 计算左右方向 | 
|                         agvDirectionType = mapService.calculateAgvWorkDirection(storeDirection, destLoc, lastCode); | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 task.getBusId(),    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                  null,    // 优先级 | 
|                                 ActionTypeType.ReadyReleaseToShelvesLoc.desc,    // 名称 | 
|                                 (double) agvDirectionType.val,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 String.valueOf(destLoc.getOffset()),   // 动作参数 | 
|                                 ActionTypeType.ReadyReleaseToShelvesLoc.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|                         break; | 
|                     case ORI_STA: | 
|                         break; | 
|                     case DEST_STA: | 
|                         break; | 
|                     case TO_CHARGE: | 
|                         // 检验方向 | 
|                         FuncSta chargeFuncSta = funcStaService.query(agvId, lastCode.getId(), FuncStaType.CHARGE.toString()); | 
|                         Double chargeDirection = Double.parseDouble(chargeFuncSta.getAngle()); | 
|                         if (!lastDirection.equals(chargeDirection)) { | 
|                             actionList.add(new Action( | 
|                                     null,    // 编号 | 
|                                     null,    // 总线 | 
|                                     task.getId(),    // 任务 | 
|                                     null,    // 动作号 | 
|                                     null,    // 优先级 | 
|                                     ActionTypeType.TurnCorner.desc,    // 名称 | 
|                                     mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D,    // 属性值 | 
|                                     lastCode.getData(),    // 地面码 | 
|                                     String.valueOf(chargeDirection),   // 动作参数 | 
|                                     ActionTypeType.TurnCorner.val(),    // 动作类型 | 
|                                     ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                     agvId,    // AGV | 
|                                     now    // 工作时间 | 
|                             )); | 
|                             lastDirection = chargeDirection; | 
|                         } | 
|   | 
|                         // charge | 
|                         actionList.add(new Action( | 
|                                 null,    // 编号 | 
|                                 null,    // 总线 | 
|                                 task.getId(),    // 任务 | 
|                                 null,    // 动作号 | 
|                                 null,    // 优先级 | 
|                                 ActionTypeType.DockingCharge.desc,    // 名称 | 
|                                 null,    // 属性值 | 
|                                 lastCode.getData(),    // 地面码 | 
|                                 null,   // 动作参数 | 
|                                 ActionTypeType.DockingCharge.val(),    // 动作类型 | 
|                                 ActionStsType.PREPARE.val(),    // 动作进度 | 
|                                 agvId,    // AGV | 
|                                 now    // 工作时间 | 
|                         )); | 
|                         break; | 
|                     case TO_STANDBY: | 
| //                        FuncSta standByFuncSta = funcStaService.query(agvId, lastCode.getId(), 2); | 
| //                        Double standByDirection = Double.parseDouble(standByFuncSta.getAngle()); | 
| //                        if (!lastDirection.equals(standByDirection)) { | 
| //                            actionList.add(new Action( | 
| //                                    null,    // 编号 | 
| //                                    null,    // 总线 | 
| //                                    task.getId(),    // 任务 | 
| //                                    null,    // 动作号 | 
| //                                    null,    // 优先级 | 
| //                                    ActionTypeType.TurnCorner.desc,    // 名称 | 
| //                                    null,    // 属性值 | 
| //                                    lastCode.getData(),    // 地面码 | 
| //                                    String.valueOf(standByDirection),   // 动作参数 | 
| //                                    ActionTypeType.TurnCorner.val(),    // 动作类型 | 
| //                                    ActionStsType.PREPARE.val(),    // 动作进度 | 
| //                                    agvId,    // AGV | 
| //                                    now    // 工作时间 | 
| //                            )); | 
| //                            lastDirection = standByDirection; | 
| //                        } | 
|                         break; | 
|                     case MOVE: | 
|                         break; | 
|                     default: | 
|                         break; | 
|                 } | 
|   | 
|             } | 
|   | 
|             // finish | 
|             actionList.add(new Action( | 
|                     null,    // 编号 | 
|                     null,    // 总线 | 
|                     null,    // 任务 | 
|                     null,    // 动作号 | 
|                     null,    // 优先级 | 
|                     ActionTypeType.FinishPath.desc,    // 名称 | 
|                     null,    // 属性值 | 
|                     lastCode.getData(),    // 地面码 | 
|                     null,   // 动作参数 | 
|                     ActionTypeType.FinishPath.val(),    // 动作类型 | 
|                     ActionStsType.PREPARE.val(),    // 动作进度 | 
|                     agvId,    // AGV | 
|                     now    // 工作时间 | 
|             )); | 
|   | 
|             List<Action> newActionList = new ArrayList<>(actionList); | 
|   | 
| //            List<Action> optimizeList = actionService.optimizeSort(actionList); | 
|   | 
| //            List<Action> newActionList = new ArrayList<>(optimizeList); | 
|   | 
|   | 
|             String groupId = String.valueOf(snowflakeIdWorker.nextId()).substring(3); | 
|   | 
|             // save action | 
|             int i = newActionList.size(); | 
|             for (Action action : newActionList) { | 
|                 action.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|                 action.setGroupId(groupId); | 
|                 action.setPriority(i); | 
|                 if (!actionService.save(action)) { | 
|                     throw new BusinessException(action.getName() + "动作保存失败"); | 
|                 } | 
|                 i -= 1; | 
|             } | 
|   | 
|             // update segment | 
|             for (Segment item : segmentList) { | 
|                 item.setGroupId(groupId); | 
|                 item.setState(SegmentStateType.RUNNING.toString()); | 
|                 item.setUpdateTime(now); | 
|                 if (!segmentService.updateById(item)) { | 
|                     throw new CoolException("更新Segment失败"); | 
|                 } | 
|             } | 
|   | 
|             log.info(agv.getUuid() + "号Agv动作组装完成,指令数量:" + newActionList.size()); | 
|         } catch (Exception e) { | 
|             log.error("mainService.generateAction", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|   | 
|             if (!Cools.isEmpty(pathList)) { | 
|                 pathList.remove(0); | 
|                 mapDataDispatcher.clearDynamicMatrixByCodeList(null, pathList); | 
|             } | 
|   | 
|             throw new RuntimeException("generateAction method caught an exception, rolling back transaction.", e); | 
|         } | 
|     } | 
|   | 
|     @Transactional | 
|     public void publishAction(String actionGroupId) { | 
|         try { | 
|             Date now = new Date(); | 
|   | 
|             // action | 
|             List<Action> actionList = actionService.list(new LambdaQueryWrapper<Action>() | 
|                     .eq(Action::getGroupId, actionGroupId).eq(Action::getActionSts, ActionStsType.PREPARE.val()) | 
|                     .orderByDesc(Action::getPriority)); | 
|             if (Cools.isEmpty(actionList)) { | 
|                 return; | 
|             } | 
|             for (Action action : actionList) { | 
|                 action.setActionSts(ActionStsType.ISSUED.val()); | 
|                 action.setUpdateTime(now); | 
|                 if (!actionService.updateById(action)) { | 
|                     throw new BusinessException(action.getPriority() + " - " + action.getName() + "动作更新失败"); | 
|                 } | 
|             } | 
|   | 
|             // task | 
|             List<Long> taskIds = actionService.selectTaskIdsByGroupId(actionGroupId); | 
|             for (Long taskId : taskIds) { | 
|                 Task task = taskService.getById(taskId); | 
|                 if (task.getTaskSts().equals(TaskStsType.ASSIGN.val())) { | 
|                     task.setTaskSts(TaskStsType.PROGRESS.val()); | 
|                     task.setUpdateTime(now); | 
|                     if (!taskService.updateById(task)) { | 
|                         throw new BusinessException(task.getSeqNum() + "任务更新失败"); | 
|                     } | 
|                 } | 
|             } | 
|   | 
|   | 
|             Agv agv = agvService.getById(actionList.get(0).getAgvId()); | 
|             AgvAction agvAction = new AgvAction(agv.getUuid(), actionGroupId); | 
|             for (Action action : actionList) { | 
|   | 
|                 switch (Objects.requireNonNull(ActionTypeType.get(action.getActionTypeEl()))) { | 
|                     case TurnCorner: | 
|                         agvAction.add(new AgvActionItem<>(TurnCornerAction.class) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .setVal(Optional.ofNullable(action.getVal()).orElse(0D).intValue()) | 
|                                 .bodySync(body -> body.setAngle((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case StraightBackUnturnable: | 
|                         break; | 
|                     case StraightBackTurnable: | 
|                         agvAction.add(new AgvActionItem<>(StraightBackTurnableAction.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setDistance((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case StraightAheadUnturnable: | 
|                         break; | 
|                     case StraightAheadTurnable: | 
|                         agvAction.add(new AgvActionItem<>(StraightAheadTurnableAction.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setDistance((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case ReadyTakeFromShelvesLoc: | 
|                         agvAction.add(new AgvActionItem<>(ReadyTakeFromShelvesLoc.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case ReadyTakeFromAgvSite: | 
|                         agvAction.add(new AgvActionItem<>(ReadyTakeFromAgvSite.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setDepth((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case ReadyReleaseToShelvesLoc: | 
|                         agvAction.add(new AgvActionItem<>(ReadyReleaseToShelvesLoc.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setHeight((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case ReadyReleaseToAgvSite: | 
|                         agvAction.add(new AgvActionItem<>(ReadyReleaseToAgvSite.class) | 
|                                 .setVal(action.getVal().intValue()) | 
|                                 .setQrCode(action.getCode()) | 
|                                 .bodySync(body -> body.setDepth((short) Double.parseDouble(action.getParams()))) | 
|                         ); | 
|                         break; | 
|                     case FinishPath: | 
|                         agvAction.add(new AgvActionItem<>(FinishPathAction.class) | 
|                                 .setQrCode(action.getCode()) | 
|                         ); | 
|                         break; | 
|                     case DockingCharge: | 
|                         agvAction.add(new AgvActionItem<>(DockingChargeAction.class) | 
|                                 .setQrCode(action.getCode()) | 
|                         ); | 
|                         break; | 
|                     default: | 
|                         break; | 
|                 } | 
|   | 
|             } | 
|   | 
|             BaseResult<?> result = agvCmdService.executeAgvActionCmd(agvAction); | 
|             if (result.success()) { | 
|                 log.info("任务组 [{}] 动作指令已下发 ===>> 指令数量:{}", actionGroupId, actionList.size()); | 
|             } else { | 
|                 log.error("任务组 [{}] 动作指令下发失败 !!!", actionGroupId); | 
|                 throw new CoolException("任务组 [{" + actionGroupId + "}] 动作指令下发失败 !!!"); | 
|             } | 
|         } catch (Exception e) { | 
|             log.error("mainService.publishAction", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|         } | 
|   | 
|     } | 
|   | 
|     @Transactional | 
|     public void upDataSubscribe(AgvProtocol protocol) { | 
|         Date now = new Date(); | 
|   | 
|         Agv agv = agvService.selectByUuid(protocol.getAgvNo()); | 
|         if (null == agv) { | 
|             log.warn("Agv [{}] 尚未鉴权 !!!", protocol.getAgvNo()); | 
|             return; | 
|         } | 
|   | 
|         IMessageBody msgBody = protocol.getMessageBody(); | 
|         assert msgBody != null; | 
|   | 
|         // 动作完成包 | 
|         if (msgBody instanceof AGV_11_UP) { | 
|             AGV_11_UP agv_11_up = (AGV_11_UP) msgBody; | 
|             String serialNo = agv_11_up.getSerialNo(); | 
|   | 
|             log.info("Agv [{}] 动作完成包 ===>> {}", protocol.getAgvNo(), JSON.toJSONString(agv_11_up)); | 
|   | 
|             // 入库放货 | 
|             if (agv_11_up.getCompleteType().equals(AgvCompleteType.RELEASE_FROM_SHELVES_COMPLETE)) { | 
|                 WebsocketServiceImpl.taskShelfBarcode = agv_11_up.getLocCode(); | 
|   | 
|                 Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, String.valueOf(Integer.parseInt(agv_11_up.getLocCode())))); | 
|                 if (null == loc) { | 
|                     log.warn("Agv [{}] 上报往货架放货完成时,库位码[{}]无效。", protocol.getAgvNo(), agv_11_up.getLocCode()); | 
|                 } else { | 
|                     List<Long> taskIds = actionService.selectTaskIdsByGroupId(serialNo); | 
|                     for (Long taskId : taskIds) { | 
|                         Task task = taskService.getById(taskId); | 
|                         if (task.getDestLoc().equals(loc.getId())) { | 
|   | 
|                             Action action = actionService.getOne(new LambdaQueryWrapper<Action>() | 
|                                     .eq(Action::getTaskId, task.getId()) | 
|                                     .eq(Action::getActionType, ActionTypeType.ReadyReleaseToShelvesLoc.val()) | 
|                                     .eq(Action::getActionSts, ActionStsType.ISSUED.val()) | 
|                             ); | 
|   | 
|                             if (null != action) { | 
|                                 action.setActionSts(ActionStsType.FINISH.val()); | 
|                                 action.setUpdateTime(now); | 
|                                 if (!actionService.updateById(action)) { | 
|                                     log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName()); | 
|                                 } | 
|                             } | 
|   | 
|                         } | 
|                     } | 
|                 } | 
|             } | 
|   | 
|             // 出库取货 | 
|             if (agv_11_up.getCompleteType().equals(AgvCompleteType.TAKE_FROM_SHELVES_COMPLETE)) { | 
|                 WebsocketServiceImpl.taskShelfBarcode = agv_11_up.getLocCode(); | 
|   | 
|                 Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getBarcode, String.valueOf(Integer.parseInt(agv_11_up.getLocCode())))); | 
|                 if (null == loc) { | 
|                     log.warn("Agv [{}] 上报从货架取货完成,库位码[{}]无效。", protocol.getAgvNo(), agv_11_up.getLocCode()); | 
|                 } else { | 
|   | 
|                     List<Long> taskIds = actionService.selectTaskIdsByGroupId(serialNo); | 
|                     for (Long taskId : taskIds) { | 
|                         Task task = taskService.getById(taskId); | 
|                         if (task.getOriLoc().equals(loc.getId())) { | 
|   | 
|                             Action action = actionService.getOne(new LambdaQueryWrapper<Action>() | 
|                                     .eq(Action::getTaskId, task.getId()) | 
|                                     .eq(Action::getActionType, ActionTypeType.ReadyTakeFromShelvesLoc.val()) | 
|                                     .eq(Action::getActionSts, ActionStsType.ISSUED.val()) | 
|                             ); | 
|                             if (null != action) { | 
|                                 action.setActionSts(ActionStsType.FINISH.val()); | 
|                                 action.setUpdateTime(now); | 
|                                 if (!actionService.updateById(action)) { | 
|                                     log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName()); | 
|                                 } | 
|                             } | 
|                         } | 
|                     } | 
|                 } | 
|             } | 
|   | 
|             // 路径完成 || 充电完成 | 
|             if (agv_11_up.getCompleteType().equals(AgvCompleteType.ENTIRE_PATH_COMPLETE) | 
|                     || agv_11_up.getCompleteType().equals(AgvCompleteType.DOCKING_CHARGE_COMPLETE)) { | 
|   | 
|                 // segment list | 
|                 List<Segment> segmentList = segmentService.list(new LambdaQueryWrapper<Segment>() | 
|                         .eq(Segment::getGroupId, serialNo) | 
| //                        .eq(Segment::getState, SegmentStateType.RUNNING.toString()) | 
|                         .orderByAsc(Segment::getSerial) | 
|                 ); | 
|   | 
|                 // task | 
|                 for (Segment segment : segmentList) { | 
|                     boolean taskComplete = false; | 
|   | 
|                     Task task = taskService.getById(segment.getTaskId());   assert null != task; | 
|                     TaskTypeType typeType = TaskTypeType.get(task.getTaskTypeEl());     assert null != typeType; | 
|   | 
|                     TaskPosDto.PosType posType = TaskPosDto.queryPosType(segment.getPosType()); | 
|                     switch (Objects.requireNonNull(posType)) { | 
|                         case ORI_STA: | 
|                         case ORI_LOC: | 
|                             break; | 
|                         case DEST_STA: | 
|                         case DEST_LOC: | 
|                         case TO_CHARGE: | 
|                         case TO_STANDBY: | 
|                             if (segment.getEndNode().equals(task.getDestCode())) { | 
|                                 taskComplete = true; | 
|                             } | 
|                             break; | 
|                         case MOVE: | 
|                             if (segment.getEndNode().equals(task.getDestCode())) { | 
|                                 if (typeType.equals(TaskTypeType.MOVE)) { | 
|                                     taskComplete = true; | 
|                                 } | 
|                             } | 
|                             break; | 
|                         default: | 
|                             break; | 
|                     } | 
|   | 
|                     if (taskComplete) { | 
|                         locService.taskCallBack(task); | 
|   | 
|                         task.setTaskSts(TaskStsType.COMPLETE.val()); | 
|                         task.setEndTime(now); | 
|                         task.setUpdateTime(now); | 
|                         if (!taskService.updateById(task)) { | 
|                             log.error("Task [{}] 更新失败 !!!", task.getSeqNum()); | 
|                         } else { | 
|                             log.info("Task [{}] 作业完毕 ==========>> ", task.getSeqNum()); | 
|                         } | 
|   | 
|                     } | 
|                 } | 
|   | 
|                 // action | 
|                 List<Action> actionList = actionService.list(new LambdaQueryWrapper<Action>() | 
|                         .eq(Action::getGroupId, serialNo) | 
|                         .eq(Action::getActionSts, ActionStsType.ISSUED.val()) | 
|                 ); | 
|                 for (Action action : actionList) { | 
|                     action.setActionSts(ActionStsType.FINISH.val()); | 
|                     action.setUpdateTime(now); | 
|                     if (!actionService.updateById(action)) { | 
|                         log.error("Action [{}] 更新失败 !!!", action.getPriority() + " - " + action.getName()); | 
|                     } | 
|                 } | 
|   | 
|                 // segment | 
|                 for (Segment segment : segmentList) { | 
|                     segment.setState(SegmentStateType.FINISH.toString()); | 
|                     segment.setUpdateTime(now); | 
|                     if (!segmentService.updateById(segment)) { | 
|                         log.error("Segment [{}] 更新失败 !!!", segment.getGroupId() + " - " + segment.getSerial()); | 
|                     } | 
|                 } | 
|   | 
|                 // segment call back | 
|                 trafficService.callback(segmentList); | 
|   | 
|                 log.info("Agv [{}] {}作业完毕 ==========>> ", protocol.getAgvNo(), serialNo); | 
|   | 
|             } | 
|         } | 
|   | 
|         // 料仓信息包 | 
|         if (msgBody instanceof AGV_70_UP) { | 
|             AGV_70_UP agv_70_up = (AGV_70_UP) msgBody; | 
|   | 
|             log.info("Agv [{}] 料仓信息包 ===>> {}", protocol.getAgvNo(), JSON.toJSONString(agv_70_up)); | 
|         } | 
|     } | 
|   | 
| } |