小车调度
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
package com.ruoyi.common.domain;
|
||||
|
||||
|
||||
|
||||
public class SendData2 {
|
||||
|
||||
|
||||
private String address;
|
||||
|
||||
private String data ;
|
||||
|
||||
private int type;//1单字 2双字 3字符串
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
@@ -102,6 +102,10 @@ public class MsgDdj extends BaseEntity
|
||||
|
||||
private String plcDbReadAddress;
|
||||
|
||||
private String ip;
|
||||
|
||||
private String port;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class PlcDdjRead implements Runnable {
|
||||
public static ConcurrentHashMap<String, MsgDdj> deviceDdjMap;
|
||||
public volatile boolean isRun = true;
|
||||
|
||||
public MsgDdj msgDdj;
|
||||
public SiemensS7Net siemensS7Net = null;
|
||||
public static IReadWriteNet readWriteNet = null;
|
||||
private final String ip;
|
||||
private final int port;
|
||||
private boolean success = false;
|
||||
|
||||
public PlcDdjRead(ConcurrentHashMap<String, MsgDdj> deviceDdjMap, PlcConnectInfo plcConnectInfo) {
|
||||
PlcDdjRead.deviceDdjMap = deviceDdjMap;
|
||||
public PlcDdjRead(MsgDdj msgDdj, PlcConnectInfo plcConnectInfo) {
|
||||
this.msgDdj = msgDdj;
|
||||
this.ip = plcConnectInfo.getIp();
|
||||
this.port = plcConnectInfo.getPort();
|
||||
}
|
||||
@@ -46,12 +46,7 @@ public class PlcDdjRead implements Runnable {
|
||||
while (isRun) {
|
||||
try {
|
||||
if (IsConnect()) {
|
||||
for (String key : deviceDdjMap.keySet()) {
|
||||
MsgDdj msgDdj = deviceDdjMap.get(key);
|
||||
if (msgDdj != null) {
|
||||
readPlcDdj(msgDdj);
|
||||
}
|
||||
}
|
||||
readPlcDdj(msgDdj);
|
||||
Thread.sleep(1000);
|
||||
} else {
|
||||
reConnect();
|
||||
|
||||
@@ -18,7 +18,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
public class PlcSrgvRead implements Runnable {
|
||||
|
||||
public static ConcurrentHashMap<String, DeviceSrgv> deviceSrgvMap;
|
||||
private ModbusTcpNet modbusTcpNet;
|
||||
private static ModbusTcpNet modbusTcpNet;
|
||||
public static IReadWriteNet readWriteNet = null;
|
||||
public volatile boolean isRun = true;
|
||||
private final String ip;
|
||||
@@ -36,12 +36,18 @@ public class PlcSrgvRead implements Runnable {
|
||||
this.station = (byte) 1;
|
||||
}
|
||||
|
||||
public static int WriteToSSJ(String address, byte[] data) {
|
||||
int a = 0;
|
||||
if (readWriteNet != null) {
|
||||
a = HslRead.WriteResultRender(readWriteNet.Write(address, data), address);
|
||||
}
|
||||
return a;
|
||||
public static boolean Write(String address, int[] value){
|
||||
|
||||
OperateResult write = modbusTcpNet.Write(address, value);
|
||||
return write.IsSuccess;
|
||||
|
||||
}
|
||||
|
||||
public static boolean Write(String address, short[] value){
|
||||
|
||||
OperateResult write = modbusTcpNet.Write(address, value);
|
||||
return write.IsSuccess;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -191,4 +197,6 @@ public class PlcSrgvRead implements Runnable {
|
||||
}
|
||||
setIsConnect(false);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.ruoyi.schedule.plcRead.srgv.roadUtil;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public class MapImageGenerator {
|
||||
|
||||
// 定义颜色常量
|
||||
private static final Color PASSABLE_COLOR = Color.GREEN;
|
||||
private static final Color PUBLIC_PATH_COLOR = Color.YELLOW;
|
||||
private static final Color OBSTACLE_COLOR = Color.BLACK;
|
||||
private static final Color PATH_COLOR = Color.RED;
|
||||
private static final Color INFLECTION_POINT_COLOR = Color.BLUE;
|
||||
|
||||
// 每个单元格的大小(像素)
|
||||
private static final int CELL_SIZE = 10;
|
||||
|
||||
/**
|
||||
* 根据二维数组生成地图图片,并标记路径和拐点
|
||||
*
|
||||
* @param grid 二维数组表示的地图
|
||||
* @param path 路径坐标列表
|
||||
* @param inflections 拐点坐标列表
|
||||
* @param outputFilePath 输出图片文件路径
|
||||
*/
|
||||
public static void generateMapImage(int[][] grid, List<int[]> path, List<int[]> inflections, String outputFilePath) {
|
||||
int rows = grid.length;
|
||||
int cols = grid[0].length;
|
||||
|
||||
int width = cols * CELL_SIZE;
|
||||
int height = rows * CELL_SIZE;
|
||||
|
||||
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics2D g = image.createGraphics();
|
||||
|
||||
// 填充背景
|
||||
g.setColor(Color.WHITE);
|
||||
g.fillRect(0, 0, width, height);
|
||||
|
||||
// 绘制地图
|
||||
for (int i = 0; i < rows; i++) {
|
||||
for (int j = 0; j < cols; j++) {
|
||||
int value = grid[i][j];
|
||||
Color color;
|
||||
|
||||
if (value == 1) {
|
||||
color = PASSABLE_COLOR;
|
||||
} else if (value == 2) {
|
||||
color = PUBLIC_PATH_COLOR;
|
||||
} else {
|
||||
color = OBSTACLE_COLOR;
|
||||
}
|
||||
|
||||
g.setColor(color);
|
||||
g.fillRect(j * CELL_SIZE, i * CELL_SIZE, CELL_SIZE, CELL_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制路径
|
||||
if (path != null && !path.isEmpty()) {
|
||||
g.setColor(PATH_COLOR);
|
||||
for (int[] coord : path) {
|
||||
int x = coord[1];
|
||||
int y = coord[0];
|
||||
g.fillRect(x * CELL_SIZE + 2, y * CELL_SIZE + 2, CELL_SIZE - 4, CELL_SIZE - 4);
|
||||
}
|
||||
}
|
||||
|
||||
// 绘制拐点
|
||||
if (inflections != null && !inflections.isEmpty()) {
|
||||
g.setColor(INFLECTION_POINT_COLOR);
|
||||
for (int[] coord : inflections) {
|
||||
int x = coord[1];
|
||||
int y = coord[0];
|
||||
g.fillOval(x * CELL_SIZE + 2, y * CELL_SIZE + 2, CELL_SIZE - 4, CELL_SIZE - 4);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存图片
|
||||
try {
|
||||
ImageIO.write(image, "png", new File(outputFilePath));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
g.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package com.ruoyi.schedule.plcRead.srgv.roadUtil;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MatrixModifier {
|
||||
|
||||
/**
|
||||
* 返回一个修改后的新二维数组,不对原数组进行修改
|
||||
*
|
||||
* @param matrix 原始二维数组
|
||||
* @param row 指定行索引
|
||||
* @param col 指定列索引
|
||||
* @return 新的修改后的二维数组
|
||||
*/
|
||||
public static int[][] modifyColumnNeighbors(int[][] matrix, int row, int col) {
|
||||
return modifySinglePoint(matrix, row, col);
|
||||
}
|
||||
|
||||
/**
|
||||
* 私有方法:对单个点执行上下列的修改(返回新数组)
|
||||
*/
|
||||
private static int[][] modifySinglePoint(int[][] matrix, int row, int col) {
|
||||
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
|
||||
return new int[0][0];
|
||||
}
|
||||
|
||||
// 深拷贝原数组
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
int[][] newMatrix = new int[rows][cols];
|
||||
for (int i = 0; i < rows; i++) {
|
||||
System.arraycopy(matrix[i], 0, newMatrix[i], 0, cols);
|
||||
}
|
||||
|
||||
// 向上修改
|
||||
for (int i = row - 1; i >= 0; i--) {
|
||||
if (newMatrix[i][col] == 1) {
|
||||
newMatrix[i][col] = 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 向下修改
|
||||
for (int i = row + 1; i < rows; i++) {
|
||||
if (newMatrix[i][col] == 1) {
|
||||
newMatrix[i][col] = 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newMatrix[row][col] = 2;
|
||||
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增方法:对两个点分别执行列方向上的修改
|
||||
*/
|
||||
public static int[][] modifyColumnNeighbors(int[][] matrix, int startX, int startY, int endX, int endY) {
|
||||
// 先对第一个点修改
|
||||
int[][] modified = modifySinglePoint(matrix, startX, startY);
|
||||
// 再对第二个点修改
|
||||
return modifySinglePoint(modified, endX, endY);
|
||||
}
|
||||
|
||||
//对多个站点执行更新
|
||||
public static void modifyColumnNeighbors(int[][] matrix, List<int[]> points,int value) {
|
||||
|
||||
if(points == null){
|
||||
return;
|
||||
}
|
||||
|
||||
for (int[] point : points) {
|
||||
matrix[point[0]][point[1]] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//拷贝原数组
|
||||
public static int[][] copyMatrix(int[][] matrix) {
|
||||
//拷贝原数组
|
||||
int rows = matrix.length;
|
||||
int cols = matrix[0].length;
|
||||
int[][] newMatrix = new int[rows][cols];
|
||||
return newMatrix;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,901 @@
|
||||
package com.ruoyi.schedule.plcRead.srgv.roadUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PathFinder {
|
||||
|
||||
private static class PathInfo {
|
||||
List<int[]> path;
|
||||
int inflectionCount;
|
||||
|
||||
PathInfo(List<int[]> path, int inflectionCount) {
|
||||
this.path = path;
|
||||
this.inflectionCount = inflectionCount;
|
||||
}
|
||||
}
|
||||
|
||||
private static class Node implements Comparable<Node> {
|
||||
int x, y, distance;
|
||||
|
||||
Node(int x, int y, int distance) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Node other) {
|
||||
return Integer.compare(this.distance, other.distance);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 Dijkstra 算法查找从起点到终点的最短路径。
|
||||
*
|
||||
* @param grid 二维数组表示的网格地图
|
||||
* @param startX 起点 X 坐标 (用户视角从1开始)
|
||||
* @param startY 起点 Y 坐标 (用户视角从1开始)
|
||||
* @param endX 终点 X 坐标 (用户视角从1开始)
|
||||
* @param endY 终点 Y 坐标 (用户视角从1开始)
|
||||
* @return 包含路径坐标的 int 数组列表,若无路径则返回 null
|
||||
*/
|
||||
public static List<int[]> findShortestPath2(int[][] grid, int startX, int startY, int endX, int endY) {
|
||||
int rows = grid.length;
|
||||
int cols = grid[0].length;
|
||||
int[][] distances = new int[rows][cols];
|
||||
int[][][] prev = new int[rows][cols][2];
|
||||
int[][][] arr = new int[rows][cols][2];
|
||||
PriorityQueue<Node> pq = new PriorityQueue<>();
|
||||
|
||||
for (int i = 0; i < rows; i++) {
|
||||
Arrays.fill(distances[i], Integer.MAX_VALUE);
|
||||
}
|
||||
distances[startX][startY] = 0;
|
||||
pq.add(new Node(startX, startY, 0));
|
||||
|
||||
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
|
||||
|
||||
while (!pq.isEmpty()) {
|
||||
Node current = pq.poll();
|
||||
int x = current.x;
|
||||
int y = current.y;
|
||||
|
||||
if (x == endX && y == endY) break;
|
||||
if (current.distance > distances[x][y]) continue;
|
||||
|
||||
for (int[] dir : dirs) {
|
||||
int nx = x + dir[0];
|
||||
int ny = y + dir[1];
|
||||
|
||||
if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
|
||||
int cellValue = grid[nx][ny];
|
||||
if (cellValue == 1 || cellValue == 2) {
|
||||
int newDist = current.distance + 1;
|
||||
if (newDist < distances[nx][ny]) {
|
||||
distances[nx][ny] = newDist;
|
||||
prev[nx][ny] = new int[]{x, y};
|
||||
pq.add(new Node(nx, ny, newDist));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<int[]> path = new ArrayList<>();
|
||||
int currX = endX, currY = endY;
|
||||
while (currX != startX || currY != startY) {
|
||||
path.add(new int[]{currX, currY});
|
||||
int[] p = prev[currX][currY];
|
||||
if (p[0] == 0 && p[1] == 0 && (currX != startX || currY != startY)) {
|
||||
return null;
|
||||
}
|
||||
currX = p[0];
|
||||
currY = p[1];
|
||||
}
|
||||
path.add(new int[]{startX, startY});
|
||||
Collections.reverse(path);
|
||||
|
||||
|
||||
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 使用 A* 算法查找从起点到终点的最短路径。
|
||||
*
|
||||
* @param grid 二维数组表示的网格地图
|
||||
* @param startX 起点 X 坐标
|
||||
* @param startY 起点 Y 坐标
|
||||
* @param endX 终点 X 坐标
|
||||
* @param endY 终点 Y 坐标
|
||||
* @return 包含路径坐标的 int 数组列表,若无路径则返回 null
|
||||
*/
|
||||
public static List<int[]> findShortestPath(int[][] grid, int startX, int startY, int endX, int endY) {
|
||||
int rows = grid.length;
|
||||
int cols = grid[0].length;
|
||||
|
||||
// gCost: 从起点到当前节点的实际成本
|
||||
int[][] gCost = new int[rows][cols];
|
||||
// fCost: gCost + 启发式估计成本(从当前节点到终点)
|
||||
int[][] fCost = new int[rows][cols];
|
||||
// prev: 记录路径回溯信息
|
||||
int[][][] prev = new int[rows][cols][2];
|
||||
// closedSet: 记录已访问的节点
|
||||
boolean[][] closedSet = new boolean[rows][cols];
|
||||
|
||||
// 初始化成本数组
|
||||
for (int i = 0; i < rows; i++) {
|
||||
Arrays.fill(gCost[i], Integer.MAX_VALUE);
|
||||
Arrays.fill(fCost[i], Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
// 优先队列,按fCost排序
|
||||
PriorityQueue<AStarNode> openSet = new PriorityQueue<>();
|
||||
|
||||
// 初始化起点
|
||||
gCost[startX][startY] = 0;
|
||||
fCost[startX][startY] = heuristic(startX, startY, endX, endY);
|
||||
openSet.add(new AStarNode(startX, startY, gCost[startX][startY], fCost[startX][startY]));
|
||||
|
||||
// 四个方向:上、下、左、右
|
||||
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
|
||||
|
||||
while (!openSet.isEmpty()) {
|
||||
// 取出fCost最小的节点
|
||||
AStarNode current = openSet.poll();
|
||||
int x = current.x;
|
||||
int y = current.y;
|
||||
|
||||
// 如果到达终点,结束搜索
|
||||
if (x == endX && y == endY) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 如果已经处理过该节点,跳过
|
||||
if (closedSet[x][y]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 标记为已处理
|
||||
closedSet[x][y] = true;
|
||||
|
||||
// 检查四个方向的邻居
|
||||
for (int[] dir : dirs) {
|
||||
int nx = x + dir[0];
|
||||
int ny = y + dir[1];
|
||||
|
||||
// 检查边界
|
||||
if (nx >= 0 && nx < rows && ny >= 0 && ny < cols) {
|
||||
// 检查是否可通行(值为1或2)且未处理过
|
||||
int cellValue = grid[nx][ny];
|
||||
if ((cellValue == 1 || cellValue == 2) && !closedSet[nx][ny]) {
|
||||
// 计算新的gCost(从起点到邻居节点的成本)
|
||||
int tentativeGCost = gCost[x][y] + 1;
|
||||
|
||||
// 如果新的路径更优
|
||||
if (tentativeGCost < gCost[nx][ny]) {
|
||||
// 更新路径信息
|
||||
prev[nx][ny] = new int[]{x, y};
|
||||
gCost[nx][ny] = tentativeGCost;
|
||||
fCost[nx][ny] = tentativeGCost + heuristic(nx, ny, endX, endY);
|
||||
|
||||
// 添加到开放集合
|
||||
openSet.add(new AStarNode(nx, ny, gCost[nx][ny], fCost[nx][ny]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果终点不可达
|
||||
if (gCost[endX][endY] == Integer.MAX_VALUE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 回溯路径
|
||||
List<int[]> path = new ArrayList<>();
|
||||
int currX = endX, currY = endY;
|
||||
|
||||
// 从终点回溯到起点
|
||||
while (currX != startX || currY != startY) {
|
||||
path.add(new int[]{currX, currY});
|
||||
int[] p = prev[currX][currY];
|
||||
|
||||
// 检查路径有效性
|
||||
if (p[0] == 0 && p[1] == 0 && (currX != startX || currY != startY)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
currX = p[0];
|
||||
currY = p[1];
|
||||
}
|
||||
|
||||
// 添加起点
|
||||
path.add(new int[]{startX, startY});
|
||||
|
||||
// 反转路径,使其从起点到终点
|
||||
Collections.reverse(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* A*算法节点类
|
||||
*/
|
||||
private static class AStarNode implements Comparable<AStarNode> {
|
||||
int x, y; // 节点坐标
|
||||
int gCost; // 从起点到当前节点的实际成本
|
||||
int fCost; // 总成本(fCost = gCost + hCost)
|
||||
|
||||
AStarNode(int x, int y, int gCost, int fCost) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.gCost = gCost;
|
||||
this.fCost = fCost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(AStarNode other) {
|
||||
// 首先按fCost排序,如果fCost相同则按gCost排序
|
||||
if (this.fCost != other.fCost) {
|
||||
return Integer.compare(this.fCost, other.fCost);
|
||||
}
|
||||
return Integer.compare(other.gCost, this.gCost); // gCost较大的优先(更接近终点)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启发式函数:使用曼哈顿距离
|
||||
*
|
||||
* @param x1 起点x坐标
|
||||
* @param y1 起点y坐标
|
||||
* @param x2 终点x坐标
|
||||
* @param y2 终点y坐标
|
||||
* @return 估计距离
|
||||
*/
|
||||
private static int heuristic(int x1, int y1, int x2, int y2) {
|
||||
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 提取路径中的拐点(1→2 或 2→1 的边界点)
|
||||
*
|
||||
* @param path 路径坐标列表(逻辑坐标)
|
||||
* @param grid 网格数据
|
||||
* @return 拐点坐标列表(逻辑坐标)
|
||||
*/
|
||||
public static List<int[]> extractInflectionPoints(List<int[]> path, int[][] grid) {
|
||||
|
||||
|
||||
//
|
||||
|
||||
// 识别并输出拐点坐标
|
||||
List<int[]> inflectionPoints = new ArrayList<>();
|
||||
for (int i = 0; i < path.size() - 1; i++) {
|
||||
int[] current = path.get(i);
|
||||
int[] next = path.get(i + 1);
|
||||
int currentValue = grid[current[0]][current[1]];
|
||||
int nextValue = grid[next[0]][next[1]];
|
||||
|
||||
// 检测1→2或2→1的转换,记录2所在的坐标
|
||||
if ((currentValue == 1 && nextValue == 2) || (currentValue == 2 && nextValue == 1)) {
|
||||
if (currentValue == 2) {
|
||||
inflectionPoints.add(current);
|
||||
} else {
|
||||
inflectionPoints.add(next);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//inflectionPoints去重
|
||||
List<int[]> collect = inflectionPoints.stream().distinct().collect(Collectors.toList());
|
||||
|
||||
|
||||
|
||||
return collect;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 使用 BFS 查找从起点开始,路径上所有点的值为 2,直到终点(值为 1 或 2)的最短路径。
|
||||
*
|
||||
* @param grid 二维数组表示的网格地图
|
||||
* @param startX 起点 X 坐标 (用户视角从1开始)
|
||||
* @param startY 起点 Y 坐标 (用户视角从1开始)
|
||||
* @param endX 终点 X 坐标 (用户视角从1开始)
|
||||
* @param endY 终点 Y 坐标 (用户视角从1开始)
|
||||
* @return 包含路径坐标的 int 数组列表,若无路径则返回 null
|
||||
*/
|
||||
public static List<int[]> findShortestPathFrom2To2Or1(int[][] grid, int startX, int startY, int endX, int endY) {
|
||||
|
||||
grid = MatrixModifier.modifyColumnNeighbors(grid, startX, startY, endX, endY);
|
||||
|
||||
|
||||
int rows = grid.length;
|
||||
int cols = grid[0].length;
|
||||
|
||||
if (grid[startX][startY] != 2) {
|
||||
return null; // 起点不是 2,直接失败
|
||||
}
|
||||
|
||||
boolean[][] visited = new boolean[rows][cols];
|
||||
int[][] prev = new int[rows * cols][2]; // 记录路径回溯信息
|
||||
|
||||
Queue<int[]> queue = new LinkedList<>();
|
||||
queue.offer(new int[]{startX, startY});
|
||||
visited[startX][startY] = true;
|
||||
|
||||
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右方向
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
int[] current = queue.poll();
|
||||
int x = current[0];
|
||||
int y = current[1];
|
||||
|
||||
if (x == endX && y == endY) {
|
||||
break; // 找到终点
|
||||
}
|
||||
|
||||
for (int[] dir : dirs) {
|
||||
int nx = x + dir[0];
|
||||
int ny = y + dir[1];
|
||||
|
||||
if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && !visited[nx][ny]) {
|
||||
if (grid[nx][ny] == 2 || (nx == endX && ny == endY && (grid[nx][ny] == 1 || grid[nx][ny] == 2))) {
|
||||
visited[nx][ny] = true;
|
||||
prev[nx * cols + ny] = new int[]{x, y};
|
||||
queue.offer(new int[]{nx, ny});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!visited[endX][endY]) {
|
||||
return null; // 终点不可达
|
||||
}
|
||||
|
||||
// 回溯路径
|
||||
List<int[]> path = new ArrayList<>();
|
||||
int currX = endX, currY = endY;
|
||||
while (currX != startX || currY != startY) {
|
||||
path.add(new int[]{currX, currY});
|
||||
int[] parent = prev[currX * cols + currY];
|
||||
currX = parent[0];
|
||||
currY = parent[1];
|
||||
}
|
||||
path.add(new int[]{startX, startY});
|
||||
Collections.reverse(path);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 提取路径中的拐点:
|
||||
* - 区域类型转换点(1 ↔ 2)
|
||||
* - 方向变化点(水平转垂直 / 垂直转水平)
|
||||
*
|
||||
* @param path 路径坐标列表(逻辑坐标)
|
||||
* @param grid 网格数据
|
||||
* @return 拐点坐标列表(逻辑坐标)
|
||||
*/
|
||||
public static List<int[]> extractInflectionPoints2(List<int[]> path, int[][] grid) {
|
||||
List<int[]> inflectionPoints = new ArrayList<>();
|
||||
|
||||
// 记录类型转换点和方向变化点
|
||||
Set<String> seen = new HashSet<>();
|
||||
|
||||
// 检测类型转换点(1 ↔ 2)
|
||||
/*for (int i = 0; i < path.size() - 1; i++) {
|
||||
int[] current = path.get(i);
|
||||
int[] next = path.get(i + 1);
|
||||
int currentValue = grid[current[0]][current[1]];
|
||||
int nextValue = grid[next[0]][next[1]];
|
||||
|
||||
if ((currentValue == 1 && nextValue == 2) || (currentValue == 2 && nextValue == 1)) {
|
||||
int[] point = currentValue == 2 ? current : next;
|
||||
String key = point[0] + "," + point[1];
|
||||
if (!seen.contains(key)) {
|
||||
seen.add(key);
|
||||
inflectionPoints.add(point);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// 检测方向变化点(连续三点的方向变化)
|
||||
for (int i = 1; i < path.size() - 1; i++) {
|
||||
int[] prev = path.get(i - 1);
|
||||
int[] curr = path.get(i);
|
||||
int[] next = path.get(i + 1);
|
||||
|
||||
boolean horizontal1 = prev[0] == curr[0]; // 前一段是否水平
|
||||
boolean horizontal2 = curr[0] == next[0]; // 后一段是否水平
|
||||
|
||||
if (horizontal1 != horizontal2) { // 方向变化
|
||||
String key = curr[0] + "," + curr[1];
|
||||
if (!seen.contains(key)) {
|
||||
seen.add(key);
|
||||
inflectionPoints.add(curr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 排序后返回(可选)
|
||||
return inflectionPoints; /*.stream()
|
||||
.sorted(Comparator.comparingInt(a -> a[0] * 1000 + a[1]))
|
||||
.collect(Collectors.toList());*/
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* 同层多车选择最优
|
||||
* 排除车辆障碍点
|
||||
*
|
||||
* 空车运行
|
||||
*
|
||||
*
|
||||
* */
|
||||
public static Site findOptimalCarWithObstacles(
|
||||
int[][] grid, int endX, int endY, List<Site> cars) {
|
||||
// 创建包含车辆位置的障碍物地图
|
||||
int[][] obstacleGrid = Arrays.stream(grid).map(int[]::clone).toArray(int[][]::new);
|
||||
cars.forEach(car -> {
|
||||
int[] pos = car.getStartPoint();
|
||||
if (pos != null) obstacleGrid[pos[0]][pos[1]] = 0; // 标记为障碍
|
||||
});
|
||||
|
||||
// 复用现有路径查找逻辑
|
||||
return findOptimalCarForDestination(obstacleGrid, endX, endY, cars);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* 同层多车选择最优
|
||||
* 排除车辆障碍点
|
||||
*
|
||||
* 载货运行
|
||||
*
|
||||
*
|
||||
* */
|
||||
public static Site findOptimalCarWithObstaclesByFull(
|
||||
int[][] grid, int endX, int endY, List<Site> cars) {
|
||||
// 创建包含车辆位置的障碍物地图
|
||||
int[][] obstacleGrid = Arrays.stream(grid).map(int[]::clone).toArray(int[][]::new);
|
||||
cars.forEach(car -> {
|
||||
int[] pos = car.getStartPoint();
|
||||
if (pos != null) obstacleGrid[pos[0]][pos[1]] = 0; // 标记为障碍
|
||||
});
|
||||
|
||||
// 复用现有路径查找逻辑
|
||||
return findOptimalCarForDestinationByFull(obstacleGrid, endX, endY, cars);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 此方法使用情景
|
||||
* 一个原点(目标点)、多个目标点(原点),多个目标点(原点)互相影响,
|
||||
* 按算法查找多个目标点(原点)的最优路径
|
||||
*
|
||||
* */
|
||||
public static Site findOptimalCarWithObstacles(int[][] grid, Site srcSite, List<Site> tarSites){
|
||||
//所有车先都设置为障碍
|
||||
tarSites.forEach(site -> {
|
||||
int[] pos = site.getStartPoint();
|
||||
if (pos != null) grid[pos[0]][pos[1]] = 0; // 标记为障碍
|
||||
});
|
||||
|
||||
// 并行计算所有车辆的路径
|
||||
Map<Site, PathInfo> carPathInfo = tarSites.parallelStream()
|
||||
.filter(Objects::nonNull) // 过滤掉null元素
|
||||
.collect(Collectors.toMap(
|
||||
car -> car,
|
||||
car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
if (startPoint == null || startPoint.length < 2) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 计算路径
|
||||
List<int[]> path = findShortestPath(grid, startPoint[0], startPoint[1], srcSite.getStartPoint()[0], srcSite.getStartPoint()[1]);
|
||||
if (path == null) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 检查路径冲突
|
||||
boolean blocked = tarSites.stream()
|
||||
.filter(other -> other != car)
|
||||
.map(Site::getStartPoint)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(pos -> path.stream().anyMatch(p -> p[0] == pos[0] && p[1] == pos[1]));
|
||||
|
||||
if (blocked) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
// 计算拐点
|
||||
List<int[]> inflectionPoints = extractInflectionPoints2(path, grid);
|
||||
return new PathInfo(path, inflectionPoints.size());
|
||||
}
|
||||
));
|
||||
|
||||
// 过滤无效路径
|
||||
Map<Site, PathInfo> availableCars = carPathInfo.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// 选择最优车辆(路径最短且拐点最少)
|
||||
Site selectedCar = availableCars.entrySet().stream()
|
||||
.min(Comparator.comparingInt(e -> e.getValue().path.size() * 100 + e.getValue().inflectionCount))
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
|
||||
return selectedCar;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* 载货查找最佳,使用情景
|
||||
*
|
||||
* */
|
||||
public static Site findOptimalWithCargo(int[][] grid, Site srcSite, List<Site> tarSites){
|
||||
|
||||
// 并行计算所有车辆的路径
|
||||
Map<Site, PathInfo> carPathInfo = tarSites.parallelStream()
|
||||
.collect(Collectors.toMap(
|
||||
car -> car,
|
||||
car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
if (startPoint == null || startPoint.length < 2) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
// 计算路径
|
||||
List<int[]> path = findShortestPathFrom2To2Or1(grid, startPoint[0], startPoint[1], srcSite.getStartPoint()[0], srcSite.getStartPoint()[1]);
|
||||
if (path == null) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 检查路径冲突
|
||||
boolean blocked = tarSites.stream()
|
||||
.filter(other -> other != car)
|
||||
.map(Site::getStartPoint)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(pos -> path.stream().anyMatch(p -> p[0] == pos[0] && p[1] == pos[1]));
|
||||
|
||||
if (blocked) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
// 计算拐点
|
||||
List<int[]> inflectionPoints = extractInflectionPoints2(path, grid);
|
||||
return new PathInfo(path, inflectionPoints.size());
|
||||
}
|
||||
));
|
||||
|
||||
// 过滤无效路径
|
||||
Map<Site, PathInfo> availableCars = carPathInfo.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// 选择最优车辆(路径最短且拐点最少)
|
||||
Site selectedCar = availableCars.entrySet().stream()
|
||||
.min(Comparator.comparingInt(e -> e.getValue().path.size() * 100 + e.getValue().inflectionCount))
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
|
||||
return selectedCar;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* 空载查找最佳,使用情景
|
||||
* 小车不载货,一个原点(目标点),多个目标点(原点)
|
||||
*
|
||||
* */
|
||||
public static Site findOptimalWithNoCargo(int[][] grid, Site srcSite, List<Site> tarSites){
|
||||
|
||||
|
||||
// 并行计算所有车辆的路径
|
||||
Map<Site, PathInfo> carPathInfo = tarSites.parallelStream()
|
||||
.collect(Collectors.toMap(
|
||||
car -> car,
|
||||
car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
if (startPoint == null || startPoint.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 计算路径
|
||||
List<int[]> path = findShortestPath(grid, startPoint[0], startPoint[1], srcSite.getStartPoint()[0], srcSite.getStartPoint()[1]);
|
||||
if (path == null) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 检查路径冲突
|
||||
boolean blocked = tarSites.stream()
|
||||
.filter(other -> other != car)
|
||||
.map(Site::getStartPoint)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(pos -> path.stream().anyMatch(p -> p[0] == pos[0] && p[1] == pos[1]));
|
||||
|
||||
if (blocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算拐点
|
||||
List<int[]> inflectionPoints = extractInflectionPoints2(path, grid);
|
||||
return new PathInfo(path, inflectionPoints.size());
|
||||
}
|
||||
));
|
||||
|
||||
// 过滤无效路径
|
||||
Map<Site, PathInfo> availableCars = carPathInfo.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// 选择最优车辆(路径最短且拐点最少)
|
||||
Site selectedCar = availableCars.entrySet().stream()
|
||||
.min(Comparator.comparingInt(e -> e.getValue().path.size() * 100 + e.getValue().inflectionCount))
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
|
||||
return selectedCar;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
* 同层多车选择最优
|
||||
* */
|
||||
/*
|
||||
*
|
||||
* 同层多车选择最优
|
||||
* */
|
||||
/*
|
||||
*
|
||||
* 同层多车选择最优
|
||||
* */
|
||||
public static Site findOptimalCarForDestination(
|
||||
int[][] grid, int endX, int endY, List<Site> cars) {
|
||||
// 创建网格副本
|
||||
int[][] tempGrid = Arrays.stream(grid).map(int[]::clone).toArray(int[][]::new);
|
||||
|
||||
// 并行计算所有车辆的路径
|
||||
Map<Site, PathInfo> carPathInfo = cars.parallelStream()
|
||||
.filter(car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
return startPoint != null && startPoint.length >= 2;
|
||||
}) // 过滤掉无效的车辆
|
||||
.collect(Collectors.toMap(
|
||||
car -> car,
|
||||
car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
// 注意:这里可以移除重复的 null 检查,因为已经在 filter 中处理过了
|
||||
|
||||
|
||||
// 计算路径
|
||||
List<int[]> path = findShortestPath(tempGrid, startPoint[0], startPoint[1], endX, endY);
|
||||
if (path == null) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE); // 返回特殊标记值而不是null
|
||||
}
|
||||
|
||||
|
||||
// 检查路径冲突
|
||||
boolean blocked = cars.stream()
|
||||
.filter(other -> other != car)
|
||||
.map(Site::getStartPoint)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(pos -> path.stream().anyMatch(p -> p[0] == pos[0] && p[1] == pos[1]));
|
||||
|
||||
if (blocked) {
|
||||
return new PathInfo(new ArrayList<>(), Integer.MAX_VALUE); // 返回特殊标记值而不是null
|
||||
}
|
||||
|
||||
// 计算拐点
|
||||
List<int[]> inflectionPoints = extractInflectionPoints2(path, tempGrid);
|
||||
return new PathInfo(path, inflectionPoints.size());
|
||||
},
|
||||
(existing, replacement) -> existing // 合并函数:如果键冲突,保留现有的值
|
||||
));
|
||||
|
||||
// 过滤无效路径(检查特殊标记值)
|
||||
Map<Site, PathInfo> availableCars = carPathInfo.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null &&
|
||||
entry.getValue().inflectionCount != Integer.MAX_VALUE)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// 选择最优车辆(路径最短且拐点最少)
|
||||
Site selectedCar = availableCars.entrySet().stream()
|
||||
.min(Comparator.comparingInt(e -> e.getValue().path.size() * 100 + e.getValue().inflectionCount))
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
|
||||
// 为所有车辆生成路径图片
|
||||
|
||||
return selectedCar;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static Site findOptimalCarForDestinationByFull(
|
||||
int[][] grid, int endX, int endY, List<Site> cars) {
|
||||
// 创建网格副本
|
||||
int[][] tempGrid = Arrays.stream(grid).map(int[]::clone).toArray(int[][]::new);
|
||||
|
||||
// 并行计算所有车辆的路径
|
||||
Map<Site, PathInfo> carPathInfo = cars.parallelStream()
|
||||
.collect(Collectors.toMap(
|
||||
car -> car,
|
||||
car -> {
|
||||
int[] startPoint = car.getStartPoint();
|
||||
if (startPoint == null || startPoint.length < 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算路径
|
||||
List<int[]> path = findShortestPathFrom2To2Or1(tempGrid, startPoint[0], startPoint[1], endX, endY);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// 检查路径冲突
|
||||
boolean blocked = cars.stream()
|
||||
.filter(other -> other != car)
|
||||
.map(Site::getStartPoint)
|
||||
.filter(Objects::nonNull)
|
||||
.anyMatch(pos -> path.stream().anyMatch(p -> p[0] == pos[0] && p[1] == pos[1]));
|
||||
|
||||
if (blocked) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 计算拐点
|
||||
List<int[]> inflectionPoints = extractInflectionPoints2(path, tempGrid);
|
||||
return new PathInfo(path, inflectionPoints.size());
|
||||
}
|
||||
));
|
||||
|
||||
// 过滤无效路径
|
||||
Map<Site, PathInfo> availableCars = carPathInfo.entrySet().stream()
|
||||
.filter(entry -> entry.getValue() != null)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
// 选择最优车辆(路径最短且拐点最少)
|
||||
Site selectedCar = availableCars.entrySet().stream()
|
||||
.min(Comparator.comparingInt(e -> e.getValue().path.size() * 100 + e.getValue().inflectionCount))
|
||||
.map(Map.Entry::getKey)
|
||||
.orElse(null);
|
||||
|
||||
// 为所有车辆生成路径图片
|
||||
|
||||
return selectedCar;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 自动避让路径规划:当B车阻挡A车路径时,自动为B车规划新路径
|
||||
*/
|
||||
public static Site findOptimalCarWithAutoAvoidance(
|
||||
int[][] grid, int endX, int endY, List<Site> cars) {
|
||||
|
||||
// 1. 首先尝试常规路径规划
|
||||
Site selectedCar = findOptimalCarForDestination(grid, endX, endY, cars);
|
||||
if (selectedCar != null) {
|
||||
return selectedCar; // 如果有车可以直接到达,直接返回
|
||||
}
|
||||
|
||||
// 2. 检查是否有车辆被其他车辆阻挡
|
||||
for (Site car : cars) {
|
||||
int[] start = car.getStartPoint();
|
||||
List<int[]> path = findShortestPath(grid, start[0], start[1], endX, endY);
|
||||
|
||||
if (path != null) {
|
||||
// 检查路径是否被其他车辆阻挡
|
||||
for (Site otherCar : cars) {
|
||||
if (otherCar != car) {
|
||||
int[] otherPos = otherCar.getStartPoint();
|
||||
if (otherPos != null && path.stream().anyMatch(p -> p[0] == otherPos[0] && p[1] == otherPos[1])) {
|
||||
// 3. 为阻挡车辆寻找最近可用位置
|
||||
int[] newPos = findNearestAvailablePosition(grid, otherCar.getStartPoint());
|
||||
if (newPos != null) {
|
||||
// 4. 更新阻挡车辆位置并重新尝试
|
||||
otherCar.setStartPoint(newPos);
|
||||
return findOptimalCarForDestination(grid, endX, endY, cars);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 如果所有尝试都失败,返回null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 为被阻挡车辆寻找最近的可用位置
|
||||
*/
|
||||
private static int[] findNearestAvailablePosition(int[][] grid, int[] currentPos) {
|
||||
if (currentPos == null) return null;
|
||||
|
||||
int rows = grid.length;
|
||||
int cols = grid[0].length;
|
||||
boolean[][] visited = new boolean[rows][cols];
|
||||
Queue<int[]> queue = new LinkedList<>();
|
||||
queue.add(currentPos);
|
||||
visited[currentPos[0]][currentPos[1]] = true;
|
||||
|
||||
int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
int[] pos = queue.poll();
|
||||
|
||||
for (int[] dir : dirs) {
|
||||
int nx = pos[0] + dir[0];
|
||||
int ny = pos[1] + dir[1];
|
||||
|
||||
if (nx >= 0 && nx < rows && ny >= 0 && ny < cols && !visited[nx][ny]) {
|
||||
if (grid[nx][ny] == 1 || grid[nx][ny] == 2) {
|
||||
return new int[]{nx, ny}; // 找到第一个可用位置
|
||||
}
|
||||
visited[nx][ny] = true;
|
||||
queue.add(new int[]{nx, ny});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; // 没有找到可用位置
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package com.ruoyi.schedule.plcRead.srgv.roadUtil;
|
||||
|
||||
public class Site {
|
||||
|
||||
private String carId;
|
||||
|
||||
private int[] startPoint;//站点坐标
|
||||
|
||||
private String site;//当前站点
|
||||
|
||||
public Site(String carId, int[] startPoint) {
|
||||
this.carId = carId;
|
||||
this.startPoint = startPoint;
|
||||
}
|
||||
|
||||
public Site(String carId, int[] startPoint, String site) {
|
||||
this.carId = carId;
|
||||
this.startPoint = startPoint;
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
public String getSite() {
|
||||
return site;
|
||||
}
|
||||
|
||||
public String getCarId() {
|
||||
return carId;
|
||||
}
|
||||
|
||||
public void setCarId(String carId) {
|
||||
this.carId = carId;
|
||||
}
|
||||
|
||||
public int[] getStartPoint() {
|
||||
return startPoint;
|
||||
}
|
||||
|
||||
public void setStartPoint(int[] startPoint) {
|
||||
this.startPoint = startPoint;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
package com.ruoyi.schedule.scheduleTask.srgv;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ruoyi.bas.port.domain.WcsPort;
|
||||
import com.ruoyi.bas.port.mapper.WcsPortMapper;
|
||||
import com.ruoyi.bas.port.service.IWcsPortService;
|
||||
import com.ruoyi.common.domain.SendData2;
|
||||
import com.ruoyi.device.srgv.domain.DeviceSrgv;
|
||||
import com.ruoyi.queue.srgv.domain.WcsSrgvQueue;
|
||||
import com.ruoyi.queue.srgv.domain.WcsSrgvQueueFinish;
|
||||
import com.ruoyi.queue.srgv.service.IWcsSrgvQueueFinishService;
|
||||
import com.ruoyi.queue.srgv.service.IWcsSrgvQueueService;
|
||||
import com.ruoyi.schedule.plcRead.srgv.PlcSrgvRead;
|
||||
import com.ruoyi.schedule.plcRead.srgv.roadUtil.PathFinder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
@@ -46,7 +51,7 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
if (ObjUtil.isNotNull(deviceSrgv)) {
|
||||
//循环所有楼层
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
sRgvTask(deviceSrgv);
|
||||
sRgvTask(deviceSrgv, i);
|
||||
}
|
||||
} else {
|
||||
log.error("堆垛机设备为空");
|
||||
@@ -76,33 +81,299 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
completeCarTask(deviceSrgv,floor);
|
||||
|
||||
//小车发送任务
|
||||
sendTask(deviceSrgv);
|
||||
sendTask(deviceSrgv, floor);
|
||||
|
||||
//小车充电任务
|
||||
inCharging(deviceSrgv);
|
||||
inCharging(deviceSrgv, floor);
|
||||
|
||||
//小车离开充电桩
|
||||
outCharging(deviceSrgv);
|
||||
outCharging(deviceSrgv, floor);
|
||||
}
|
||||
/**
|
||||
* 小车离开充电桩
|
||||
*/
|
||||
private void outCharging(DeviceSrgv deviceSrgv) {
|
||||
private void outCharging(DeviceSrgv deviceSrgv, int floor) {
|
||||
|
||||
}
|
||||
/**
|
||||
* 小车充电任务
|
||||
*/
|
||||
private void inCharging(DeviceSrgv deviceSrgv) {
|
||||
private void inCharging(DeviceSrgv deviceSrgv, int floor) {
|
||||
|
||||
}
|
||||
/**
|
||||
* 小车发送任务
|
||||
*/
|
||||
private void sendTask(DeviceSrgv deviceSrgv) {
|
||||
private void sendTask(DeviceSrgv deviceSrgv, int floor) {
|
||||
WcsSrgvQueue param = new WcsSrgvQueue();
|
||||
param.setIssued("0");
|
||||
param.setTheFloor(Convert.toStr(floor));
|
||||
List<WcsSrgvQueue> wcsSrgvQueues = wcsSagvQueueMapper.selectWcsSrgvQueueList(param);
|
||||
if (CollUtil.isEmpty(wcsSrgvQueues)) {
|
||||
return;
|
||||
}
|
||||
for (WcsSrgvQueue wcsSrgvQueue : wcsSrgvQueues) {
|
||||
processPendingTask(wcsSrgvQueue, deviceSrgv, floor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理队列下发
|
||||
*/
|
||||
private void processPendingTask(WcsSrgvQueue wcsSrgvQueue, DeviceSrgv deviceSrgv, int floor) {
|
||||
String agvId = wcsSrgvQueue.getAgvId();
|
||||
if (agvId.equals(Convert.toStr(deviceSrgv.getDeviceId())) && deviceSrgv.getWorkMode() == 5 && deviceSrgv.getAllowSend() == 1) {
|
||||
String startSite = wcsSrgvQueue.getStartSite();
|
||||
String endSite = wcsSrgvQueue.getEndSite();
|
||||
String currSite = deviceSrgv.getCurrSite();
|
||||
|
||||
if (isSameFloor(startSite, endSite)) {
|
||||
if (StrUtil.isNotEmpty(endSite)) {
|
||||
handleUnspecifiedDestination(wcsSrgvQueue, startSite, floor, deviceSrgv);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//判断是否可以下发任务
|
||||
if ("0".equals(wcsSrgvQueue.getIssued()) && deviceSrgv.getStatus() == 1) {
|
||||
handleUnissuedTask(deviceSrgv, wcsSrgvQueue, currSite, startSite, endSite, floor);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未下发的任务
|
||||
*/
|
||||
private void handleUnissuedTask(DeviceSrgv deviceSrgv, WcsSrgvQueue wcsSrgvQueue, String currSite, String startSite, String endSite, int floor) {
|
||||
|
||||
//判断小车是否在起始点
|
||||
if (currSite.equals(startSite)) {
|
||||
//小车在起始点
|
||||
sendMainTask(deviceSrgv, wcsSrgvQueue, currSite, endSite, floor);
|
||||
} else {
|
||||
//小车不在起始点
|
||||
sendPointToPointTask(deviceSrgv, wcsSrgvQueue, currSite, startSite, floor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车发送点对点任务
|
||||
*/
|
||||
private void sendPointToPointTask(DeviceSrgv deviceSrgv, WcsSrgvQueue wcsSrgvQueue, String currSite, String startSite, int floor) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车发送点对点主任务
|
||||
*/
|
||||
private void sendMainTask(DeviceSrgv deviceSrgv, WcsSrgvQueue wcsSrgvQueue, String currSite, String endSite, int floor) {
|
||||
String taskType = wcsSrgvQueue.getTaskType();
|
||||
boolean sendFlag = false;
|
||||
switch (taskType) {
|
||||
case "1":
|
||||
// 空车任务(充电、换层、手动点到点、除尘等)
|
||||
sendFlag = sendEmptyCarTask(deviceSrgv, wcsSrgvQueue, currSite, endSite, floor);
|
||||
break;
|
||||
case "2":
|
||||
// 载货任务(出库、入库、移库等)
|
||||
sendFlag = sendFullCarTask(deviceSrgv, wcsSrgvQueue, currSite, endSite, floor);
|
||||
break;
|
||||
}
|
||||
|
||||
if (sendFlag) {
|
||||
//处理监控信息
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车发送载货任务
|
||||
*/
|
||||
private boolean sendFullCarTask(DeviceSrgv deviceSrgv, WcsSrgvQueue wcsSrgvQueue, String currSite, String endSite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车发送空车任务
|
||||
*/
|
||||
private boolean sendEmptyCarTask(DeviceSrgv deviceSrgv, WcsSrgvQueue wcsSrgvQueue, String startSite, String endSite, int floor) {
|
||||
//获取当前地图
|
||||
Map<String, int[]> arrByRoadSite = roadCacheService.getArrByRoadSite(floor);
|
||||
//获取空载地图
|
||||
int[][] emptyMap = roadCacheService.findEmptyMap(floor, arrByRoadSite.get(startSite)[0], arrByRoadSite.get(startSite)[1], arrByRoadSite.get(endSite)[0], arrByRoadSite.get(endSite)[1], splc);
|
||||
//计算最短路径
|
||||
List<int[]> shortestPath = PathFinder.findShortestPath(emptyMap, arrByRoadSite.get(startSite)[0], arrByRoadSite.get(startSite)[1], arrByRoadSite.get(endSite)[0], arrByRoadSite.get(endSite)[1]);
|
||||
|
||||
//判断是否能规划出最短路径
|
||||
if (shortestPath == null || shortestPath.isEmpty()) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
//获取拐点路径
|
||||
List<int[]> trun = PathFinder.extractInflectionPoints2(shortestPath, emptyMap);
|
||||
|
||||
// 路径点拼接
|
||||
StringBuilder pathBuilder = new StringBuilder();
|
||||
Map<String, String> roadSiteByArr = roadCacheService.getRoadSiteByArr(Integer.parseInt(floor));
|
||||
for (int[] point : shortestPath) {
|
||||
String site = roadSiteByArr.get(point[0] + "," + point[1]);
|
||||
if (!"000".equals(site)) {
|
||||
pathBuilder.append(site).append(",");
|
||||
}
|
||||
|
||||
}
|
||||
if (pathBuilder.length() > 0) {
|
||||
pathBuilder.setLength(pathBuilder.length() - 1); // 移除最后一个逗号
|
||||
}
|
||||
|
||||
// 拐点拼接
|
||||
StringBuilder inflectionBuilder = new StringBuilder();
|
||||
if (!trun.isEmpty()) {
|
||||
for (int[] ints : trun) {
|
||||
String site = roadSiteByArr.get(ints[0] + "," + ints[1]);//获取站点
|
||||
inflectionBuilder.append(site).append(",");
|
||||
}
|
||||
if (inflectionBuilder.length() > 0) {
|
||||
inflectionBuilder.setLength(inflectionBuilder.length() - 1);
|
||||
}
|
||||
// 当trun长度小于6时,后面用0拼接
|
||||
if (trun.size() < 6) {
|
||||
for (int i = trun.size(); i < 6; i++) {
|
||||
inflectionBuilder.append(",0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<SendData2> sendDataList = new ArrayList<>();
|
||||
if (!trun.isEmpty()) {
|
||||
SendData2 path = new SendData2();
|
||||
path.setAddress("4000");//地址
|
||||
path.setData(startSite + "," + endSite + "," + inflectionBuilder.toString());
|
||||
path.setType(2);
|
||||
sendDataList.add(path);
|
||||
} else {
|
||||
SendData2 path = new SendData2();
|
||||
path.setAddress("4000");//地址
|
||||
path.setData(startSite + "," + endSite);
|
||||
path.setType(2);
|
||||
sendDataList.add(path);
|
||||
}
|
||||
|
||||
|
||||
//任务号、原、目标数据转换
|
||||
String siteId = wcsSrgvQueue.getTaskNo();//+ "," + startSite + "," + endSite;
|
||||
SendData2 site = new SendData2();
|
||||
site.setAddress("4400");
|
||||
site.setData(siteId);
|
||||
site.setType(2);
|
||||
sendDataList.add(site);
|
||||
|
||||
//任务类型转换
|
||||
SendData2 taskType = new SendData2();
|
||||
taskType.setAddress("2200");
|
||||
taskType.setData(wcsSrgvQueue.getDefault02());
|
||||
taskType.setType(1);
|
||||
sendDataList.add(taskType);
|
||||
|
||||
//下发标志转换
|
||||
SendData2 flag = new SendData2();
|
||||
flag.setAddress("2202");
|
||||
flag.setData("1");
|
||||
flag.setType(1);
|
||||
sendDataList.add(flag);
|
||||
|
||||
return sendCarTask(sendDataList, deviceSrgv);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车发送任务
|
||||
*/
|
||||
private boolean sendCarTask(List<SendData2> sendDataList, DeviceSrgv deviceSrgv) {
|
||||
|
||||
boolean b = true;
|
||||
|
||||
for (SendData2 sendData : sendDataList) {
|
||||
String address = sendData.getAddress();
|
||||
String data = sendData.getData();//
|
||||
|
||||
String[] split = data.split(",");
|
||||
|
||||
int type = sendData.getType();//1单字 2双字 3字符串
|
||||
|
||||
//双字
|
||||
if (type == 2) {
|
||||
int[] intData = Arrays.stream(split)
|
||||
.map(String::trim)
|
||||
.mapToInt(Integer::parseInt)
|
||||
.toArray();
|
||||
boolean write = PlcSrgvRead.Write(address, intData);
|
||||
|
||||
if (!write) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//单字
|
||||
if (type == 1) {
|
||||
//String[]转为 short[]
|
||||
short[] shortData = new short[split.length];
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
try {
|
||||
shortData[i] = Short.parseShort(split[i].trim());
|
||||
} catch (NumberFormatException e) {
|
||||
// 记录日志并设置默认值
|
||||
shortData[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
boolean write = PlcSrgvRead.Write(address, shortData);
|
||||
|
||||
if (!write) {
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String collect = sendDataList.stream().map(li -> "{" + li.getAddress() + ":" + li.getData() + "}").collect(Collectors.joining(","));
|
||||
|
||||
log.info("小车:" + deviceSrgv.getDeviceId() + ",下发值:" + collect + ",,结果:" + b);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理未指定目的地的任务
|
||||
*/
|
||||
private void handleUnspecifiedDestination(WcsSrgvQueue wcsSrgvQueue, String startSite, int floor, DeviceSrgv deviceSrgv) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断站点是否是同一层
|
||||
*
|
||||
* @param startSite
|
||||
* @param endSite
|
||||
* @return
|
||||
*/
|
||||
private boolean isSameFloor(String startSite, String endSite) {
|
||||
// 参数校验
|
||||
if (startSite == null || endSite == null || startSite.length() < 2 || endSite.length() < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 正确提取起始和结束站点的楼层信息
|
||||
String startFloor = startSite.substring(startSite.length() - 2);
|
||||
String endFloor = endSite.substring(endSite.length() - 2);
|
||||
|
||||
// 比较是否在同一层
|
||||
return Objects.equals(startFloor, endFloor);
|
||||
}
|
||||
|
||||
/**
|
||||
* 小车完成队列
|
||||
*/
|
||||
@@ -124,6 +395,21 @@ public class BaseSRgvThread implements Runnable, SRgvThreadInterface {
|
||||
* 2、处理终点不是提升机站点的
|
||||
* */
|
||||
boolean taskFinishFlag = finishTaskByQueue(deviceSrgv.getCurrSite(), wcsSrgvQueue, floor);
|
||||
|
||||
if (taskFinishFlag) {
|
||||
String spare2 = deviceSrgv.getSpare2();
|
||||
if ("-".equals(spare2)) {
|
||||
//换层 不删除队列
|
||||
wcsSrgvQueue.setExecutionStatus("2");
|
||||
wcsSagvQueueMapper.updateWcsSrgvQueue(wcsSrgvQueue);
|
||||
} else {
|
||||
WcsSrgvQueueFinish wcsSrgvQueueFinish = new WcsSrgvQueueFinish();
|
||||
BeanUtil.copyProperties(wcsSrgvQueue, wcsSrgvQueueFinish);
|
||||
wcsSrgvQueueFinish.setFinishDate(DateUtil.now());
|
||||
wcsSagvQueueFinishMapper.insertWcsSrgvQueueFinish(wcsSrgvQueueFinish);
|
||||
wcsSagvQueueMapper.deleteWcsSrgvQueueByGuid(wcsSrgvQueue.getGuid());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user