| | |
| | | package com.zy.acs.manager.core.service.astart; |
| | | |
| | | import com.zy.acs.common.constant.RedisConstant; |
| | | import com.zy.acs.common.utils.RedisSupport; |
| | | import com.zy.acs.framework.common.Cools; |
| | | import com.zy.acs.manager.common.utils.MapDataUtils; |
| | | import com.zy.acs.manager.core.domain.Lane; |
| | | import com.zy.acs.manager.core.service.LaneService; |
| | | import com.zy.acs.manager.core.service.astart.domain.AStarNavigateNode; |
| | | 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.Route; |
| | | import com.zy.acs.manager.manager.entity.Segment; |
| | | import com.zy.acs.manager.manager.service.CodeService; |
| | | import com.zy.acs.manager.manager.service.JamService; |
| | | import com.zy.acs.manager.manager.service.RouteService; |
| | | 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; |
| | | |
| | |
| | | /** |
| | | * Created by vincent on 6/12/2024 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class AStarNavigateService { |
| | | |
| | |
| | | |
| | | public static final int WEIGHT_CALC_FACTOR = 1; |
| | | |
| | | @Autowired |
| | | private CodeService codeService; |
| | | @Autowired |
| | | private RouteService routeService; |
| | | // right left up down |
| | | private final static int[][] DIRECTIONS = {{0,1},{0,-1},{-1,0},{1,0}}; |
| | | |
| | | @Autowired |
| | | private MapDataDispatcher mapDataDispatcher; |
| | | @Autowired |
| | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | |
| | | public synchronized NavigateNode execute(String agvNo, NavigateNode start, NavigateNode end |
| | | public synchronized AStarNavigateNode execute(String agvNo, AStarNavigateNode start, AStarNavigateNode end |
| | | , Boolean lock, List<String> blackList, Segment segment) { |
| | | if (start.getX() == end.getX() && start.getY() == end.getY()) { |
| | | return end; |
| | | } |
| | | Integer maxAgvCountInLane = configService.getVal("maxAgvCountInLane", Integer.class); |
| | | |
| | | PriorityQueue<NavigateNode> openQueue = new PriorityQueue<>(); |
| | | Set<NavigateNode> existNodes = new HashSet<>(); |
| | | PriorityQueue<AStarNavigateNode> openQueue = new PriorityQueue<>(); |
| | | Set<AStarNavigateNode> existNodes = new HashSet<>(); |
| | | Map<String, Integer> bestGMap = new HashMap<>(); |
| | | |
| | | start.setG(0); |
| | | start.setH(calcNodeCost(start, end)); |
| | | start.setF(start.getG() + start.getH()); |
| | | String startKey = start.getX() + "_" + start.getY(); |
| | | bestGMap.put(startKey, start.getG()); |
| | | |
| | | openQueue.add(start); |
| | | existNodes.add(start); |
| | | |
| | | int[][] mapMatrix = mapDataDispatcher.getMapMatrix(null, null); |
| | | String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(null); |
| | | DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null); |
| | | String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(null); |
| | | |
| | | long getNeighborNodesTime = 0; |
| | | int getNeighborNodesCount = 0; |
| | | |
| | | while (!openQueue.isEmpty()) { |
| | | // 取优先队列顶部元素并且把这个元素从Open表中删除,取F值最小的节点 |
| | | NavigateNode currentNode = openQueue.poll(); |
| | | AStarNavigateNode currentNode = openQueue.poll(); |
| | | |
| | | ArrayList<NavigateNode> neighbourNodes = this.getNeighborNodes(currentNode, mapMatrix, existNodes); |
| | | for (NavigateNode node : neighbourNodes) { |
| | | // 终点 |
| | | if (currentNode.getX() == end.getX() && currentNode.getY() == end.getY()) { |
| | | // System.out.println("getNeighborNodes spend time: " + getNeighborNodesTime +", count: " + getNeighborNodesCount); |
| | | return currentNode; |
| | | } |
| | | long currentTime = System.currentTimeMillis(); |
| | | List<AStarNavigateNode> neighbourNodes = this.getNeighborNodes(currentNode, mapMatrix, existNodes); |
| | | getNeighborNodesTime += System.currentTimeMillis() - currentTime; |
| | | getNeighborNodesCount ++; |
| | | for (AStarNavigateNode node : neighbourNodes) { |
| | | node.setCodeData(codeMatrix[node.getX()][node.getY()]); |
| | | |
| | | boolean isEndNode = node.getX() == end.getX() && node.getY() == end.getY(); |
| | | |
| | | int weight = 0; |
| | |
| | | assert !vehicle.equals(DynamicNodeType.BLOCK.val); |
| | | if (!vehicle.equals(DynamicNodeType.ACCESS.val)) { |
| | | if (!vehicle.equals(agvNo)) { |
| | | if (lock) { |
| | | continue; |
| | | } |
| | | |
| | | // 如果存在车辆,则增加权重 2 或者 3,因为拐点会增加权重 1 |
| | | // vehicle已经为当前segment做过了避让,且避让任务已完成,则权重值增加 |
| | |
| | | weight += (WEIGHT_CALC_FACTOR * 2); |
| | | } |
| | | } |
| | | |
| | | if (lock) { |
| | | continue; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | if (!waveNode.equals(WaveNodeType.ENABLE.val)) { |
| | | List<String> waveNodeList = MapDataUtils.parseWaveNode(waveNode); |
| | | List<String> otherWaveList = MapDataUtils.hasOtherWave(waveNodeList, agvNo); |
| | | |
| | | if (!Cools.isEmpty(otherWaveList)) { |
| | | |
| | | if (lock) { |
| | |
| | | } |
| | | |
| | | // 单巷道车辆容载数量 |
| | | Lane lane = laneService.search(node.getCodeData()); |
| | | if (null != lane) { |
| | | 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]]; |
| | | 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); |
| | | // redis.setObject(RedisConstant.AGV_TO_STANDBY_FLAG, laneVehicle, true, 30); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | |
| | | //找到目标结点就返回 |
| | | if (isEndNode) { |
| | | //并且计算出G, F, H等值 |
| | | node.initNode(currentNode, end); |
| | | return node; |
| | | } |
| | | |
| | | // G + H + T (对启发函数增加去拐点方案calcNodeTurnCost) |
| | | int gCost = calcNodeCost(currentNode, node); |
| | | |
| | | if (OPEN_TURN_COST_WEIGHT) { |
| | | gCost += calcNodeTurnCost(currentNode, node, end); |
| | | if (this.isTurning(currentNode, node)) { |
| | | weight += WEIGHT_CALC_FACTOR; |
| | | node.setTurnCount(currentNode.getTurnCount() + 1); |
| | | } else { |
| | | // 方向没变 |
| | | node.setTurnCount(currentNode.getTurnCount()); |
| | | } |
| | | } |
| | | |
| | | //进行计算对 G, F, H 等值 |
| | | node.setWeight(weight); |
| | | node.setLastDistance(gCost); |
| | | node.setLastDistance(calcNodeCost(currentNode, node)); |
| | | node.initNode(currentNode, end); |
| | | node.setH(calcNodeCost(node, end)); |
| | | node.setF(node.getG() + node.getH()); |
| | | |
| | | openQueue.add(node); |
| | | existNodes.add(node); |
| | | String key = node.getX() + "_" + node.getY(); |
| | | Integer recordedG = bestGMap.get(key); |
| | | if (recordedG == null || node.getG() <= recordedG) { |
| | | bestGMap.put(key, node.getG()); |
| | | |
| | | openQueue.add(node); |
| | | } |
| | | |
| | | // openQueue.add(node); |
| | | // existNodes.add(node); |
| | | } |
| | | } |
| | | //如果遍历完所有出现的结点都没有找到最终的结点,返回null |
| | | // System.out.println("getNeighborNodes spend time: " + getNeighborNodesTime +", count: " + getNeighborNodesCount); |
| | | return null; |
| | | } |
| | | |
| | | // 获取四周节点 |
| | | private ArrayList<NavigateNode> getNeighborNodes(NavigateNode currentNode, int[][] mapMatrix, Set<NavigateNode> existNodes) { |
| | | //获取当前结点的x, y |
| | | private List<AStarNavigateNode> getNeighborNodes(AStarNavigateNode currentNode, int[][] mapMatrix, Set<AStarNavigateNode> existNodes) { |
| | | int x = currentNode.getX(); |
| | | int y = currentNode.getY(); |
| | | //如果当前结点的邻结点合法,就加入到neighbour_node |
| | | ArrayList<NavigateNode> neighbourNodes = new ArrayList<>(); |
| | | AStarNavigateNode parent = currentNode.getParent(); |
| | | |
| | | NavigateNode rightNode = extendNeighborNodes(currentNode, new NavigateNode(x, y + 1), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, rightNode)) { |
| | | neighbourNodes.add(rightNode); |
| | | // List<AStarNavigateNode> neighbourNodes = new CopyOnWriteArrayList<>(); |
| | | List<AStarNavigateNode> neighbourNodes = new ArrayList<>(); |
| | | |
| | | List<AStarNavigateNode> possibleNodes = new ArrayList<>(); |
| | | for (int[] d: DIRECTIONS) { |
| | | int nx = x + d[0]; |
| | | int ny = y + d[1]; |
| | | // 如果父节点不为空,并且 (nx,ny) 等于父节点坐标,则跳过 |
| | | if (parent != null && nx == parent.getX() && ny == parent.getY()) { |
| | | continue; |
| | | } |
| | | possibleNodes.add(new AStarNavigateNode(nx, ny)); |
| | | } |
| | | |
| | | NavigateNode leftNode = extendNeighborNodes(currentNode, new NavigateNode(x, y - 1), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, leftNode)) { |
| | | neighbourNodes.add(leftNode); |
| | | } |
| | | // possibleNodes.parallelStream() |
| | | // .map(extendNode -> extendNeighborNodes(currentNode, extendNode, mapMatrix, existNodes, null, null)) |
| | | // .filter(Objects::nonNull) |
| | | // .forEach(neighbourNodes::add); |
| | | |
| | | NavigateNode topNode = extendNeighborNodes(currentNode, new NavigateNode(x - 1, y), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, topNode)) { |
| | | neighbourNodes.add(topNode); |
| | | } |
| | | |
| | | NavigateNode bottomNode = extendNeighborNodes(currentNode, new NavigateNode(x + 1, y), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, bottomNode)) { |
| | | neighbourNodes.add(bottomNode); |
| | | for (AStarNavigateNode pn : possibleNodes) { |
| | | AStarNavigateNode next = extendNeighborNodes(currentNode, pn, mapMatrix, existNodes, null, null); |
| | | if (next != null) { |
| | | neighbourNodes.add(next); |
| | | } |
| | | } |
| | | |
| | | return neighbourNodes; |
| | | } |
| | | |
| | | private NavigateNode extendNeighborNodes(NavigateNode currentNode, NavigateNode extendNode, int[][] mapMatrix, Set<NavigateNode> existNodes, Integer dx, Integer dy) { |
| | | NavigateNode nextNode = null; |
| | | private AStarNavigateNode extendNeighborNodes(AStarNavigateNode currentNode, AStarNavigateNode extendNode, int[][] mapMatrix, Set<AStarNavigateNode> existNodes, Integer dx, Integer dy) { |
| | | AStarNavigateNode nextNode; |
| | | |
| | | if (null == dx || null == dy) { |
| | | dx = extendNode.getX() - currentNode.getX(); |
| | | dy = extendNode.getY() - currentNode.getY(); |
| | | nextNode = extendNode; |
| | | } else { |
| | | nextNode = new NavigateNode(extendNode.getX() + dx, extendNode.getY() + dy); |
| | | nextNode = new AStarNavigateNode(extendNode.getX() + dx, extendNode.getY() + dy); |
| | | } |
| | | |
| | | int x = nextNode.getX(); |
| | |
| | | if (mapMatrix[x][y] == MapNodeType.DISABLE.val) { |
| | | |
| | | return extendNeighborNodes(currentNode, nextNode, mapMatrix, existNodes, dx, dy); |
| | | |
| | | } else { |
| | | |
| | | if (existNodes.contains(nextNode)) { |
| | | return null; |
| | | } |
| | | |
| | | // 节点是否可用 |
| | | if (mapMatrix[x][y] != MapNodeType.ENABLE.val) { |
| | | return null; |
| | | } |
| | | |
| | | String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(null); |
| | | String currentNodeCodeData = codeMatrix[currentNode.getX()][currentNode.getY()]; |
| | | String nextNodeCodeData = codeMatrix[nextNode.getX()][nextNode.getY()]; |
| | | nextNode.setCodeData(nextNodeCodeData); |
| | | |
| | | // 判断通过性 |
| | | String routeKey = RouteGenerator.generateRouteKey(currentNodeCodeData, nextNodeCodeData); |
| | | Object routeVal = redis.getMap(RedisConstant.AGV_MAP_ROUTE_HASH_FLAG, routeKey); |
| | | if (routeVal == null || !(Boolean) routeVal) { |
| | | return null; |
| | | } |
| | | |
| | | return nextNode; |
| | | } |
| | | } |
| | | |
| | | private boolean is_valid(NavigateNode currentNode, NavigateNode node) { |
| | | if (null == node) { |
| | | return false; |
| | | } |
| | | return true; |
| | | } |
| | | assert mapMatrix[x][y] == MapNodeType.ENABLE.val; |
| | | |
| | | private boolean isExist(NavigateNode node, List<NavigateNode> existNodes) { |
| | | for (NavigateNode existNode : existNodes) { |
| | | if (this.isSame(node, existNode)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | // if (existNodes.contains(nextNode)) { |
| | | // return null; |
| | | // } |
| | | |
| | | private boolean isSame(NavigateNode o1, NavigateNode o2) { |
| | | if (Cools.isEmpty(o1, o2)) { |
| | | return false; |
| | | // 判断通过性 |
| | | String routeCdaKey = RouteGenerator.generateRouteCdaKey(new int[]{currentNode.getX(), currentNode.getY()}, new int[]{nextNode.getX(), nextNode.getY()}); |
| | | if (!mapDataDispatcher.validRouteCdaKey(routeCdaKey)) { |
| | | return null; |
| | | } |
| | | return o1.getX() == o2.getX() && o1.getY() == o2.getY(); |
| | | |
| | | return nextNode; |
| | | } |
| | | |
| | | //------------------A*启发函数------------------// |
| | | |
| | | //计算通过现在的结点的位置和最终结点的位置计算H值(曼哈顿法:坐标分别取差值相加) |
| | | private int calcNodeCost(NavigateNode node1, NavigateNode node2) { |
| | | // Code code1 = codeService.selectByData(node1.getCodeData()); |
| | | // Code code2 = codeService.selectByData(node2.getCodeData()); |
| | | // return (int) (Math.abs(code2.getX() - code1.getX()) + Math.abs(code2.getY() - code1.getY())); |
| | | private int calcNodeCost(AStarNavigateNode node1, AStarNavigateNode node2) { |
| | | return Math.abs(node2.getX() - node1.getX()) + Math.abs(node2.getY() - node1.getY()); |
| | | } |
| | | |
| | | //去除拐点算法,给直线增加优先级 |
| | | private int calcNodeTurnCost(NavigateNode currNode, NavigateNode nextNode, NavigateNode endNode) { |
| | | // 第一个点或直线点 |
| | | if (currNode.getParent() == null |
| | | || nextNode.getX() == currNode.getParent().getX() |
| | | || nextNode.getY() == currNode.getParent().getY() |
| | | ) { |
| | | return 0; |
| | | // 转弯判断:只允许“垂直或水平”运动 |
| | | private boolean isTurning(AStarNavigateNode currNode, AStarNavigateNode nextNode) { |
| | | // 第一个点 |
| | | if (currNode.getParent() == null) { |
| | | return false; |
| | | } |
| | | |
| | | // 拐向终点的点 |
| | | if (nextNode.getX() == endNode.getX() || nextNode.getY() == endNode.getY()) { |
| | | return 1; |
| | | } |
| | | |
| | | // 普通拐点 |
| | | return 1; |
| | | AStarNavigateNode parent = currNode.getParent(); |
| | | // 如果下一点(nextNode)与 parent 在同一行或同一列 => 没有转弯 |
| | | // 注意,这实际上等同于“(curr->next) 的方向 == (parent->curr) 的方向”。 |
| | | boolean sameRowOrCol = |
| | | (nextNode.getX() == parent.getX()) |
| | | || (nextNode.getY() == parent.getY()); |
| | | return !sameRowOrCol; |
| | | } |
| | | |
| | | // 转弯判断:如果这两个向量相同(例如都等于 (0,1)),说明方向相同;否则说明转弯。 |
| | | // private boolean isTurning(AStarNavigateNode currNode, AStarNavigateNode nextNode) { |
| | | // // 如果 currNode 没有父节点,说明是起点,不算转弯 |
| | | // if (currNode.getParent() == null) { |
| | | // return false; |
| | | // } |
| | | // // 取出坐标 |
| | | // AStarNavigateNode parent = currNode.getParent(); |
| | | // int px = currNode.getX() - parent.getX(); // parent -> curr 的x偏移 |
| | | // int py = currNode.getY() - parent.getY(); // parent -> curr 的y偏移 |
| | | // |
| | | // int nx = nextNode.getX() - currNode.getX(); // curr -> next 的x偏移 |
| | | // int ny = nextNode.getY() - currNode.getY(); // curr -> next 的y偏移 |
| | | // |
| | | // // 如果 (px, py) 与 (nx, ny) 不一样,就说明转弯 |
| | | // return (px != nx) || (py != ny); |
| | | // } |
| | | |
| | | } |