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.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.*;
|
|
/**
|
* Created by vincent on 6/12/2024
|
*/
|
@Service
|
public class AStarNavigateService {
|
|
private final RedisSupport redis = RedisSupport.defaultRedisSupport;
|
|
public static final boolean OPEN_TURN_COST_WEIGHT = Boolean.FALSE;
|
|
public static final int WEIGHT_CALC_FACTOR = 1;
|
|
@Autowired
|
private CodeService codeService;
|
@Autowired
|
private RouteService routeService;
|
@Autowired
|
private MapDataDispatcher mapDataDispatcher;
|
@Autowired
|
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, 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<>();
|
|
openQueue.add(start);
|
existNodes.add(start);
|
|
int[][] mapMatrix = mapDataDispatcher.getMapMatrix(null, null);
|
DynamicNode[][] dynamicMatrix = mapDataDispatcher.getDynamicMatrix(null);
|
String[][] waveMatrix = mapDataDispatcher.getWaveMatrix(null);
|
|
while (openQueue.size() > 0) {
|
// 取优先队列顶部元素并且把这个元素从Open表中删除,取F值最小的节点
|
NavigateNode currentNode = openQueue.poll();
|
|
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()];
|
String vehicle = dynamicNode.getVehicle();
|
assert !vehicle.equals(DynamicNodeType.BLOCK.val);
|
if (!vehicle.equals(DynamicNodeType.ACCESS.val)) {
|
if (!vehicle.equals(agvNo)) {
|
|
// vehicle已经为当前segment做过了避让,且避让任务已完成,则权重值增加
|
if (null != segment) {
|
if (!Cools.isEmpty(jamService.getJamFromSegmentByAvo(segment, vehicle))) {
|
weight += WEIGHT_CALC_FACTOR;
|
}
|
}
|
|
if (lock) {
|
continue;
|
}
|
}
|
}
|
|
// 避障波
|
String waveNode = waveMatrix[node.getX()][node.getY()];
|
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)) {
|
|
if (lock) {
|
continue;
|
}
|
}
|
}
|
|
// 单巷道车辆容载数量
|
Lane lane = laneService.search(node.getCodeData());
|
if (null != lane) {
|
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);
|
}
|
}
|
}
|
|
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) * (OPEN_TURN_COST_WEIGHT ? calcNodeTurnCost(currentNode, node, end) : 1);
|
|
//进行计算对 G, F, H 等值
|
node.setWeight(weight);
|
node.setLastDistance(gCost);
|
node.initNode(currentNode, end);
|
node.setH(calcNodeCost(node, end));
|
node.setF(node.getG() + node.getH());
|
|
openQueue.add(node);
|
existNodes.add(node);
|
}
|
}
|
//如果遍历完所有出现的结点都没有找到最终的结点,返回null
|
return null;
|
}
|
|
// 获取四周节点
|
private ArrayList<NavigateNode> getNeighborNodes(NavigateNode currentNode, int[][] mapMatrix, List<NavigateNode> existNodes) {
|
//获取当前结点的x, y
|
int x = currentNode.getX();
|
int y = currentNode.getY();
|
//如果当前结点的邻结点合法,就加入到neighbour_node
|
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)) {
|
neighbourNodes.add(rightNode);
|
}
|
|
NavigateNode leftNode = extendNeighborNodes(currentNode, new NavigateNode(x, y - 1), mapMatrix, existNodes, null, null);
|
if (is_valid(currentNode, leftNode, mapMatrix, existNodes)) {
|
neighbourNodes.add(leftNode);
|
}
|
|
NavigateNode topNode = extendNeighborNodes(currentNode, new NavigateNode(x - 1, y), mapMatrix, existNodes, null, null);
|
if (is_valid(currentNode, topNode, mapMatrix, existNodes)) {
|
neighbourNodes.add(topNode);
|
}
|
|
NavigateNode bottomNode = extendNeighborNodes(currentNode, new NavigateNode(x + 1, y), mapMatrix, existNodes, null, null);
|
if (is_valid(currentNode, bottomNode, mapMatrix, existNodes)) {
|
neighbourNodes.add(bottomNode);
|
}
|
|
return neighbourNodes;
|
}
|
|
private NavigateNode extendNeighborNodes(NavigateNode currentNode, NavigateNode extendNode, int[][] mapMatrix, List<NavigateNode> existNodes, Integer dx, Integer dy) {
|
NavigateNode nextNode = null;
|
|
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);
|
}
|
|
int x = nextNode.getX();
|
int y = nextNode.getY();
|
// 数组越界
|
if (x < 0 || x >= mapMatrix.length
|
|| y < 0 || y >= mapMatrix[0].length) {
|
return null;
|
}
|
|
if (mapMatrix[x][y] == MapNodeType.DISABLE.val) {
|
|
return extendNeighborNodes(currentNode, nextNode, mapMatrix, existNodes, dx, dy);
|
|
} else {
|
|
if (isExist(nextNode, existNodes)) {
|
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);
|
|
// 判断通过性
|
Route route = routeService.findByCodeOfBoth(
|
codeService.selectByData(currentNodeCodeData).getId(),
|
codeService.selectByData(nextNodeCodeData).getId()
|
);
|
if (null == route) {
|
return null;
|
}
|
|
return nextNode;
|
}
|
}
|
|
private boolean is_valid(NavigateNode currentNode, NavigateNode node, int[][] mapMatrix, List<NavigateNode> existNodes) {
|
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) {
|
if (this.isSame(node, existNode)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private boolean isSame(NavigateNode o1, NavigateNode o2) {
|
if (Cools.isEmpty(o1, o2)) {
|
return false;
|
}
|
return o1.getX() == o2.getX() && o1.getY() == o2.getY();
|
}
|
|
//------------------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()));
|
|
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 1;
|
}
|
|
// 拐向终点的点
|
if (nextNode.getX() == endNode.getX() || nextNode.getY() == endNode.getY()) {
|
return 2;
|
}
|
|
// 普通拐点
|
/*
|
拐点判断逻辑
|
拿到父节点和下一节点
|
通过判断父节点和下一节点的x数据和y数据都不相同时,则表明当前坐标是一个拐点
|
*/
|
return 3;
|
}
|
|
}
|