diff --git a/servo/src/main/java/com/galaxis/rcs/common/entity/StoreLocation.java b/servo/src/main/java/com/galaxis/rcs/common/entity/StoreLocation.java
index a301f1f..3df6545 100644
--- a/servo/src/main/java/com/galaxis/rcs/common/entity/StoreLocation.java
+++ b/servo/src/main/java/com/galaxis/rcs/common/entity/StoreLocation.java
@@ -1,5 +1,7 @@
package com.galaxis.rcs.common.entity;
+import org.clever.core.Conv;
+
/**
* 存储存储位信息
*
@@ -21,4 +23,25 @@ public record StoreLocation(
public String toString() {
return rackId + "_" + bay + "_" + level + "_" + cell;
}
+
+ public static StoreLocation of(String rackPosition, String separator) {
+ // 从 'rack1/0/0/0' 解析为 'rack1_0_0_0'
+ if (rackPosition == null || rackPosition.isEmpty()) {
+ throw new RuntimeException("rackPosition cannot be null or empty");
+ }
+
+ String[] parts = rackPosition.split(separator);
+ if (parts.length == 1) {
+ return new StoreLocation(parts[0], 0, 0, 0);
+ }
+ if (parts.length != 4) {
+ throw new RuntimeException("Invalid rack position format: " + rackPosition);
+ }
+
+ String rackId = parts[0];
+ int bay = Conv.asInteger(parts[1]);
+ int level = Conv.asInteger(parts[2]);
+ int cell = Conv.asInteger(parts[3]);
+ return new StoreLocation(rackId, bay, level, cell);
+ }
}
diff --git a/servo/src/main/java/com/galaxis/rcs/common/enums/LCCDirection.java b/servo/src/main/java/com/galaxis/rcs/common/enums/LCCDirection.java
index 7b073ef..4cc23ea 100644
--- a/servo/src/main/java/com/galaxis/rcs/common/enums/LCCDirection.java
+++ b/servo/src/main/java/com/galaxis/rcs/common/enums/LCCDirection.java
@@ -35,4 +35,17 @@ public enum LCCDirection {
throw new IllegalArgumentException("No constant with name: " + value);
}
+
+ public static LCCDirection fromString(String value, LCCDirection defaultValue) {
+ if (value == null)
+ throw new IllegalArgumentException("Value cannot be null");
+
+ for (LCCDirection type : LCCDirection.values()) {
+ if (type.toString().equalsIgnoreCase(value.trim())) {
+ return type;
+ }
+ }
+
+ return defaultValue;
+ }
}
diff --git a/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2DeviceConnector.java b/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2DeviceConnector.java
index 6a29965..f6fb011 100644
--- a/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2DeviceConnector.java
+++ b/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2DeviceConnector.java
@@ -1,20 +1,12 @@
package com.galaxis.rcs.connector.cl2;
import com.fasterxml.jackson.core.JsonProcessingException;
-import com.galaxis.rcs.plan.PlanTaskSequence;
-import com.galaxis.rcs.ptr.sendEntity.RcsConfigMessage;
import com.galaxis.rcs.ptr.sendEntity.RcsTaskMessage;
import com.galaxis.rcs.ptr.AmrMessageHandler;
-import com.google.common.base.Splitter;
import com.yvan.logisticsModel.LogisticsRuntime;
-import com.yvan.logisticsModel.PtrAgvItem;
import lombok.extern.slf4j.Slf4j;
-import org.clever.core.BannerUtils;
-import org.clever.core.json.JsonWrapper;
import org.eclipse.paho.mqttv5.common.MqttException;
-import java.util.Map;
-
/**
* CL2 车型报文推送
*/
diff --git a/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2Item.java b/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2Item.java
index e490862..3d2d893 100644
--- a/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2Item.java
+++ b/servo/src/main/java/com/galaxis/rcs/connector/cl2/Cl2Item.java
@@ -2,7 +2,7 @@ package com.galaxis.rcs.connector.cl2;
import com.galaxis.rcs.ptr.sendEntity.RcsConfigMessage;
import com.yvan.logisticsModel.LogisticsRuntime;
-import com.yvan.logisticsModel.PtrAgvItem;
+import com.galaxis.rcs.ptr.PtrAgvItem;
import java.util.Map;
diff --git a/servo/src/main/java/com/galaxis/rcs/connector/cl2/VirtualCl2Connector.java b/servo/src/main/java/com/galaxis/rcs/connector/cl2/VirtualCl2Connector.java
index 28ea82c..a42cdd1 100644
--- a/servo/src/main/java/com/galaxis/rcs/connector/cl2/VirtualCl2Connector.java
+++ b/servo/src/main/java/com/galaxis/rcs/connector/cl2/VirtualCl2Connector.java
@@ -1,9 +1,5 @@
package com.galaxis.rcs.connector.cl2;
-import com.galaxis.rcs.common.entity.RcsTaskPlan;
-import com.yvan.logisticsModel.ExecutorItem;
-import com.yvan.logisticsModel.LogisticsRuntime;
-import com.yvan.logisticsModel.PtrAgvItem;
import lombok.extern.slf4j.Slf4j;
@Slf4j
diff --git a/servo/src/main/java/com/galaxis/rcs/plan/path/AStarNodeState.java b/servo/src/main/java/com/galaxis/rcs/plan/path/AStarNodeState.java
deleted file mode 100644
index e7f5f10..0000000
--- a/servo/src/main/java/com/galaxis/rcs/plan/path/AStarNodeState.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package com.galaxis.rcs.plan.path;
-
-/**
- * A*路径节点状态
- */
-public record AStarNodeState(
- String nodeId, // 当前节点ID
- int direction, // 当前方向 (0,90,180,270)
- float gCost, // 实际代价
- float hCost, // 启发式代价
- AStarNodeState parent // 父节点
-) implements Comparable {
-
- // 状态唯一标识
- public String stateKey() {
- return nodeId + ":" + direction;
- }
-
- // 总代价
- public float fCost() {
- return gCost + hCost;
- }
-
- @Override
- public int compareTo(AStarNodeState other) {
- return Float.compare(this.fCost(), other.fCost());
- }
-}
-
diff --git a/servo/src/main/java/com/galaxis/rcs/plan/path/AStarPathPlanner.java b/servo/src/main/java/com/galaxis/rcs/plan/path/AStarPathPlanner.java
index 0c1a830..8347234 100644
--- a/servo/src/main/java/com/galaxis/rcs/plan/path/AStarPathPlanner.java
+++ b/servo/src/main/java/com/galaxis/rcs/plan/path/AStarPathPlanner.java
@@ -1,122 +1,220 @@
package com.galaxis.rcs.plan.path;
+import com.galaxis.rcs.common.enums.LCCDirection;
+import com.google.common.collect.Maps;
+
import java.util.*;
-/**
- * A*路径规划器
- */
+import static com.galaxis.rcs.plan.path.PathUtils.*;
+
public class AStarPathPlanner {
+ private static final float ROTATION_COST_PER_DEGREE = 0.01f;
+ private static final float BLOCKED_COST = 10000f; // 阻塞系数成本
+ private static final float WEIGHT_FACTOR = 1.5f; // 权重因子
+
private final NavigationGraph graph;
+ private final Map nodeWeights = Maps.newConcurrentMap();
+ private final Map blockedNodes = Maps.newConcurrentMap();
public AStarPathPlanner(NavigationGraph graph) {
this.graph = graph;
}
- /**
- * 规划路径
- *
- * @param startNodeId 起始节点ID
- * @param startDirection 起始方向
- * @param endNodeId 目标节点ID
- * @param endDirection 目标方向
- * @return 路径节点序列 (包含方向信息)
- */
- public List planPath(String startNodeId, int startDirection, String endNodeId, int endDirection) {
- // 开放集 (优先队列)
- PriorityQueue openSet = new PriorityQueue<>();
-
- // 状态管理
- Map gScoreMap = new HashMap<>();
- Map cameFrom = new HashMap<>();
-
- // 初始化起点
- AStarNodeState start = new AStarNodeState(
- startNodeId,
- startDirection,
- 0,
- graph.heuristicCost(startNodeId, endNodeId),
- null
- );
-
- openSet.add(start);
- gScoreMap.put(start.stateKey(), 0.0f);
-
- while (!openSet.isEmpty()) {
- AStarNodeState current = openSet.poll();
-
- // 到达目标状态
- if (current.nodeId().equals(endNodeId) &&
- current.direction() == endDirection) {
- return reconstructPath(cameFrom, current);
+ // 路径规划状态
+ public List findPath(String startId, LCCDirection startDirection, String endId, LCCDirection endDirection) {
+ Node start = graph.getNode(startId);
+ Node end = graph.getNode(endId);
+ if (start == null) {
+ throw new RuntimeException("Start node not found: " + startId);
+ }
+ if (end == null) {
+ throw new RuntimeException("End node not found: " + endId);
+ }
+
+ // 使用复合键避免状态重复
+ Map visited = new HashMap<>();
+ PriorityQueue open = new PriorityQueue<>();
+
+ // 初始状态
+ State initialState = new State(start, startDirection, 0, heuristic(start, end), null);
+ open.add(initialState);
+ visited.put(stateKey(start.id(), startDirection), initialState);
+
+ while (!open.isEmpty()) {
+ State current = open.poll();
+
+ // 到达目标节点且方向匹配
+ if (current.node().id().equals(endId) && current.direction() == endDirection) {
+ return buildPath(current);
}
- // 处理移动操作 (到相邻节点)
- for (NavigationNode neighbor : graph.getAdjacentNodes(current.nodeId())) {
- PathSegment segment = graph.getPathSegment(current.nodeId(), neighbor.id());
- if (segment == null) continue;
-
- float moveCost = segment.distance();
- float tentativeGCost = current.gCost() + moveCost;
- String neighborKey = neighbor.id() + ":" + current.direction();
-
- // 发现更好路径
- if (tentativeGCost < gScoreMap.getOrDefault(neighborKey, Float.MAX_VALUE)) {
- AStarNodeState neighborState = new AStarNodeState(
- neighbor.id(),
- current.direction(),
- tentativeGCost,
- graph.heuristicCost(neighbor.id(), endNodeId),
+ // 处理邻边移动
+ for (Node neighbor : graph.getNeighbors(current.node())) {
+ // 检查节点是否被阻塞
+ if (isBlocked(neighbor.id())) continue;
+
+ // 计算移动方向
+ LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor);
+
+ // 尝试前进
+ if (canMoveForward(current.direction(), moveDirection)) {
+ float moveCost = calculateMoveCost(current.node(), neighbor, false);
+ considerState(current, neighbor, current.direction(),
+ moveCost, open, visited, end);
+ }
+ // 尝试后退
+ else if (canMoveBackward(current.direction(), moveDirection)) {
+ float moveCost = calculateMoveCost(current.node(), neighbor, true);
+ considerState(current, neighbor, current.direction(),
+ moveCost, open, visited, end);
+ }
+ // 需要旋转
+ else if (current.node().rotatable()) {
+ // 计算需要旋转到的方向
+ LCCDirection requiredDirection = calculateRequiredDirection(moveDirection);
+
+ // 考虑旋转后移动
+ float rotationCost = calculateRotationCost(
+ current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE
+ );
+ float moveCost = calculateMoveCost(current.node(), neighbor, false);
+ float totalCost = rotationCost + moveCost;
+
+ // 创建旋转状态
+ State rotatedState = new State(
+ current.node(), requiredDirection,
+ current.g() + rotationCost,
+ heuristic(current.node(), end),
current
);
- cameFrom.put(neighborKey, current);
- gScoreMap.put(neighborKey, tentativeGCost);
- openSet.add(neighborState);
+ // 考虑旋转后的移动
+ considerState(rotatedState, neighbor, requiredDirection,
+ moveCost, open, visited, end);
}
}
- // 处理旋转操作 (当前节点可旋转时)
- NavigationNode currentNode = graph.nodes.get(current.nodeId());
- if (currentNode != null && currentNode.rotatable()) {
- for (int rotation : new int[]{90, -90}) {
- int newDirection = (current.direction() + rotation + 360) % 360;
- float rotateCost = 1.0f; // 旋转代价
- float tentativeGCost = current.gCost() + rotateCost;
- String rotatedKey = current.nodeId() + ":" + newDirection;
-
- // 发现更好路径
- if (tentativeGCost < gScoreMap.getOrDefault(rotatedKey, Float.MAX_VALUE)) {
- AStarNodeState rotatedState = new AStarNodeState(
- current.nodeId(),
- newDirection,
- tentativeGCost,
- current.hCost(), // 旋转不改变位置,启发值不变
- current
- );
+ // 处理原地旋转 - 只考虑目标方向
+ if (current.node().rotatable()) {
+ // 只考虑旋转到目标方向(如果可能)
+ if (current.direction() != endDirection) {
+ float rotationCost = calculateRotationCost(
+ current.direction(), endDirection, ROTATION_COST_PER_DEGREE
+ );
+ considerState(current, current.node(), endDirection,
+ rotationCost, open, visited, end);
+ }
+
+ // 另外考虑旋转到移动所需的方向
+ for (Node neighbor : graph.getNeighbors(current.node())) {
+ LCCDirection moveDirection = calculateMoveDirection(current.node(), neighbor);
+ LCCDirection requiredDirection = calculateRequiredDirection(moveDirection);
- cameFrom.put(rotatedKey, current);
- gScoreMap.put(rotatedKey, tentativeGCost);
- openSet.add(rotatedState);
+ if (requiredDirection != current.direction()) {
+ float rotationCost = calculateRotationCost(
+ current.direction(), requiredDirection, ROTATION_COST_PER_DEGREE
+ );
+ considerState(current, current.node(), requiredDirection,
+ rotationCost, open, visited, end);
}
}
+
+ // for (LCCDirection rotation : LCCDirection.values()) {
+ // if (rotation == current.direction()) continue;
+ //
+ // float rotationCost = calculateRotationCost(
+ // current.direction(), rotation, ROTATION_COST_PER_DEGREE
+ // );
+ // considerState(current, current.node(), rotation,
+ // rotationCost, open, visited, end);
+ // }
}
}
-
- return Collections.emptyList(); // 未找到路径
+ return Collections.emptyList();
}
- private List reconstructPath(
- Map cameFrom,
- AStarNodeState endState
- ) {
- LinkedList path = new LinkedList<>();
- AStarNodeState current = endState;
-
- while (current != null) {
- path.addFirst(current);
- current = cameFrom.get(current.stateKey());
+ /**
+ * 考虑新的状态并更新开放列表和访问记录
+ *
+ * @param current 当前状态
+ * @param nextNode 下一个节点
+ * @param nextDirection 下一个方向
+ * @param cost 移动成本
+ * @param open 开放列表
+ * @param visited 访问记录
+ * @param end 目标节点
+ */
+ private void considerState(State current, Node nextNode, LCCDirection nextDirection,
+ float cost, PriorityQueue open,
+ Map visited, Node end) {
+ String key = stateKey(nextNode.id(), nextDirection);
+ float newG = current.g() + cost;
+
+ if (!visited.containsKey(key) || visited.get(key).g() > newG) {
+ float h = heuristic(nextNode, end);
+ State newState = new State(nextNode, nextDirection, newG, h, current);
+ open.add(newState);
+ visited.put(key, newState);
}
+ }
+ private List buildPath(State state) {
+ LinkedList path = new LinkedList<>();
+ while (state != null) {
+ path.addFirst(state);
+ state = state.parent();
+ }
return path;
}
+
+ /**
+ * 启发式函数,计算两个节点之间的距离
+ */
+ private float heuristic(Node a, Node b) {
+ return graph.distance(a, b);
+ // 使用曼哈顿距离??
+ // return Math.abs(a.x() - b.x()) + Math.abs(a.z() - b.z());
+ }
+
+ /**
+ * 生成状态的唯一键
+ */
+ private String stateKey(String nodeId, LCCDirection direction) {
+ return nodeId + "|" + direction;
+ }
+
+ public void setNodeWeight(String nodeId, float weight) {
+ nodeWeights.put(nodeId, weight);
+ }
+
+ public void setBlocked(String nodeId, float blockedFactor) {
+ blockedNodes.put(nodeId, blockedFactor);
+ }
+
+ private float calculateMoveCost(Node from, Node to, boolean isBackward) {
+ float baseCost = graph.distance(from, to);
+ float weight = nodeWeights.getOrDefault(to.id(), 1.0f);
+ float blockedFactor = blockedNodes.getOrDefault(to.id(), 0f);
+
+ // 后退移动增加额外成本??
+ // return baseCost * weight * (1 + blockedFactor) * (isBackward ? 1.2f : 1.0f);
+ return baseCost * weight * (1 + blockedFactor);
+ }
+
+ private boolean isBlocked(String nodeId) {
+ return blockedNodes.containsKey(nodeId) && blockedNodes.get(nodeId) > 0.8f;
+ }
+
+ private boolean canMoveForward(LCCDirection currentDir, LCCDirection moveDir) {
+ return currentDir == moveDir;
+ }
+
+ private boolean canMoveBackward(LCCDirection currentDir, LCCDirection moveDir) {
+ return currentDir == getOppositeDirection(moveDir);
+ }
+
+ private LCCDirection calculateRequiredDirection(LCCDirection moveDirection) {
+ // 侧插式AGV只能前进或后退,不能横移
+ return moveDirection;
+ }
}
diff --git a/servo/src/main/java/com/galaxis/rcs/plan/path/GraphInitializer.java b/servo/src/main/java/com/galaxis/rcs/plan/path/GraphInitializer.java
deleted file mode 100644
index e3dd8cf..0000000
--- a/servo/src/main/java/com/galaxis/rcs/plan/path/GraphInitializer.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.galaxis.rcs.plan.path;
-
-import com.galaxis.rcs.common.enums.OperationSide;
-import com.yvan.logisticsModel.LogisticsRuntime;
-import org.clever.core.Conv;
-
-import java.util.List;
-import java.util.Map;
-
-public class GraphInitializer {
-
-// public NavigationGraph initializeGraph(LogisticsRuntime runtime, String agvType, List