|  |  |  | 
|---|
|  |  |  | package com.zy.acs.manager.core.service; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 
|---|
|  |  |  | import com.zy.acs.common.utils.RedisSupport; | 
|---|
|  |  |  | import com.zy.acs.common.utils.Utils; | 
|---|
|  |  |  | import com.zy.acs.framework.common.Cools; | 
|---|
|  |  |  | import com.zy.acs.manager.common.utils.MapDataUtils; | 
|---|
|  |  |  | import com.zy.acs.manager.core.constant.MapDataConstant; | 
|---|
|  |  |  | import com.zy.acs.manager.core.domain.Lane; | 
|---|
|  |  |  | import com.zy.acs.manager.core.service.astart.*; | 
|---|
|  |  |  | import com.zy.acs.manager.core.service.astart.domain.DynamicNode; | 
|---|
|  |  |  | import com.zy.acs.manager.core.utils.RouteGenerator; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.entity.Code; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.entity.Jam; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.entity.Route; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.service.AgvModelService; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.service.AgvService; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.service.CodeService; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.service.RouteService; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.entity.Segment; | 
|---|
|  |  |  | import com.zy.acs.manager.manager.enums.JamStateType; | 
|---|
|  |  |  | 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.Service; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | import java.util.*; | 
|---|
|  |  |  | import java.util.concurrent.CopyOnWriteArrayList; | 
|---|
|  |  |  | import java.util.stream.Collectors; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private final RedisSupport redis = RedisSupport.defaultRedisSupport; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | public static final int WEIGHT_CALC_COEFFICIENT = 1000; | 
|---|
|  |  |  | public static final int WEIGHT_CALC_FACTOR = 10000; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private CodeService codeService; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private RouteService routeService; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private MapDataDispatcher mapDataDispatcher; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | 
|---|
|  |  |  | private LaneService laneService; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private ConfigService configService; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private SegmentService segmentService; | 
|---|
|  |  |  | @Autowired | 
|---|
|  |  |  | private JamService jamService; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | /** | 
|---|
|  |  |  | * avoidPathList ===>>  [ minor vehicle ] [wave] [ curr vehicle ] [ code2 ] [ code3 ] ...... | 
|---|
|  |  |  | 
|---|
|  |  |  | String breakPoint = avoidPathList.stream().findFirst().orElse(null); | 
|---|
|  |  |  | List<String> blackList = Utils.singletonList(sponsor); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance(agvModelService.getById(agvService.selectByUuid(sponsor).getAgvModel()).getDiameter() | 
|---|
|  |  |  | , MapDataConstant.IDLE_DISTANCE_COE); | 
|---|
|  |  |  | Double avoidDistance = MapDataUtils.getVehicleWaveSafeDistance(agvModelService.getByAgvNo(sponsor).getDiameter() | 
|---|
|  |  |  | , MapDataConstant.MAX_DISTANCE_BETWEEN_ADJACENT_AGV_FACTOR); | 
|---|
|  |  |  | List<String> avoidPathListWave = mapService.getWaveScopeByCodeList(lev, avoidPathList, avoidDistance); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(lev); | 
|---|
|  |  |  | DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(lev); | 
|---|
|  |  |  | String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(lev); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | 
|---|
|  |  |  | RetreatNavigateNode finialNode = null; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | PriorityQueue<RetreatNavigateNode> openQueue = new PriorityQueue<>(); | 
|---|
|  |  |  | ArrayList<RetreatNavigateNode> existNodes = new ArrayList<>(); | 
|---|
|  |  |  | Set<RetreatNavigateNode> existNodes = new HashSet<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | openQueue.add(start); | 
|---|
|  |  |  | existNodes.add(start); | 
|---|
|  |  |  | boolean phaseSecond = true; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | while (openQueue.size() > 0 && null == finialNode) { | 
|---|
|  |  |  | while (!openQueue.isEmpty() && null == finialNode) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode currentNode = openQueue.poll(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | List<RetreatNavigateNode> enableNodes = new ArrayList<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | ArrayList<RetreatNavigateNode> neighborNodes = this.getNeighborNodes(currentNode, existNodes); | 
|---|
|  |  |  | List<RetreatNavigateNode> neighborNodes = this.getNeighborNodes(currentNode, existNodes, codeMatrix); | 
|---|
|  |  |  | boolean pointOfTurn = neighborNodes.size() >= 2; | 
|---|
|  |  |  | label: for (RetreatNavigateNode node : neighborNodes) { | 
|---|
|  |  |  | if (node.getCodeData().equals(breakPoint)) { continue; } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | 
|---|
|  |  |  | if (!Cools.isEmpty(blackList) && blackList.contains(otherWave)) { | 
|---|
|  |  |  | continue label; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (1 < mapDataDispatcher.queryCodeListFromDynamicNode(lev, otherWave).size()) { | 
|---|
|  |  |  | if (mapService.isWalkingByVehicle(lev, otherWave)) { | 
|---|
|  |  |  | phaseSecond = false;    // there is a running way | 
|---|
|  |  |  | continue label; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | weight += WEIGHT_CALC_COEFFICIENT; | 
|---|
|  |  |  | weight += WEIGHT_CALC_FACTOR; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // lane | 
|---|
|  |  |  | Lane lane = laneService.search(node.getCodeData()); | 
|---|
|  |  |  | if (null != lane) { | 
|---|
|  |  |  | Set<String> lanVehicleSet = new HashSet<>(); | 
|---|
|  |  |  | if (pointOfTurn) { | 
|---|
|  |  |  | List<int[]> laneCodeIdxList = laneService.getLaneCodeIdxList(node.getCodeData()); | 
|---|
|  |  |  | if (!Cools.isEmpty(laneCodeIdxList)) { | 
|---|
|  |  |  | Set<String> lanVehicleSet = new HashSet<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | List<String> laneCodes = lane.getCodes(); | 
|---|
|  |  |  | for (String laneCodeData : laneCodes) { | 
|---|
|  |  |  | int[] laneCodeMatrixIdx = mapDataDispatcher.getCodeMatrixIdx(null, laneCodeData); | 
|---|
|  |  |  | // scan dynamicMatrix or WaveMatrix | 
|---|
|  |  |  | DynamicNode laneDynamicNode = dynamicMatrix[laneCodeMatrixIdx[0]][laneCodeMatrixIdx[1]]; | 
|---|
|  |  |  | String laneVehicle = laneDynamicNode.getVehicle(); | 
|---|
|  |  |  | assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); | 
|---|
|  |  |  | if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { | 
|---|
|  |  |  | if (!laneVehicle.equals(agvNo)) { | 
|---|
|  |  |  | lanVehicleSet.add(laneVehicle); | 
|---|
|  |  |  | //                                redis.setObject(RedisConstant.AGV_TO_STANDBY_FLAG, laneVehicle, true, 30); | 
|---|
|  |  |  | for (int[] codeMatrixIdx : laneCodeIdxList) { | 
|---|
|  |  |  | // overlap with sponsor | 
|---|
|  |  |  | String laneCodeData = codeMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; | 
|---|
|  |  |  | if (avoidPathList.contains(laneCodeData)) { | 
|---|
|  |  |  | lanVehicleSet.add(sponsor); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | DynamicNode laneDynamicNode = dynamicMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; | 
|---|
|  |  |  | String laneVehicle = laneDynamicNode.getVehicle(); | 
|---|
|  |  |  | assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); | 
|---|
|  |  |  | if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { | 
|---|
|  |  |  | if (!laneVehicle.equals(agvNo)) { | 
|---|
|  |  |  | lanVehicleSet.add(laneVehicle); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { | 
|---|
|  |  |  | phaseSecond = false;    // there is a running way | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (lanVehicleSet.contains(sponsor)) { | 
|---|
|  |  |  | weight += WEIGHT_CALC_FACTOR * 2; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | Code code = codeService.getCacheByData(node.getCodeData()); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // judge whether the node has already been marked as a retreat node? | 
|---|
|  |  |  | // This is a very troublesome matter, if the node be repeatedly mark as a retreat node | 
|---|
|  |  |  | List<Segment> runningSegments = segmentService.getRunningByEndCode(code.getId()); | 
|---|
|  |  |  | for (Segment runningSeg : runningSegments) { | 
|---|
|  |  |  | if (0 < jamService.count(new LambdaQueryWrapper<Jam>() | 
|---|
|  |  |  | .eq(Jam::getAvoSeg, runningSeg.getId()) | 
|---|
|  |  |  | .ne(Jam::getState, JamStateType.DEPRECATED.toString()))) { | 
|---|
|  |  |  | weight += WEIGHT_CALC_FACTOR * 3; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | weight += WEIGHT_CALC_FACTOR; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (null == finialNode) { | 
|---|
|  |  |  | if (null == finialNode && phaseSecond) { | 
|---|
|  |  |  | //            assert openQueue.size() == 0; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | existNodes.clear(); | 
|---|
|  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode availablePointOfTurn = null; | 
|---|
|  |  |  | List<String> availablePointWaveScopeOfTurn = new ArrayList<>(); | 
|---|
|  |  |  | int actuallyLanesOfTurn = 0; | 
|---|
|  |  |  | int actualLanesOfTurn = 0; | 
|---|
|  |  |  | int filterPointOfTurnTimes = 0; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | while (openQueue.size() > 0 && null == finialNode) { | 
|---|
|  |  |  | while (!openQueue.isEmpty() && null == finialNode) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode currentNode = openQueue.poll(); | 
|---|
|  |  |  | List<RetreatNavigateNode> enableNodes = new ArrayList<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | ArrayList<RetreatNavigateNode> neighborNodes = this.getNeighborNodes(currentNode, existNodes); | 
|---|
|  |  |  | boolean pointOfTurn = false; | 
|---|
|  |  |  | if (neighborNodes.size() >= 2 && !isSame(start, currentNode)) { | 
|---|
|  |  |  | pointOfTurn = true; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | List<RetreatNavigateNode> neighborNodes = this.getNeighborNodes(currentNode, existNodes, codeMatrix); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 计算是否为可用转弯点 | 
|---|
|  |  |  | if (pointOfTurn) { | 
|---|
|  |  |  | for (RetreatNavigateNode node : neighborNodes) { | 
|---|
|  |  |  | // lane | 
|---|
|  |  |  | Lane lane = laneService.search(node.getCodeData()); | 
|---|
|  |  |  | if (null != lane) { | 
|---|
|  |  |  | Set<String> lanVehicleSet = new HashSet<>(); | 
|---|
|  |  |  | // 第一步:获取有效转弯点 | 
|---|
|  |  |  | if (null == availablePointOfTurn) { | 
|---|
|  |  |  | // 计算是否为可用转弯点 | 
|---|
|  |  |  | if (neighborNodes.size() >= 2 && !isSame(start, currentNode)) { | 
|---|
|  |  |  | filterPointOfTurnTimes ++; | 
|---|
|  |  |  | if (filterPointOfTurnTimes > 2) { break; } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | List<String> laneCodes = lane.getCodes(); | 
|---|
|  |  |  | for (String laneCodeData : laneCodes) { | 
|---|
|  |  |  | int[] laneCodeMatrixIdx = mapDataDispatcher.getCodeMatrixIdx(null, laneCodeData); | 
|---|
|  |  |  | // scan dynamicMatrix or WaveMatrix | 
|---|
|  |  |  | DynamicNode laneDynamicNode = dynamicMatrix[laneCodeMatrixIdx[0]][laneCodeMatrixIdx[1]]; | 
|---|
|  |  |  | String laneVehicle = laneDynamicNode.getVehicle(); | 
|---|
|  |  |  | assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); | 
|---|
|  |  |  | if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { | 
|---|
|  |  |  | if (!laneVehicle.equals(agvNo)) { | 
|---|
|  |  |  | lanVehicleSet.add(laneVehicle); | 
|---|
|  |  |  | //                                redis.setObject(RedisConstant.AGV_TO_STANDBY_FLAG, laneVehicle, true, 30); | 
|---|
|  |  |  | for (RetreatNavigateNode node : neighborNodes) { | 
|---|
|  |  |  | // lane | 
|---|
|  |  |  | List<int[]> laneCodeIdxList = laneService.getLaneCodeIdxList(node.getCodeData()); | 
|---|
|  |  |  | if (!Cools.isEmpty(laneCodeIdxList)) { | 
|---|
|  |  |  | Set<String> lanVehicleSet = new HashSet<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | for (int[] codeMatrixIdx : laneCodeIdxList) { | 
|---|
|  |  |  | DynamicNode laneDynamicNode = dynamicMatrix[codeMatrixIdx[0]][codeMatrixIdx[1]]; | 
|---|
|  |  |  | String laneVehicle = laneDynamicNode.getVehicle(); | 
|---|
|  |  |  | assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); | 
|---|
|  |  |  | if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { | 
|---|
|  |  |  | if (!laneVehicle.equals(agvNo)) { | 
|---|
|  |  |  | lanVehicleSet.add(laneVehicle); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | actualLanesOfTurn ++; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | actuallyLanesOfTurn ++; | 
|---|
|  |  |  | // 有两条以上可走巷道,则视为有效转弯点 | 
|---|
|  |  |  | if (actualLanesOfTurn >= 2) { | 
|---|
|  |  |  | availablePointOfTurn = currentNode; | 
|---|
|  |  |  | availablePointWaveScopeOfTurn = mapService.getWaveScopeByCode(lev, availablePointOfTurn.getCodeData(), avoidDistance) | 
|---|
|  |  |  | .stream().map(NavigateNode::getCodeData).distinct().collect(Collectors.toList()); | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | actualLanesOfTurn = 0; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (actuallyLanesOfTurn >= 2) { | 
|---|
|  |  |  | availablePointOfTurn = currentNode; | 
|---|
|  |  |  | availablePointWaveScopeOfTurn = mapService.getWaveScopeByCode(lev, availablePointOfTurn.getCodeData(), avoidDistance) | 
|---|
|  |  |  | .stream().map(NavigateNode::getCodeData).distinct().collect(Collectors.toList()); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 延伸转弯点巷道 | 
|---|
|  |  |  | label: for (RetreatNavigateNode node : neighborNodes) { | 
|---|
|  |  |  | int weight = 0; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | 
|---|
|  |  |  | if (!Cools.isEmpty(blackList) && blackList.contains(otherWave)) { | 
|---|
|  |  |  | continue; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (1 < mapDataDispatcher.queryCodeListFromDynamicNode(lev, otherWave).size()) { | 
|---|
|  |  |  | if (mapService.isWalkingByVehicle(lev, otherWave)) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (null != availablePointOfTurn && actuallyLanesOfTurn > 0) { | 
|---|
|  |  |  | actuallyLanesOfTurn --; | 
|---|
|  |  |  | if (null != availablePointOfTurn && actualLanesOfTurn > 0) { | 
|---|
|  |  |  | actualLanesOfTurn --; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | continue label; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | weight += WEIGHT_CALC_COEFFICIENT; | 
|---|
|  |  |  | weight += WEIGHT_CALC_FACTOR; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | 
|---|
|  |  |  | existNodes.add(node); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (!Cools.isEmpty(enableNodes)) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (actuallyLanesOfTurn >= 2) { | 
|---|
|  |  |  | if (actualLanesOfTurn < 2) { | 
|---|
|  |  |  | availablePointOfTurn = null; | 
|---|
|  |  |  | availablePointWaveScopeOfTurn = new ArrayList<>(); | 
|---|
|  |  |  | actualLanesOfTurn = 0; | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | if (!Cools.isEmpty(enableNodes)) { | 
|---|
|  |  |  | Collections.sort(enableNodes); | 
|---|
|  |  |  | finialNode = enableNodes.stream().findFirst().orElse(null); | 
|---|
|  |  |  | jam.setCycleAvo(1); | 
|---|
|  |  |  | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 获取四周节点 | 
|---|
|  |  |  | private ArrayList<RetreatNavigateNode> getNeighborNodes(RetreatNavigateNode currentNode, List<RetreatNavigateNode> existNodes) { | 
|---|
|  |  |  | private List<RetreatNavigateNode> getNeighborNodes(RetreatNavigateNode currentNode, Set<RetreatNavigateNode> existNodes, String[][] codeMatrix) { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | int x = currentNode.getX(); | 
|---|
|  |  |  | int y = currentNode.getY(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | ArrayList<RetreatNavigateNode> neighbourNodes = new ArrayList<>(); | 
|---|
|  |  |  | List<RetreatNavigateNode> neighbourNodes = new CopyOnWriteArrayList<>(); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode rightNode = extendNeighborNodes(currentNode, new RetreatNavigateNode(x, y + 1), existNodes, null, null); | 
|---|
|  |  |  | if (null != rightNode) { | 
|---|
|  |  |  | neighbourNodes.add(rightNode); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | List<RetreatNavigateNode> possibleNodes = Arrays.asList( | 
|---|
|  |  |  | new RetreatNavigateNode(x, y + 1), // right | 
|---|
|  |  |  | new RetreatNavigateNode(x, y - 1), // left | 
|---|
|  |  |  | new RetreatNavigateNode(x - 1, y), // up | 
|---|
|  |  |  | new RetreatNavigateNode(x + 1, y)  // down | 
|---|
|  |  |  | ); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode leftNode = extendNeighborNodes(currentNode, new RetreatNavigateNode(x, y - 1), existNodes, null, null); | 
|---|
|  |  |  | if (null != leftNode) { | 
|---|
|  |  |  | neighbourNodes.add(leftNode); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode topNode = extendNeighborNodes(currentNode, new RetreatNavigateNode(x - 1, y), existNodes, null, null); | 
|---|
|  |  |  | if (null != topNode) { | 
|---|
|  |  |  | neighbourNodes.add(topNode); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | RetreatNavigateNode bottomNode = extendNeighborNodes(currentNode, new RetreatNavigateNode(x + 1, y), existNodes, null, null); | 
|---|
|  |  |  | if (null != bottomNode) { | 
|---|
|  |  |  | neighbourNodes.add(bottomNode); | 
|---|
|  |  |  | } | 
|---|
|  |  |  | possibleNodes.parallelStream() | 
|---|
|  |  |  | .map(extendNode -> extendNeighborNodes(currentNode, extendNode, existNodes, null, null)) | 
|---|
|  |  |  | .filter(Objects::nonNull) | 
|---|
|  |  |  | .peek(node -> node.setCodeData(codeMatrix[node.getX()][node.getY()])) | 
|---|
|  |  |  | .forEach(neighbourNodes::add); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return neighbourNodes; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private RetreatNavigateNode extendNeighborNodes(RetreatNavigateNode currentNode, RetreatNavigateNode extendNode, List<RetreatNavigateNode> existNodes, Integer dx, Integer dy) { | 
|---|
|  |  |  | private RetreatNavigateNode extendNeighborNodes(RetreatNavigateNode currentNode, RetreatNavigateNode extendNode, Set<RetreatNavigateNode> existNodes, Integer dx, Integer dy) { | 
|---|
|  |  |  | RetreatNavigateNode nextNode; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | if (null == dx || null == dy) { | 
|---|
|  |  |  | 
|---|
|  |  |  | if (mapMatrix[x][y] == MapNodeType.DISABLE.val)  { | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return extendNeighborNodes(currentNode, nextNode, existNodes, dx, dy); | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | } else { | 
|---|
|  |  |  | if (this.isExist(nextNode, existNodes)) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | assert mapMatrix[x][y] == MapNodeType.ENABLE.val; | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 节点是否可用 | 
|---|
|  |  |  | if (mapMatrix[x][y] != MapNodeType.ENABLE.val) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | if (existNodes.contains(nextNode)) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(null); | 
|---|
|  |  |  | String currentNodeCodeData = codeMatrix[currentNode.getX()][currentNode.getY()]; | 
|---|
|  |  |  | String nextNodeCodeData = codeMatrix[nextNode.getX()][nextNode.getY()]; | 
|---|
|  |  |  | nextNode.setCodeData(nextNodeCodeData); | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 判断通过性 | 
|---|
|  |  |  | Route route = routeService.findByCodeOfBoth( | 
|---|
|  |  |  | codeService.selectByData(currentNodeCodeData).getId(), | 
|---|
|  |  |  | codeService.selectByData(nextNodeCodeData).getId() | 
|---|
|  |  |  | ); | 
|---|
|  |  |  | if (null == route) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | // 判断通过性 | 
|---|
|  |  |  | String routeCdaKey = RouteGenerator.generateRouteCdaKey(new int[]{currentNode.getX(), currentNode.getY()}, new int[]{nextNode.getX(), nextNode.getY()}); | 
|---|
|  |  |  | if (!mapDataDispatcher.validRouteCdaKey(routeCdaKey)) { | 
|---|
|  |  |  | return null; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | return nextNode; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private boolean isExist(RetreatNavigateNode node, List<RetreatNavigateNode> existNodes) { | 
|---|
|  |  |  | for (RetreatNavigateNode existNode : existNodes) { | 
|---|
|  |  |  | if (this.isSame(node, existNode)) { | 
|---|
|  |  |  | return true; | 
|---|
|  |  |  | } | 
|---|
|  |  |  | } | 
|---|
|  |  |  | return false; | 
|---|
|  |  |  | } | 
|---|
|  |  |  |  | 
|---|
|  |  |  | private boolean isSame(RetreatNavigateNode o1, RetreatNavigateNode o2) { | 
|---|