| package com.zy.acs.manager.core.service; | 
|   | 
| import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 
| import com.zy.acs.common.utils.GsonUtils; | 
| import com.zy.acs.common.utils.Utils; | 
| 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.manager.common.utils.MapDataUtils; | 
| import com.zy.acs.manager.core.constant.MapDataConstant; | 
| import com.zy.acs.manager.core.domain.BlockVehicleDto; | 
| import com.zy.acs.manager.core.domain.TaskPosDto; | 
| import com.zy.acs.manager.core.domain.type.BlockSeverityType; | 
| import com.zy.acs.manager.core.service.astart.MapDataDispatcher; | 
| import com.zy.acs.manager.core.service.astart.NavigateNode; | 
| import com.zy.acs.manager.core.service.astart.RetreatNavigateNode; | 
| import com.zy.acs.manager.core.service.astart.WaveNodeType; | 
| import com.zy.acs.manager.manager.entity.*; | 
| import com.zy.acs.manager.manager.enums.JamStateType; | 
| import com.zy.acs.manager.manager.enums.SegmentStateType; | 
| import com.zy.acs.manager.manager.enums.TaskTypeType; | 
| import com.zy.acs.manager.manager.service.*; | 
| 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.ArrayList; | 
| import java.util.Arrays; | 
| import java.util.Date; | 
| import java.util.List; | 
| import java.util.stream.Collectors; | 
|   | 
| /** | 
|  * Wavefront | 
|  * Created by vincent on 6/25/2024 | 
|  */ | 
| @Slf4j | 
| @Component | 
| public class TrafficService { | 
|   | 
|     @Autowired | 
|     private AgvService agvService; | 
|     @Autowired | 
|     private AgvDetailService agvDetailService; | 
|     @Autowired | 
|     private CodeService codeService; | 
|     @Autowired | 
|     private TravelService travelService; | 
|     @Autowired | 
|     private SegmentService segmentService; | 
|     @Autowired | 
|     private MainService mainService; | 
|     @Autowired | 
|     private MainLockWrapService mainLockWrapService; | 
|     @Autowired | 
|     private MapService mapService; | 
|     @Autowired | 
|     private MapDataDispatcher mapDataDispatcher; | 
|     @Autowired | 
|     private SnowflakeIdWorker snowflakeIdWorker; | 
|     @Autowired | 
|     private AgvModelService agvModelService; | 
|     @Autowired | 
|     private RetreatNavigateService retreatNavigateService; | 
|     @Autowired | 
|     private ConfigService configService; | 
|     @Autowired | 
|     private JamService jamService; | 
|     @Autowired | 
|     private AvoidWaveCalculator avoidWaveCalculator; | 
|     @Autowired | 
|     private TaskService taskService; | 
|     @Autowired | 
|     private FuncStaService funcStaService; | 
|   | 
|     @Transactional | 
|     public synchronized void trigger(Segment segment) { | 
|         try { | 
|             Date now = new Date(); | 
|   | 
|             // deprecate jam | 
|             if (this.isExpiredJamByAvo(segment.getId())) { | 
|                 mainService.settleSegmentList(Utils.singletonList(segment), null); | 
|                 return; | 
|             } | 
|   | 
|             // temporary ----------------- | 
|             Integer algoExtensionTime = configService.getVal("algoExtensionTime", Integer.class); | 
|             if (null != algoExtensionTime && algoExtensionTime > 0) { | 
|                 Thread.sleep(algoExtensionTime); | 
|             } | 
|             // --------------------------- | 
|   | 
|             Travel travel = travelService.getById(segment.getTravelId()); | 
|             Agv agv = agvService.getById(travel.getAgvId()); | 
|             AgvDetail agvDetail = agvDetailService.selectByAgvId(travel.getAgvId()); | 
|             long endNode = segment.getEndNode(); | 
|   | 
|             // valid ---------------------------------------------------- | 
|             if (!agvService.judgeEnable(agv.getId())) { | 
|                 return; | 
|             } | 
|   | 
|             // resolve deadlock | 
| //            if (configService.getVal("unlockSwitch", Boolean.class)) { | 
| // | 
| //                boolean preSegIsStandbyDeadLock = false; | 
| //                Segment previousSeg = segmentService.getPreviousStepOfFinish(segment.getTravelId(), segment.getSerial(), MapDataConstant.RESOLVE_DEADLOCK); | 
| //                preSegIsStandbyDeadLock = previousSeg != null; | 
| //                if (preSegIsStandbyDeadLock && !jamService.isAvoidSeg(segment.getId())) { | 
| //                    if (0 < segmentService.count(new LambdaQueryWrapper<Segment>() | 
| //                            .in(Segment::getState, SegmentStateType.WAITING.toString(), SegmentStateType.RUNNING.toString()) | 
| //                            .eq(Segment::getMemo, MapDataConstant.RESOLVE_DEADLOCK))) { | 
| //                        return; | 
| //                    } | 
| //                } | 
| // | 
| //                Jam jam = jamService.getJam(agv.getId(), agvDetail.getRecentCode(), segment.getId()); | 
| //                if (!preSegIsStandbyDeadLock && (null != jam && null != jam.getDuration() && jam.getDuration() > (BlockSeverityType.SEVERE.duration - MapDataConstant.MAX_JAM_TIMEOUT) | 
| //                        || DateUtils.diffToMilliseconds(segment.getUpdateTime(), now)  > (BlockSeverityType.SEVERE.duration - MapDataConstant.MAX_JAM_TIMEOUT)) | 
| //                        && (Cools.isEmpty(segment.getMemo()) || !segment.getMemo().equals(MapDataConstant.RESOLVE_DEADLOCK))) { | 
| // | 
| //                    Task task = taskService.getById(segment.getTaskId()); | 
| //                    if (task.getTaskSts().equals(TaskStsType.PROGRESS.val()) | 
| //                            && DateUtils.diffToMilliseconds(task.getUpdateTime(), now) > MapDataConstant.DEADLOCK_TASK_TIMEOUT) { | 
| // | 
| //                        Code endCode = null; | 
| //                        List<FuncSta> idleFunStaList = funcStaService.findInIdleStatus(FuncStaType.STANDBY, segment.getAgvId()); | 
| //                        if (!Cools.isEmpty(idleFunStaList)) { | 
| //                            idleFunStaList = idleFunStaList.stream().filter(funcSta -> { | 
| //                                return 0 == segmentService.count(new LambdaQueryWrapper<Segment>() | 
| //                                                .eq(Segment::getEndNode, funcSta.getCode()) | 
| //                                                .in(Segment::getState, SegmentStateType.WAITING.toString(), SegmentStateType.RUNNING.toString()) | 
| //                                                .eq(Segment::getMemo, MapDataConstant.RESOLVE_DEADLOCK) | 
| //                                ); | 
| //                            }).collect(Collectors.toList()); | 
| //                            FuncSta funcSta = funcStaService.checkoutFurthestFunSta(agvDetailService.getCurrentCode(segment.getAgvId()).getId(), idleFunStaList); | 
| //                            if (null != funcSta) { | 
| //                                endCode = codeService.getCacheById(funcSta.getCode()); | 
| //                            } | 
| //                        } | 
| //                        if (null == endCode) { | 
| //                            log.warn("AGV[{}] failed to search destination,there hadn't any idle funSta,TaskTypeType:{}", segment.getAgvId(), FuncStaType.STANDBY); | 
| //                            return; | 
| //                        } | 
| // | 
| //                        segment.setState(SegmentStateType.INIT.toString()); | 
| //                        segment.setUpdateTime(now); | 
| //                        if (!segmentService.updateById(segment)) { | 
| //                            log.error("Segment [{}] failed to update !!!", segment.getGroupId() + " - " + segment.getSerial()); | 
| //                            return; | 
| //                        } | 
| // | 
| //                        // new move seg | 
| //                        Segment insertSeg = new Segment(); | 
| //                        insertSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
| //                        insertSeg.setTravelId(segment.getTravelId()); | 
| //                        insertSeg.setAgvId(segment.getAgvId()); | 
| //                        insertSeg.setTaskId(segment.getTaskId()); | 
| //                        insertSeg.setSerial(segment.getSerial() - 1); | 
| //                        insertSeg.setEndNode(endCode.getId()); | 
| //                        insertSeg.setPosType(TaskPosDto.PosType.MOVE.toString()); | 
| //                        insertSeg.setState(SegmentStateType.WAITING.toString()); | 
| //                        insertSeg.setMemo(MapDataConstant.RESOLVE_DEADLOCK); | 
| //                        if (!segmentService.save(insertSeg)) { | 
| //                            log.error("Segment [{}] failed to save !!!", segment.getTravelId() + " - " + segment.getSerial()); | 
| //                            return; | 
| //                        } | 
| //                        return; | 
| //                    } | 
| //                } | 
| //            } | 
|   | 
|             if (!Cools.isEmpty(segmentService.getByAgvAndState(agv.getId(), SegmentStateType.RUNNING.toString()))) { | 
|                 return; | 
|             } | 
|             List<Segment> waitingSegList = segmentService.getByAgvAndState(agv.getId(), SegmentStateType.WAITING.toString()); | 
|             if (!Cools.isEmpty(waitingSegList)) { | 
|                 for (Segment waitingSeg : waitingSegList) { | 
|                     if (!waitingSeg.getId().equals(segment.getId())) { | 
| //                        log.error("AGV[{}] 任务异常,服务器错误!!!", agv.getUuid()); | 
|                         return; | 
|                     } | 
|                 } | 
|             } | 
|   | 
|             // dead lane | 
|             if (jamService.count(new LambdaQueryWrapper<Jam>() | 
|                     .eq(Jam::getJamAgv, agv.getId()) | 
|                     .eq(Jam::getJamSeg, segment.getId()) | 
|                     .eq(Jam::getCycleAvo, 1) | 
|                     .eq(Jam::getCycleCode, endNode) | 
|                     .eq(Jam::getState, JamStateType.RUNNING.toString()) | 
|             ) > 0) { | 
|                 return; | 
|             } | 
|   | 
|             // execute ----------------------------------------------- | 
|             //        ArrayList<List<TaskPosDto>> list = JSON.parseObject(travel.getTaskContent(), new TypeReference<ArrayList<List<TaskPosDto>>>() {}); | 
|   | 
|             // * sync wave scope | 
|             if (!avoidWaveCalculator.calcWaveScope()) { | 
|                 log.error("failed to calculate avoid wave matrix ..."); | 
|                 return; | 
|             } | 
|   | 
|             // checkout path | 
|             Code startCode = codeService.getCacheById(agvDetail.getRecentCode()); | 
|             Code endCode = codeService.getCacheById(endNode); | 
|             long startTime = System.currentTimeMillis(); | 
|             List<String> pathList = this.checkoutPath(agv, startCode, endCode, segment); | 
| //            System.out.println("checkoutPath: " + (System.currentTimeMillis() - startTime)); | 
|             if (Cools.isEmpty(pathList)) { | 
|                 return; | 
|             } | 
|   | 
|             List<Segment> segmentList = new ArrayList<>(); | 
|             segmentList.add(segment); | 
|   | 
|             String lastCodeData = pathList.get(pathList.size() - 1); | 
|             if (!endCode.getData().equals(lastCodeData)) { | 
|                 // slice | 
|                 Code lastCode = codeService.getCacheByData(lastCodeData); | 
|   | 
| //                if (pathList.size() <= MIN_SLICE_PATH_LENGTH) { | 
| //                    return; | 
| //                } | 
|   | 
|                 // revert | 
|                 segment.setState(SegmentStateType.INIT.toString()); | 
|                 segment.setUpdateTime(now); | 
|                 if (!segmentService.updateById(segment)) { | 
|                     log.error("Segment [{}] failed to update !!!", segment.getGroupId() + " - " + segment.getSerial()); | 
|                 } | 
|                 segmentList.clear(); | 
|   | 
|                 // new move seg | 
|                 Segment insertSeg = new Segment(); | 
|                 insertSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|                 insertSeg.setTravelId(travel.getId()); | 
|                 insertSeg.setAgvId(agv.getId()); | 
|                 insertSeg.setTaskId(segment.getTaskId()); | 
|                 insertSeg.setSerial(segment.getSerial() - 1); | 
|                 insertSeg.setEndNode(lastCode.getId()); | 
|                 insertSeg.setPosType(TaskPosDto.PosType.MOVE.toString()); | 
|                 insertSeg.setState(SegmentStateType.WAITING.toString()); | 
|                 if (!segmentService.save(insertSeg)) { | 
|                     log.error("Segment [{}] failed to save !!!", segment.getTravelId() + " - " + segment.getSerial()); | 
|                 } | 
|                 segmentList.add(insertSeg); | 
|   | 
|             } else { | 
|   | 
|                 // complete first segment then merge behind segment | 
|                 int serial = segment.getSerial(); | 
|                 boolean interrupt = false; | 
|                 while (!interrupt) { | 
|   | 
|                     Segment nextStep = segmentService.getNextStepOfInit(travel.getId(), serial); | 
|                     serial ++; | 
|                     if (null == nextStep) { | 
|                         interrupt = true; | 
|                     } else { | 
|                         if (nextStep.getEndNode() == endNode) { | 
|   | 
|                             segmentList.add(nextStep); | 
|                         } else { | 
|                             interrupt = true; | 
|                         } | 
|                     } | 
|   | 
|                 } | 
|             } | 
|   | 
|             mapService.lockPath(null, pathList, agv.getUuid()); | 
|   | 
|             startTime = System.currentTimeMillis(); | 
|             mainService.generateAction(segment.getAgvId(), segmentList, pathList, now); | 
| //            System.out.println("generateAction: " + (System.currentTimeMillis() - startTime)); | 
|   | 
|         } catch (Exception e) { | 
|             log.error("TrafficService.trigger", e); | 
|             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); | 
|   | 
|         } | 
|     } | 
|   | 
|     private List<String> checkoutPath(Agv agv, Code startCode, Code endCode, Segment segment) { | 
|         Integer lev = null; | 
|         String agvNo = agv.getUuid(); | 
|         Date now = new Date(); | 
|         Jam jam = jamService.getJam(agv.getId(), startCode.getId(), segment.getId()); | 
|         BlockSeverityType blockSeverity = BlockSeverityType.query(null == jam ? null : jam.getDuration()); | 
|         // judge avoid of jam 如果已经在避让点(因为当前车执行了避让任务),那么则不能再去检索之前的阻塞路径 | 
|         List<Jam> unfinishedOriginJamByCurrAgv = jamService.getUnfinishedOriginJamByAvo(agv.getId(), startCode.getId(), segment.getId()); | 
|         List<String> blackPath = this.getBlackPathList(unfinishedOriginJamByCurrAgv); | 
|   | 
|         // 结果集 | 
|         List<String> pathList = new ArrayList<>(); | 
|   | 
|         // 无障碍解 | 
|         List<String> unlockPathList = mapService.checkoutPath(agvNo, startCode, endCode, false, blackPath, segment); | 
|         // 避让解 | 
|         List<String> lockPathList = mapService.checkoutPath(agvNo, startCode, endCode, true, blackPath, segment); | 
|   | 
|         if (!Cools.isEmpty(lockPathList) && // 存在避让解 | 
|             Math.abs(lockPathList.size() - unlockPathList.size()) <= Arrays.stream(mapDataDispatcher.getCodeMatrix(lev)).mapToInt(row -> row.length).sum() / 10 | 
|         ) { | 
|   | 
|             pathList = lockPathList; | 
|         } else { | 
|   | 
|             // preview path | 
|             List<String> draftPath = new ArrayList<>(unlockPathList); | 
|             if (blockSeverity.equals(BlockSeverityType.SEVERE)) { | 
|                 unlockPathList.remove(endCode.getData()); | 
|                 blackPath.addAll(unlockPathList); | 
|                 List<String> newUnlockPathList = mapService.checkoutPath(agvNo, startCode, endCode, false, blackPath, segment); | 
|                 if (!Cools.isEmpty(newUnlockPathList)) { | 
|                     draftPath = newUnlockPathList; | 
|                 } | 
|             } | 
|             // 可走行路径集合计算 | 
|             List<BlockVehicleDto> blockVehicleList = this.slicePathAndReturnBlockVehicleList(lev, draftPath, agvNo, pathList);    // jamAgvNo may was wave | 
|   | 
|             if (!Cools.isEmpty(pathList)) { | 
|   | 
|                 if (!pathList.get(pathList.size() - 1).equals(endCode.getData())) { | 
|                     assert !Cools.isEmpty(blockVehicleList); | 
|   | 
|                     boolean hasUnavoidableBlocks = blockVehicleList.stream().anyMatch(blockVehicleDto -> !blockVehicleDto.isAvoidable()); | 
|                     if (hasUnavoidableBlocks && pathList.size() <= MapDataConstant.MIN_SLICE_PATH_LENGTH) { | 
|                         log.info("AGV[{}] waiting in place, because the path list is too short...", agvNo); | 
|                         pathList.clear(); | 
|                     } | 
|   | 
|                     boolean hasCycleJam = blockVehicleList.stream().anyMatch( | 
|                             blockVehicleDto -> null != jamService.getCycleJam(agv.getId(), segment.getId(), blockVehicleDto.getVehicle()) | 
|                     ); | 
|                     if (hasCycleJam) { | 
|                         log.info("AGV[{}] waiting in place, because has cycle jam...", agvNo); | 
|                         pathList.clear(); | 
|                     } | 
|                 } | 
|   | 
|             // 无可走行路径 | 
|             } else { | 
|   | 
|                 if (Cools.isEmpty(blockVehicleList)) { | 
|                     log.warn("AGV[{}] can't reach to code: {}, because there is too many vehicle in the lane...", agvNo, endCode.getData()); | 
|                 } else { | 
|   | 
|                     assert !Cools.isEmpty(blockVehicleList); | 
|                     Integer maxJamTimeoutFactor = null; | 
|   | 
|                     // persist jam data | 
|                     jam = this.createOrUpdateJam(agv, startCode, segment, jam, draftPath); | 
|   | 
|                     // ? has unAvoidable block vehicles | 
|                     if (blockVehicleList.stream().anyMatch(blockVehicleDto -> !blockVehicleDto.isAvoidable())) { | 
|   | 
|                         // set factor of jam timeout | 
|                         maxJamTimeoutFactor = 1; | 
|                     } else { | 
|   | 
|                         // ? already do notify to avoid | 
|                         if (!Cools.isEmpty(jam.getAvoAgv()) | 
|                                 && BlockVehicleDto.customContain(blockVehicleList, agvService.getById(jam.getAvoAgv()).getUuid())) { | 
|   | 
|                             maxJamTimeoutFactor = 4; | 
|   | 
|                         } else { | 
|   | 
|                             // select optimal block vehicle | 
|                             String blockAgvNo = this.checkoutBestSolutionOfBlocks(blockVehicleList, segment); | 
|                             if (Cools.isEmpty(blockAgvNo)) { | 
|   | 
|                                 maxJamTimeoutFactor = 2; | 
|   | 
|                             } else { | 
|   | 
|                                 // block vehicle info | 
|                                 Long blockAgvId = agvService.getAgvId(blockAgvNo); | 
|                                 String blockAgvCode = codeService.getCacheById(agvDetailService.selectMajorByAgvId(blockAgvId).getRecentCode()).getData(); | 
|   | 
|                                 // create new jam if already notify the avoid vehicle | 
|                                 if (!Cools.isEmpty(jam.getAvoAgv(), jam.getAvoSeg()) && !blockAgvId.equals(jam.getAvoAgv())) { | 
|                                     jam = this.setupNewJam(jam, agv, startCode, segment, draftPath); | 
|                                 } | 
|   | 
|                                 do { | 
|   | 
|                                     // 阻塞车辆正在原地作业,等待 ===>> 超过等待时间,绕路 | 
|                                     List<Segment> runningSegList = segmentService.getByAgvAndState(blockAgvId, SegmentStateType.RUNNING.toString()); | 
|                                     if (!Cools.isEmpty(runningSegList)) { | 
|                                         maxJamTimeoutFactor = 1; | 
|                                         break; | 
|                                     } | 
|   | 
|                                     // 判断下个任务是否为原地任务,如果是则等待 ===>> 超过等待时间,绕路;如果不是,让阻塞车辆避让 | 
|                                     List<Segment> waitingSegList = segmentService.getJustWaitingSeg(blockAgvId); | 
|                                     if (null != waitingSegList | 
|                                             && waitingSegList.stream().anyMatch( | 
|                                             waitingSeg -> waitingSeg.getEndNode().equals(codeService.getCacheByData(blockAgvCode).getId()) | 
|                                     )) { | 
|                                         maxJamTimeoutFactor = 1; | 
|                                         break; | 
|                                     } | 
|   | 
|                                     // notify block vehicle to avoid | 
|                                     if (this.notifyVehicleAvoid(blockAgvNo, blockAgvCode, draftPath, agvNo, jam)) { | 
|                                         if (jam.getCycleAvo() == 1) { | 
|                                             jam.setCycleCode(endCode.getId()); | 
|                                         } | 
|                                         jam.setAvoAgv(blockAgvId); | 
|                                         jam.setNotifyTime(new Date()); | 
|                                         if (!jamService.updateById(jam)) { | 
|                                             throw new CoolException(jam.getUuid() + "-jam failed to update!!!"); | 
|                                         } | 
|                                     } else { | 
|   | 
|                                         maxJamTimeoutFactor = 1; | 
|                                     } | 
|   | 
|                                 } while (false); | 
|                             } | 
|   | 
|                         } | 
|   | 
|                     } | 
|   | 
|                     // handle jam timeout | 
|                     if (null != maxJamTimeoutFactor) { | 
|                         if (System.currentTimeMillis() - jam.getStartTime().getTime() > (long) MapDataConstant.MAX_JAM_TIMEOUT * maxJamTimeoutFactor) { | 
|   | 
|                             if (!Cools.isEmpty(lockPathList)) { | 
|   | 
|                                 pathList = lockPathList; | 
|                             } else { | 
|                                 log.error("{}号车辆检索[{}] ===>> [{}]路径失败,原因:{}" | 
|                                         , agvNo, startCode.getData(), endCode.getData(), "路径阻塞超时"); | 
|                             } | 
|                         } else { | 
|                             log.warn("{}号车辆正在等待交通堵塞,阻塞车辆:【{}】" | 
|                                     , agvNo | 
|                                     , blockVehicleList.stream().map(BlockVehicleDto::getVehicle).collect(Collectors.toList()).toString() | 
|                             ); | 
|                         } | 
|                     } | 
|   | 
|                 } | 
|   | 
|             } | 
|         } | 
|   | 
|         if (!Cools.isEmpty(pathList)) { | 
|   | 
|             if (null != jam) { | 
|                 boolean beDeprecate = false; | 
|                 if (blockSeverity.equals(BlockSeverityType.SEVERE) && !Cools.isEmpty(jam.getJamPath())) { | 
|                     List<String> jamPath = GsonUtils.fromJsonToList(jam.getJamPath(), String.class); | 
|                     if (!this.comparePathLists(jamPath, pathList)) {    // jamPath >= pathList | 
|                         beDeprecate = true; | 
|                     } | 
|                 } | 
|                 jam.setEndTime(now); | 
|                 jam.setUpdateTime(now); | 
|                 jam.setState(beDeprecate ? JamStateType.DEPRECATED.toString() : JamStateType.FINISH.toString()); | 
|                 if (jamService.updateById(jam)) { | 
|                     if (beDeprecate) { | 
|                         // search previous jam that jamSeg from this segment | 
|                         List<Jam> previousJams = jamService.list(new LambdaQueryWrapper<Jam>() | 
|                                 .eq(Jam::getJamSeg, segment.getId()) | 
|                                 .eq(Jam::getState, JamStateType.FINISH.toString()) | 
|                         ); | 
|                         for (Jam previousJam : previousJams) { | 
|                             previousJam.setState(JamStateType.DEPRECATED.toString()); | 
|                             previousJam.setUpdateTime(now); | 
|                             if (!jamService.updateById(previousJam)) { | 
|                                 log.error("Jam[{}] failed to update!!!", previousJam.getUuid()); | 
|                             } | 
|                         } | 
|                     } | 
|                 } else { | 
|                     log.error("Jam[{}] failed to update!!!", jam.getUuid()); | 
|                 } | 
|             } | 
|             // deal expired jam | 
|             for (Jam expiredJam : jamService.list(new LambdaQueryWrapper<Jam>() | 
|                     .eq(Jam::getJamAgv, agv.getId()) | 
|                     .eq(Jam::getState, JamStateType.RUNNING.toString()))) { | 
|                 expiredJam.setEndTime(now); | 
|                 expiredJam.setUpdateTime(now); | 
|                 expiredJam.setState(JamStateType.DEPRECATED.toString()); | 
|                 if (!jamService.updateById(expiredJam)) { | 
|                     log.error("Jam[{}] failed to update!!!", expiredJam.getUuid()); | 
|                 } | 
|             } | 
|   | 
|         } | 
|   | 
|         return pathList; | 
|     } | 
|   | 
|     private List<BlockVehicleDto> slicePathAndReturnBlockVehicleList(Integer lev, List<String> fullPathList, String agvNo, List<String> pathList) { | 
|         List<BlockVehicleDto> blockVehicleList = new ArrayList<>(); | 
|   | 
|         String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(lev); | 
|         List<int[]> codeMatrixIdxList = mapDataDispatcher.getCodeMatrixIdxList(lev, fullPathList); | 
|         for (int i = 0; i < fullPathList.size(); i++) { | 
|             String codeData = fullPathList.get(i); | 
|             int[] codeMatrixIdx = codeMatrixIdxList.get(i); | 
|   | 
|             String waveNode = waveMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; | 
|             assert !waveNode.equals(WaveNodeType.DISABLE.val); | 
|             if (!waveNode.equals(WaveNodeType.ENABLE.val)) { | 
|                 List<String> waveNodeList = MapDataUtils.parseWaveNode(waveNode); | 
|                 List<String> otherWaveList = MapDataUtils.hasOtherWave(waveNodeList, agvNo); | 
|                 if (!Cools.isEmpty(otherWaveList)) { | 
|                     for (String otherWave : otherWaveList) { | 
|                         if (mapService.isWalkingByVehicle(lev, otherWave)) { | 
|                             blockVehicleList.add(new BlockVehicleDto(otherWave, false)); | 
|                         } else { | 
|                             blockVehicleList.add(new BlockVehicleDto(otherWave, true)); | 
|                         } | 
|                     } | 
|                     break; | 
|                 } | 
|             } | 
|   | 
|             pathList.add(codeData); | 
|         } | 
|   | 
|         if (pathList.size() <= 1) { | 
|             pathList.clear(); | 
|         } | 
|   | 
|         return blockVehicleList.stream().distinct().collect(Collectors.toList()); | 
|     } | 
|   | 
|     /** | 
|      * avoidPathList include wave node and dynamic node | 
|      */ | 
|     private boolean notifyVehicleAvoid(String agvNo, String agvPosCode, List<String> avoidPathList, String sponsor, Jam jam) { | 
|         Long agvId = agvService.getAgvId(agvNo); | 
|         if (!Cools.isEmpty(segmentService.getByAgvAndState(agvId, SegmentStateType.RUNNING.toString()))) { | 
|             log.warn("{}号车辆避让失败,存在进行中任务!!!", agvNo); | 
|             return false; | 
|         } | 
|   | 
|         int[] startMapIdx = mapDataDispatcher.getCodeMatrixIdx(null, agvPosCode); | 
|         RetreatNavigateNode startNode = new RetreatNavigateNode(startMapIdx[0], startMapIdx[1], agvPosCode); | 
|   | 
|         assert avoidPathList.size() >= 2; | 
|         RetreatNavigateNode finalNode = retreatNavigateService.execute(agvNo, startNode, avoidPathList, sponsor, jam); | 
|         if (null == finalNode) { | 
|             log.warn("{}号车辆避让失败,检索避让点失败!!!", agvNo); | 
|             return false; | 
|         } | 
|   | 
|         String endCodeData = finalNode.getCodeData(); | 
|         Code endCode = codeService.getCacheByData(endCodeData); | 
|   | 
|         List<Segment> waitingSegList = segmentService.getByAgvAndState(agvId, SegmentStateType.WAITING.toString()); | 
|         if (!Cools.isEmpty(waitingSegList)) { | 
|   | 
|             if (waitingSegList.size() > 1) { | 
|                 log.error("避让通知失败,{}号车辆存在多个等待中的Segment!!!", agvNo); | 
|                 return false; | 
|             } | 
|             // revert | 
|             Date now = new Date(); | 
|             for (Segment seg : waitingSegList) { | 
|                 seg.setState(SegmentStateType.INIT.toString()); | 
|                 seg.setUpdateTime(now); | 
|                 if (!segmentService.updateById(seg)) { | 
|                     log.error("Segment [{}] 更新失败 !!!", seg.getTravelId() + " - " + seg.getSerial()); | 
|                 } | 
|             } | 
|             Segment segment = waitingSegList.get(0); | 
|   | 
|             Segment insertSeg = new Segment(); | 
|             insertSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             insertSeg.setTravelId(segment.getTravelId()); | 
|             insertSeg.setAgvId(agvId); | 
|             insertSeg.setTaskId(segment.getTaskId()); | 
|             insertSeg.setSerial(segment.getSerial() - 1); | 
|             insertSeg.setEndNode(endCode.getId()); | 
|             insertSeg.setPosType(TaskPosDto.PosType.MOVE.toString()); | 
|             insertSeg.setState(SegmentStateType.WAITING.toString()); | 
|             if (!segmentService.save(insertSeg)) { | 
|                 log.error("Segment [{}] 保存失败 !!!", segment.getTravelId() + " - " + segment.getSerial()); | 
|                 return false; | 
|             } else { | 
|                 jam.setAvoSeg(insertSeg.getId()); | 
|                 jam.setAvoCode(endCode.getId()); | 
|             } | 
|   | 
|         } else { | 
|   | 
|             return mainLockWrapService.buildMinorTask(agvId, TaskTypeType.MOVE, endCodeData, jam); | 
|         } | 
|   | 
|         return true; | 
|     } | 
|   | 
|     private Jam createOrUpdateJam(Agv agv, Code startCode, Segment segment, Jam jam, List<String> jamPath) { | 
|         if (jam == null) { | 
|             jam = new Jam(); | 
|             jam.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); | 
|             jam.setJamAgv(agv.getId()); | 
|             jam.setJamCode(startCode.getId()); | 
|             jam.setJamSeg(segment.getId()); | 
|             jam.setJamPath(GsonUtils.toJson(jamPath)); | 
|             jam.setStartTime(new Date()); | 
|             jam.setState(JamStateType.RUNNING.toString()); | 
|             if (!jamService.save(jam)) { | 
|                 log.error("AGV[{}] failed to save jam", agv.getUuid()); | 
|                 throw new CoolException("failed to save jam"); | 
|             } | 
|         } else { | 
|             jam.setDuration(System.currentTimeMillis() - jam.getStartTime().getTime()); | 
|             if (!jamService.updateById(jam)) { | 
|                 log.error("AGV[{}] failed to update jam", agv.getUuid()); | 
|             } | 
|         } | 
|         return jam; | 
|     } | 
|   | 
|     private Jam setupNewJam(Jam originJam, Agv agv, Code startCode, Segment segment, List<String> draftPath) { | 
|         originJam.setUpdateTime(new Date()); | 
|         originJam.setState(JamStateType.FINISH.toString()); | 
|         if (!jamService.updateById(originJam)) { | 
|             log.error("Jam[{}] failed to update", originJam.getUuid()); | 
|             return originJam; | 
|         } else { | 
|             return this.createOrUpdateJam( | 
|                     agv | 
|                     , startCode | 
|                     , segment | 
|                     , null | 
|                     , draftPath | 
|             ); | 
|         } | 
|     } | 
|   | 
|     private List<String> getBlackPathList(List<Jam> unfinishedOriginJamByCurrAgv) { | 
|         List<String> blackPathList = new ArrayList<>(); | 
|         Integer lev = MapDataDispatcher.MAP_DEFAULT_LEV; | 
|         if (!Cools.isEmpty(unfinishedOriginJamByCurrAgv)) { | 
|             for (Jam jam : unfinishedOriginJamByCurrAgv) { | 
|                 if (!Cools.isEmpty(jam.getJamPath())) { | 
|   | 
|                     List<String> list = GsonUtils.fromJsonToList(jam.getJamPath(), String.class); | 
|   | 
|                     Agv jamAgv = agvService.getById(jam.getJamAgv()); | 
|                     List<String> jamDynamicNodes = mapService.queryCodeListFromDynamicNode(lev, jamAgv.getUuid()); | 
|                     // jamDynamicNodes has sorted | 
|                     String firstCodeNode = jamDynamicNodes.stream().findFirst().orElse(null); | 
|   | 
|                     if (!Cools.isEmpty(firstCodeNode)) { | 
|                         int idx = list.indexOf(firstCodeNode); | 
|                         if (idx != -1) { | 
|                             list = new ArrayList<>(list.subList(idx, list.size())); | 
|   | 
|                             // the wave of first node | 
|                             Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance( | 
|                                     agvModelService.getById(jamAgv.getAgvModel()).getDiameter(), | 
|                                     MapDataConstant.MAX_DISTANCE_BETWEEN_ADJACENT_AGV_FACTOR | 
|                             ); | 
|                             List<String> waveCodeList = mapService.getWaveScopeByCode(lev, firstCodeNode, avoidDistance) | 
|                                     .stream().map(NavigateNode::getCodeData).distinct().collect(Collectors.toList()); | 
|                             list.addAll(waveCodeList); | 
|                         } else { | 
|                             // 如果被阻塞车辆已经不在原来的阻塞路径中,考虑避让车走行时不需要把之前的阻塞路径加入黑名单 | 
|                             list = new ArrayList<>(); | 
|                         } | 
|                     } | 
|   | 
|                     blackPathList.addAll(list); | 
|                 } | 
|             } | 
|         } | 
|         return blackPathList.stream().distinct().collect(Collectors.toList()); | 
|     } | 
|   | 
|     private String checkoutBestSolutionOfBlocks(List<BlockVehicleDto> blockVehicleList, Segment segment) { | 
|         assert !Cools.isEmpty(blockVehicleList); | 
|         for (BlockVehicleDto blockVehicleDto : blockVehicleList) { | 
|             if (!blockVehicleDto.isAvoidable()) { | 
|                 continue; | 
|             } | 
|             // 当前vehicle正在进行避让作业 | 
|             if (!Cools.isEmpty(jamService.getUnfinishedAvoSegByAvo(blockVehicleDto.getVehicle(), null))) { | 
|                 continue; | 
|             } | 
|             return blockVehicleDto.getVehicle(); | 
|         } | 
|         return null; | 
|     } | 
|   | 
|     public boolean comparePathLists(List<String> list1, List<String> list2) { | 
|         if (list1.equals(list2)) { | 
|             return true; | 
|         } | 
|         if (list1.containsAll(list2)) { | 
|             return true; | 
|         } | 
|         return false; | 
|     } | 
|   | 
|     /** | 
|      * 1. the jam was deprecated | 
|      * 2. the jam segment is finished | 
|      * | 
|      * all these by avoid segment | 
|      */ | 
|     private boolean isExpiredJamByAvo(Long avoSeg) { | 
|         List<Jam> jamList = jamService.list(new LambdaQueryWrapper<Jam>() | 
| //                .eq(Jam::getAvoAgv, avoAgv) | 
|                 .eq(Jam::getAvoSeg, avoSeg)); | 
|         if (Cools.isEmpty(jamList)) { | 
|             return false; | 
|         } | 
|         if (jamList.size() > 1) { | 
|             log.error("AvoSeg[id = {}] seg data has exception, result in two jams", avoSeg); | 
|         } | 
|         Jam jam = jamList.get(0); | 
|         if (jam.getState().equals(JamStateType.DEPRECATED.toString())) { | 
|             return true; | 
|         } | 
|         Segment jamSegment = segmentService.getById(jam.getJamSeg()); | 
|         if (jamSegment.getState().equals(SegmentStateType.FINISH.toString())) { | 
|             return true; | 
|         } | 
|         return false; | 
|     } | 
|   | 
| } |