| | |
| | | package com.zy.acs.manager.core.service.astart; |
| | | |
| | | 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.DynamicNode; |
| | | import com.zy.acs.manager.manager.entity.Route; |
| | | import com.zy.acs.manager.manager.service.AgvService; |
| | | 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 org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.PriorityQueue; |
| | | import java.util.*; |
| | | |
| | | /** |
| | | * Created by vincent on 6/12/2024 |
| | |
| | | @Service |
| | | public class AStarNavigateService { |
| | | |
| | | public static final boolean OPEN_TURN_COST_WEIGHT = Boolean.FALSE; |
| | | private final RedisSupport redis = RedisSupport.defaultRedisSupport; |
| | | |
| | | public static final boolean OPEN_TURN_COST_WEIGHT = Boolean.TRUE; |
| | | |
| | | public static final int WEIGHT_CALC_FACTOR = 1; |
| | | |
| | | @Autowired |
| | | private CodeService codeService; |
| | |
| | | @Autowired |
| | | private MapDataDispatcher mapDataDispatcher; |
| | | @Autowired |
| | | private AgvService agvService; |
| | | private JamService jamService; |
| | | @Autowired |
| | | private LaneService laneService; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | |
| | | public synchronized NavigateNode execute(String agvNo, NavigateNode start, NavigateNode end |
| | | , Boolean lock, List<String> blackList) { |
| | | , 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<>(); |
| | | ArrayList<NavigateNode> existNodes = new ArrayList<>(); |
| | | Set<NavigateNode> existNodes = new HashSet<>(); |
| | | |
| | | openQueue.add(start); |
| | | existNodes.add(start); |
| | |
| | | DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null); |
| | | String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(null); |
| | | |
| | | // List<String> included = new ArrayList<>(); |
| | | // if (!Cools.isEmpty(whiteList)) { |
| | | // included.addAll(whiteList); |
| | | // } |
| | | // included.add(agvNo); |
| | | // List<VehicleDto> vehicleDtoList = agvService.getVehicleDtoList(included); |
| | | |
| | | while (openQueue.size() > 0) { |
| | | while (!openQueue.isEmpty()) { |
| | | // 取优先队列顶部元素并且把这个元素从Open表中删除,取F值最小的节点 |
| | | NavigateNode currentNode = openQueue.poll(); |
| | | // 对当前结点进行扩展,得到一个四周结点的数组 |
| | | ArrayList<NavigateNode> neighbourNodes = this.getNeighborNodes(currentNode, mapMatrix, existNodes); |
| | | // 对这个结点遍历,看是否有目标结点出现 |
| | | label: for (NavigateNode node : neighbourNodes) { |
| | | |
| | | // 节点存在其他车辆 |
| | | // for (VehicleDto vehicleDto : vehicleDtoList) { |
| | | // if (node.getCodeData().equals(vehicleDto.getPosCode())) { |
| | | // if (!Cools.isEmpty(blackList) && blackList.contains(vehicleDto.getVehicle())) { |
| | | // continue label; |
| | | // } |
| | | // if (lock) { |
| | | // continue label; |
| | | // } |
| | | // } |
| | | // } |
| | | ArrayList<NavigateNode> neighbourNodes = this.getNeighborNodes(currentNode, mapMatrix, existNodes); |
| | | for (NavigateNode node : neighbourNodes) { |
| | | boolean isEndNode = node.getX() == end.getX() && node.getY() == end.getY(); |
| | | |
| | | int weight = 0; |
| | | |
| | | if (!Cools.isEmpty(blackList) && blackList.contains(node.getCodeData())) { |
| | | continue; |
| | | } |
| | | // 特殊情况,当blackList有且只有一个元素且为startNode时 |
| | | // 说明blackList已经知道当前导航起始点和目标点为相邻节点 |
| | | // 但是当前blackList的任务是不让系统走相邻的最短路径,所以才会有下面的判断和continue |
| | | if (blackList.size() == 1 && blackList.get(0).equals(start.getCodeData())) { |
| | | if (isEndNode && currentNode.getCodeData().equals(start.getCodeData())) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | // 节点被占用 |
| | | DynamicNode dynamicNode = dynamicMatrix[node.getX()][node.getY()]; |
| | |
| | | if (!vehicle.equals(DynamicNodeType.ACCESS.val)) { |
| | | if (!vehicle.equals(agvNo)) { |
| | | |
| | | if (!Cools.isEmpty(blackList) && blackList.contains(vehicle)) { |
| | | continue; |
| | | // 如果存在车辆,则增加权重 2 或者 3,因为拐点会增加权重 1 |
| | | // vehicle已经为当前segment做过了避让,且避让任务已完成,则权重值增加 |
| | | if (null != segment) { |
| | | if (!Cools.isEmpty(jamService.getJamFromSegmentByAvo(segment, vehicle))) { |
| | | weight += (WEIGHT_CALC_FACTOR * 3); |
| | | } else { |
| | | weight += (WEIGHT_CALC_FACTOR * 2); |
| | | } |
| | | } |
| | | |
| | | if (lock) { |
| | | continue; |
| | | } |
| | |
| | | List<String> otherWaveList = MapDataUtils.hasOtherWave(waveNodeList, agvNo); |
| | | |
| | | if (!Cools.isEmpty(otherWaveList)) { |
| | | if (!Cools.isEmpty(blackList) && 0 < Cools.getIntersection(otherWaveList, blackList).size()) { |
| | | continue; |
| | | } |
| | | |
| | | if (lock) { |
| | | continue; |
| | |
| | | // 单巷道车辆容载数量 |
| | | Lane lane = laneService.search(node.getCodeData()); |
| | | if (null != lane) { |
| | | int otherVehicleCount = 0; |
| | | Set<String> lanVehicleSet = new HashSet<>(); |
| | | |
| | | List<String> laneCodes = lane.getCodes(); |
| | | for (String laneCodeData : laneCodes) { |
| | |
| | | assert !laneVehicle.equals(DynamicNodeType.BLOCK.val); |
| | | if (!laneVehicle.equals(DynamicNodeType.ACCESS.val)) { |
| | | if (!laneVehicle.equals(agvNo)) { |
| | | otherVehicleCount++; |
| | | lanVehicleSet.add(laneVehicle); |
| | | // redis.setObject(RedisConstant.AGV_TO_STANDBY_FLAG, laneVehicle, true, 30); |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (otherVehicleCount + 1 > maxAgvCountInLane) { |
| | | if (lock) { |
| | | continue; |
| | | } |
| | | if (lanVehicleSet.size() + 1 > maxAgvCountInLane) { |
| | | continue; |
| | | } |
| | | } |
| | | |
| | | |
| | | //找到目标结点就返回 |
| | | if (node.getX() == end.getX() && node.getY() == end.getY()) { |
| | | if (isEndNode) { |
| | | //并且计算出G, F, H等值 |
| | | node.initNode(currentNode, end); |
| | | return node; |
| | | } |
| | | |
| | | // G + H + T (对启发函数增加去拐点方案calcNodeTurnCost) |
| | | int gCost = calcNodeCost(currentNode, node) * (OPEN_TURN_COST_WEIGHT ? calcNodeTurnCost(currentNode, node, end) : 1); |
| | | int gCost = calcNodeCost(currentNode, node); |
| | | |
| | | if (OPEN_TURN_COST_WEIGHT) { |
| | | gCost += calcNodeTurnCost(currentNode, node, end); |
| | | } |
| | | |
| | | //进行计算对 G, F, H 等值 |
| | | node.setWeight(weight); |
| | | node.setLastDistance(gCost); |
| | | node.initNode(currentNode, end); |
| | | node.setH(calcNodeCost(node, end)); |
| | |
| | | } |
| | | |
| | | // 获取四周节点 |
| | | private ArrayList<NavigateNode> getNeighborNodes(NavigateNode currentNode, int[][] mapMatrix, List<NavigateNode> existNodes) { |
| | | private ArrayList<NavigateNode> getNeighborNodes(NavigateNode currentNode, int[][] mapMatrix, Set<NavigateNode> existNodes) { |
| | | //获取当前结点的x, y |
| | | int x = currentNode.getX(); |
| | | int y = currentNode.getY(); |
| | |
| | | ArrayList<NavigateNode> neighbourNodes = new ArrayList<>(); |
| | | |
| | | NavigateNode rightNode = extendNeighborNodes(currentNode, new NavigateNode(x, y + 1), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, rightNode, mapMatrix, existNodes)) { |
| | | if (is_valid(currentNode, rightNode)) { |
| | | neighbourNodes.add(rightNode); |
| | | } |
| | | |
| | | NavigateNode leftNode = extendNeighborNodes(currentNode, new NavigateNode(x, y - 1), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, leftNode, mapMatrix, existNodes)) { |
| | | if (is_valid(currentNode, leftNode)) { |
| | | neighbourNodes.add(leftNode); |
| | | } |
| | | |
| | | NavigateNode topNode = extendNeighborNodes(currentNode, new NavigateNode(x - 1, y), mapMatrix, existNodes, null, null); |
| | | if (is_valid(currentNode, topNode, mapMatrix, existNodes)) { |
| | | 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, mapMatrix, existNodes)) { |
| | | if (is_valid(currentNode, bottomNode)) { |
| | | neighbourNodes.add(bottomNode); |
| | | } |
| | | |
| | | return neighbourNodes; |
| | | } |
| | | |
| | | private NavigateNode extendNeighborNodes(NavigateNode currentNode, NavigateNode extendNode, int[][] mapMatrix, List<NavigateNode> existNodes, Integer dx, Integer dy) { |
| | | private NavigateNode extendNeighborNodes(NavigateNode currentNode, NavigateNode extendNode, int[][] mapMatrix, Set<NavigateNode> existNodes, Integer dx, Integer dy) { |
| | | NavigateNode nextNode = null; |
| | | |
| | | if (null == dx || null == dy) { |
| | |
| | | |
| | | } else { |
| | | |
| | | if (isExist(nextNode, existNodes)) { |
| | | if (existNodes.contains(nextNode)) { |
| | | return null; |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private boolean is_valid(NavigateNode currentNode, NavigateNode node, int[][] mapMatrix, List<NavigateNode> existNodes) { |
| | | private boolean is_valid(NavigateNode currentNode, NavigateNode node) { |
| | | if (null == node) { |
| | | return false; |
| | | } |
| | | // int x = node.getX(); |
| | | // int y = node.getY(); |
| | | // if (x < 0 || x >= mapMatrix.length |
| | | // || y < 0 || y >= mapMatrix[0].length) { |
| | | // return false; |
| | | // } |
| | | // |
| | | // // 如果结点的位置小于0,则不合法 |
| | | // if (mapMatrix[x][y] < 0) return false; |
| | | // |
| | | // if (is_exist(node, existNodes)) { |
| | | // return false; |
| | | // } |
| | | // |
| | | // // 判断通过性 |
| | | // String[][] codeMatrix = mapDataDispatcher.getCodeMatrix(null); |
| | | // String currentNodeCodeData = codeMatrix[currentNode.getX()][currentNode.getY()]; |
| | | // String nextNodeCodeData = codeMatrix[node.getX()][node.getY()]; |
| | | // node.setCodeData(nextNodeCodeData); |
| | | // |
| | | // Route route = routeService.findByCodeOfBoth( |
| | | // codeService.selectByData(currentNodeCodeData).getId(), |
| | | // codeService.selectByData(nextNodeCodeData).getId() |
| | | // ); |
| | | // if (null == route) { |
| | | // return false; |
| | | // } |
| | | |
| | | return true; |
| | | } |
| | | |
| | | // private boolean is_exist(NavigateNode node, List<NavigateNode> existNodes) { |
| | | // for (NavigateNode exist_node : existNodes) { |
| | | // if (node.getX() == exist_node.getX() && node.getY() == exist_node.getY()) { |
| | | // return true; |
| | | // } |
| | | // } |
| | | // return false; |
| | | // } |
| | | |
| | | private boolean isExist(NavigateNode node, List<NavigateNode> existNodes) { |
| | | for (NavigateNode existNode : existNodes) { |
| | |
| | | |
| | | //计算通过现在的结点的位置和最终结点的位置计算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())); |
| | | return Math.abs(node2.getX() - node1.getX()) + Math.abs(node2.getY() - node1.getY()); |
| | | } |
| | | |
| | |
| | | || nextNode.getX() == currNode.getParent().getX() |
| | | || nextNode.getY() == currNode.getParent().getY() |
| | | ) { |
| | | return 1; |
| | | return 0; |
| | | } |
| | | |
| | | // 拐向终点的点 |
| | | if (nextNode.getX() == endNode.getX() || nextNode.getY() == endNode.getY()) { |
| | | return 2; |
| | | return 1; |
| | | } |
| | | |
| | | // 普通拐点 |
| | | /* |
| | | 拐点判断逻辑 |
| | | 拿到父节点和下一节点 |
| | | 通过判断父节点和下一节点的x数据和y数据都不相同时,则表明当前坐标是一个拐点 |
| | | */ |
| | | return 3; |
| | | return 1; |
| | | } |
| | | |
| | | } |