package com.algo.service;
|
|
import com.algo.config.EnvDataConfig;
|
import com.algo.model.*;
|
import com.algo.util.JsonUtils;
|
import com.algo.util.PathTimeCalculator;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
|
import javax.annotation.PostConstruct;
|
import java.util.*;
|
import java.util.concurrent.*;
|
|
/**
|
* 路径规划服务
|
*/
|
@Service
|
public class PathPlanningService {
|
|
@Autowired
|
private EnvDataConfig envDataConfig;
|
|
/**
|
* 执行中任务提取
|
*/
|
private ExecutingTaskExtractor taskExtractor;
|
|
/**
|
* 路径规划
|
*/
|
private PathPlanner pathPlanner;
|
|
/**
|
* 碰撞检测
|
*/
|
private CollisionDetector collisionDetector;
|
|
/**
|
* 碰撞解决
|
*/
|
private CollisionResolver collisionResolver;
|
|
/**
|
* 剩余路径处理
|
*/
|
private RemainingPathProcessor remainingPathProcessor;
|
|
/**
|
* 路径时间计算器
|
*/
|
private PathTimeCalculator timeCalculator;
|
|
|
/**
|
* 线程池
|
*/
|
private final ExecutorService executorService = Executors.newFixedThreadPool(Math.max(4, Runtime.getRuntime().availableProcessors()));
|
|
/**
|
* CTU批处理大小
|
*/
|
private final int batchSize = 10;
|
|
|
/**
|
* 初始化各个组件
|
*/
|
@PostConstruct
|
public void initializeComponents() {
|
|
// 初始化路径规划器
|
this.pathPlanner = new AStarPathPlanner(envDataConfig.getPathMapping());
|
|
// 初始化碰撞检测器
|
this.collisionDetector = new CollisionDetector(envDataConfig.getPathMapping());
|
|
// 初始化碰撞解决器
|
this.collisionResolver = new CollisionResolver(collisionDetector);
|
|
// 初始化剩余路径处理器
|
this.remainingPathProcessor = new RemainingPathProcessor(envDataConfig.getPathMapping());
|
|
// 初始化时间计算器
|
Map<String, double[]> realCoordinateMapping = JsonUtils.loadRealCoordinateMapping("man_code.json");
|
this.timeCalculator = new PathTimeCalculator(envDataConfig.getPathMapping(), realCoordinateMapping);
|
|
// 为碰撞解决器设置时间计算器
|
this.collisionResolver.setTimeCalculator(timeCalculator);
|
}
|
|
/**
|
* 为所有CTU规划路径
|
*
|
* @param agvStatusList CTU状态列表
|
* @param includeIdleAgv 是否包含空闲CTU
|
* @param constraints 路径约束条件
|
* @return 路径规划结果
|
*/
|
public PathPlanningResult planAllAgvPaths(List<TaskData> taskList, List<AGVStatus> agvStatusList,
|
boolean includeIdleAgv,
|
List<double[]> constraints) {
|
// 初始化任务提取器
|
this.taskExtractor = new ExecutingTaskExtractor(envDataConfig.getPathMapping(), taskList);
|
|
long startTime = System.currentTimeMillis();
|
|
System.out.println("开始为 " + agvStatusList.size() + " 个CTU规划");
|
|
// 1. 构建现有剩余路径的时空占用表
|
System.out.println("步骤1: 构建时空占用表");
|
Map<String, String> spaceTimeOccupancyMap = remainingPathProcessor.buildSpaceTimeOccupancyMap(agvStatusList);
|
System.out.println("时空占用表构建完成,包含 " + spaceTimeOccupancyMap.size() + " 个占用点");
|
|
// 2. 分类处理CTU:有剩余路径的和需要新路径的
|
List<AGVStatus> ctuWithRemainingPaths = new ArrayList<>();
|
List<AGVStatus> ctuNeedingNewPaths = new ArrayList<>();
|
|
for (AGVStatus agv : agvStatusList) {
|
if (agv.hasRemainingPath()) {
|
ctuWithRemainingPaths.add(agv);
|
} else if (agv.canAcceptNewTask() || (includeIdleAgv && isAgvIdle(agv))) {
|
ctuNeedingNewPaths.add(agv);
|
}
|
}
|
|
System.out.println("CTU分类完成: 有剩余路径=" + ctuWithRemainingPaths.size() +
|
", 需要新路径=" + ctuNeedingNewPaths.size());
|
|
// 3. 处理有剩余路径的CTU
|
List<PlannedPath> plannedPaths = new ArrayList<>();
|
List<ExecutingTask> executingTasks = new ArrayList<>();
|
Map<String, String> plannedAgvIds = new HashMap<>();
|
|
System.out.println("步骤2: 处理有剩余路径的CTU");
|
for (AGVStatus agv : ctuWithRemainingPaths) {
|
PlannedPath remainingPath = processRemainingPath(agv);
|
if (remainingPath != null) {
|
plannedPaths.add(remainingPath);
|
plannedAgvIds.put(agv.getAgvId(), "REMAINING_PATH");
|
|
// 创建对应的执行任务
|
ExecutingTask task = createExecutingTaskFromRemainingPath(agv);
|
if (task != null) {
|
executingTasks.add(task);
|
}
|
|
System.out.println("CTU " + agv.getAgvId() + " 剩余路径处理完成,路径长度: " +
|
remainingPath.getCodeList().size());
|
}
|
}
|
|
// 4. 为需要新路径的CTU提取执行中任务
|
System.out.println("步骤3: 提取需要新路径的CTU任务");
|
List<ExecutingTask> newTasks = taskExtractor.extractExecutingTasks(ctuNeedingNewPaths);
|
System.out.println("提取到 " + newTasks.size() + " 个新任务");
|
|
// 5. 为新任务规划路径(考虑时空约束)
|
System.out.println("步骤4: 为新任务规划路径");
|
for (ExecutingTask task : newTasks) {
|
String agvId = task.getAgvId();
|
String currentPos = task.getCurrentPosition();
|
String targetPos = task.getTargetPosition();
|
|
// 避免重复规划
|
if (plannedAgvIds.containsKey(agvId)) {
|
System.out.println("跳过CTU " + agvId + ":已规划路径");
|
continue;
|
}
|
|
// 验证位置信息
|
if (currentPos == null || targetPos == null) {
|
System.out.println("CTU " + agvId + " 位置信息无效,跳过");
|
continue;
|
}
|
|
// 获取CTU状态信息
|
AGVStatus agvStatus = findAgvStatus(agvId, ctuNeedingNewPaths);
|
if (agvStatus == null) {
|
System.out.println("CTU " + agvId + " 状态信息未找到,跳过");
|
continue;
|
}
|
|
// 规划时空安全的路径
|
PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
|
currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
|
);
|
|
if (plannedPath != null) {
|
// 设置CTU信息
|
plannedPath.setAgvId(agvId);
|
plannedPath.setSegId(generateSegId(task.getTaskId(), agvId, task.getTaskType()));
|
|
// 增强路径代码信息
|
enhancePathCodes(plannedPath, task);
|
|
plannedPaths.add(plannedPath);
|
plannedAgvIds.put(agvId, targetPos);
|
executingTasks.add(task);
|
|
// 更新时空占用表
|
updateSpaceTimeOccupancyMap(plannedPath, spaceTimeOccupancyMap, agvStatus);
|
|
System.out.println("CTU " + agvId + " 路径规划成功:" + currentPos + " -> " + targetPos +
|
",路径长度: " + plannedPath.getCodeList().size());
|
} else {
|
System.out.println("CTU " + agvId + " 路径规划失败:" + currentPos + " -> " + targetPos);
|
}
|
}
|
|
// 6. 处理空闲CTU
|
if (includeIdleAgv) {
|
System.out.println("步骤5: 处理空闲CTU");
|
List<PlannedPath> idlePaths = planIdleAgvPathsWithConstraints(
|
ctuNeedingNewPaths, plannedAgvIds, constraints, spaceTimeOccupancyMap
|
);
|
plannedPaths.addAll(idlePaths);
|
System.out.println("空闲CTU路径规划完成,数量: " + idlePaths.size());
|
}
|
|
// 7. 最终碰撞检测和解决(针对同时生成的新路径)
|
System.out.println("步骤6: 最终碰撞检测");
|
List<Conflict> conflicts = collisionDetector.detectConflicts(plannedPaths);
|
|
if (!conflicts.isEmpty()) {
|
System.out.println("发现 " + conflicts.size() + " 个碰撞,开始解决");
|
plannedPaths = collisionResolver.resolveConflicts(plannedPaths, conflicts, executingTasks);
|
|
// 重新检测冲突
|
List<Conflict> remainingConflicts = collisionDetector.detectConflicts(plannedPaths);
|
conflicts = remainingConflicts;
|
}
|
|
long endTime = System.currentTimeMillis();
|
double totalTime = (endTime - startTime) / 1000.0;
|
|
// 构建结果
|
PathPlanningResult result = new PathPlanningResult(
|
agvStatusList.size(),
|
executingTasks.size(),
|
plannedPaths.size(),
|
totalTime,
|
conflicts.size(),
|
conflicts.isEmpty(),
|
"A*_WITH_SPACETIME_CONSTRAINTS",
|
plannedPaths,
|
executingTasks
|
);
|
|
System.out.println("路径规划完成 - 总CTU: " + result.getTotalAgvs() +
|
", 执行任务: " + result.getExecutingTasksCount() +
|
", 规划路径: " + result.getPlannedPathsCount() +
|
", 耗时: " + result.getTotalPlanningTime() + "s" +
|
", 无冲突: " + result.isCollisionFree());
|
|
return result;
|
}
|
|
/**
|
* 处理CTU的剩余路径
|
*
|
* @param agv CTU状态
|
* @return 处理后的路径
|
*/
|
private PlannedPath processRemainingPath(AGVStatus agv) {
|
if (!agv.hasRemainingPath()) {
|
return null;
|
}
|
|
PlannedPath remainingPath = agv.getRemainingPath();
|
List<PathCode> remainingCodes = new ArrayList<>();
|
|
// 从当前位置开始,获取剩余路径
|
List<PathCode> originalCodes = remainingPath.getCodeList();
|
for (int i = agv.getCurrentPathIndex(); i < originalCodes.size(); i++) {
|
remainingCodes.add(originalCodes.get(i));
|
}
|
|
// 创建新的路径对象
|
PlannedPath processedPath = new PlannedPath();
|
processedPath.setAgvId(agv.getAgvId());
|
processedPath.setCodeList(remainingCodes);
|
processedPath.setSegId(agv.getAgvId() + "_REMAINING_" + System.currentTimeMillis());
|
|
return processedPath;
|
}
|
|
/**
|
* 从剩余路径创建执行任务
|
*
|
* @param agv CTU状态
|
* @return 执行任务
|
*/
|
private ExecutingTask createExecutingTaskFromRemainingPath(AGVStatus agv) {
|
if (!agv.hasRemainingPath()) {
|
return null;
|
}
|
|
PlannedPath remainingPath = agv.getRemainingPath();
|
List<PathCode> codeList = remainingPath.getCodeList();
|
|
if (codeList.isEmpty() || agv.getCurrentPathIndex() >= codeList.size()) {
|
return null;
|
}
|
|
// 获取当前位置和目标位置
|
PathCode currentCode = codeList.get(agv.getCurrentPathIndex());
|
PathCode targetCode = codeList.get(codeList.size() - 1);
|
|
ExecutingTask task = new ExecutingTask();
|
task.setAgvId(agv.getAgvId());
|
task.setTaskId(remainingPath.getSegId());
|
task.setCurrentPosition(currentCode.getCode());
|
task.setTargetPosition(targetCode.getCode());
|
task.setTaskType("remaining");
|
task.setLoaded(false); // 假设值
|
task.setBackpackIndex(0);
|
task.setPriority(1);
|
|
return task;
|
}
|
|
/**
|
* 规划带时空约束的路径
|
*
|
* @param startPos 起始位置
|
* @param endPos 目标位置
|
* @param constraints 约束条件
|
* @param occupancyMap 时空占用表
|
* @param agvStatus CTU状态
|
* @return 规划的路径
|
*/
|
private PlannedPath planPathWithSpaceTimeConstraints(String startPos, String endPos,
|
List<double[]> constraints,
|
Map<String, String> occupancyMap,
|
AGVStatus agvStatus) {
|
// 尝试基本路径规划
|
PlannedPath basicPath = pathPlanner.planPath(startPos, endPos, constraints);
|
if (basicPath == null) {
|
return null;
|
}
|
|
// 找到安全的起始时间
|
long safeStartTime = remainingPathProcessor.findSafeStartTime(
|
basicPath, occupancyMap, agvStatus.getPhysicalConfig()
|
);
|
|
// 使用统一的时间计算器设置精确的时间信息
|
timeCalculator.calculatePathTiming(basicPath, safeStartTime, agvStatus.getPhysicalConfig(), 0.0);
|
|
return basicPath;
|
}
|
|
/**
|
* 路径的时间信息
|
*
|
* @param path 路径
|
* @param startTime 起始时间
|
* @param config 物理配置
|
*/
|
private void enhancePathWithTimeInfo(PlannedPath path, long startTime, CTUPhysicalConfig config) {
|
List<PathCode> codeList = path.getCodeList();
|
if (codeList == null || codeList.isEmpty()) {
|
return;
|
}
|
|
long currentTime = startTime;
|
|
for (int i = 0; i < codeList.size(); i++) {
|
PathCode pathCode = codeList.get(i);
|
|
// 设置预计到达时间
|
|
// 计算到下一个点的时间
|
if (i < codeList.size() - 1) {
|
double travelTime = config.getStandardPointDistance() / config.getNormalSpeed();
|
currentTime += (long) (travelTime * 1000);
|
|
// 如果有方向变化,增加转向时间
|
PathCode nextCode = codeList.get(i + 1);
|
if (!pathCode.getDirection().equals(nextCode.getDirection())) {
|
double turnTime = config.getTurnTime(pathCode.getDirection(), nextCode.getDirection());
|
currentTime += (long) (turnTime * 1000);
|
}
|
}
|
}
|
}
|
|
/**
|
* 更新时空占用表
|
*
|
* @param path 新路径
|
* @param occupancyMap 占用表
|
* @param agvStatus CTU状态
|
*/
|
private void updateSpaceTimeOccupancyMap(PlannedPath path, Map<String, String> occupancyMap,
|
AGVStatus agvStatus) {
|
// 将新路径添加到占用表中
|
List<PathCode> codeList = path.getCodeList();
|
CTUPhysicalConfig config = agvStatus.getPhysicalConfig();
|
|
long currentTime = System.currentTimeMillis() / 1000; // 转换为秒
|
|
for (PathCode pathCode : codeList) {
|
int[] coord = JsonUtils.getCoordinate(pathCode.getCode(), envDataConfig.getPathMapping());
|
if (coord != null) {
|
String spaceTimeKey = coord[0] + "," + coord[1] + "," + currentTime;
|
occupancyMap.put(spaceTimeKey, agvStatus.getAgvId());
|
currentTime += 2; // 假设每个点停留2秒
|
}
|
}
|
}
|
|
/**
|
* 查找CTU状态
|
*
|
* @param agvId CTU编号
|
* @param agvList CTU列表
|
* @return CTU状态
|
*/
|
private AGVStatus findAgvStatus(String agvId, List<AGVStatus> agvList) {
|
for (AGVStatus agv : agvList) {
|
if (agv.getAgvId().equals(agvId)) {
|
return agv;
|
}
|
}
|
return null;
|
}
|
|
/**
|
* 规划带约束的空闲CTU路径
|
*
|
* @param agvStatusList CTU状态列表
|
* @param plannedAgvIds 已规划CTU映射
|
* @param constraints 约束条件
|
* @param occupancyMap 时空占用表
|
* @return 空闲CTU路径列表
|
*/
|
private List<PlannedPath> planIdleAgvPathsWithConstraints(List<AGVStatus> agvStatusList,
|
Map<String, String> plannedAgvIds,
|
List<double[]> constraints,
|
Map<String, String> occupancyMap) {
|
List<PlannedPath> idlePaths = new ArrayList<>();
|
|
for (AGVStatus agvStatus : agvStatusList) {
|
String agvId = agvStatus.getAgvId();
|
|
// 跳过已规划的CTU
|
if (plannedAgvIds.containsKey(agvId)) {
|
continue;
|
}
|
|
// 检查CTU是否空闲
|
if (isAgvIdle(agvStatus)) {
|
String currentPos = agvStatus.getPosition();
|
String targetPos = getIdleAgvTarget(agvId, currentPos);
|
|
if (targetPos != null && !targetPos.equals(currentPos)) {
|
PlannedPath idlePath = planPathWithSpaceTimeConstraints(
|
currentPos, targetPos, constraints, occupancyMap, agvStatus
|
);
|
|
if (idlePath != null) {
|
idlePath.setAgvId(agvId);
|
idlePath.setSegId(generateSegId("IDLE", agvId, "idle"));
|
|
// 设置空闲路径的代码信息
|
enhanceIdlePathCodes(idlePath, agvStatus);
|
|
idlePaths.add(idlePath);
|
plannedAgvIds.put(agvId, targetPos);
|
|
// 更新占用表
|
updateSpaceTimeOccupancyMap(idlePath, occupancyMap, agvStatus);
|
|
System.out.println("空闲CTU " + agvId + " 路径规划成功:" + currentPos + " -> " + targetPos);
|
}
|
}
|
}
|
}
|
|
return idlePaths;
|
}
|
|
/**
|
* 路径代码信息
|
*
|
* @param plannedPath 规划路径
|
* @param task 执行任务
|
*/
|
private void enhancePathCodes(PlannedPath plannedPath, ExecutingTask task) {
|
List<PathCode> codeList = plannedPath.getCodeList();
|
if (codeList == null || codeList.isEmpty()) {
|
return;
|
}
|
|
String actionType = "2"; // 任务类型
|
String taskId = task.getTaskId();
|
int backpackLevel = task.getBackpackIndex();
|
|
for (int i = 0; i < codeList.size(); i++) {
|
PathCode pathCode = codeList.get(i);
|
|
// 设置基本信息
|
pathCode.setActionType(actionType);
|
pathCode.setTaskId(taskId);
|
pathCode.setLev(backpackLevel);
|
|
// 设置目标点信息
|
if (i == codeList.size() - 1) { // 最后一个点
|
pathCode.setTargetPoint(true);
|
if (task.isLoaded()) {
|
pathCode.setPosType("2"); // 放货
|
} else {
|
pathCode.setPosType("1"); // 取货
|
}
|
} else {
|
pathCode.setTargetPoint(false);
|
pathCode.setPosType(null);
|
}
|
}
|
}
|
|
/**
|
* 空闲路径代码信息
|
*
|
* @param plannedPath 规划路径
|
* @param agvStatus CTU状态
|
*/
|
private void enhanceIdlePathCodes(PlannedPath plannedPath, AGVStatus agvStatus) {
|
List<PathCode> codeList = plannedPath.getCodeList();
|
if (codeList == null || codeList.isEmpty()) {
|
return;
|
}
|
|
String actionType = agvStatus.needsCharging() ? "3" : "4"; // 充电或待机
|
|
for (PathCode pathCode : codeList) {
|
pathCode.setActionType(actionType);
|
pathCode.setTaskId(null);
|
pathCode.setPosType(null);
|
pathCode.setLev(0);
|
pathCode.setTargetPoint(false);
|
}
|
}
|
|
/**
|
* 判断CTU是否空闲
|
*
|
* @param agvStatus CTU状态
|
* @return true如果CTU空闲
|
*/
|
private boolean isAgvIdle(AGVStatus agvStatus) {
|
if (!agvStatus.isAvailable()) {
|
return false;
|
}
|
|
// 检查背篓是否有执行中的任务
|
List<BackpackData> backpack = agvStatus.getBackpack();
|
if (backpack != null) {
|
for (BackpackData bp : backpack) {
|
if (bp.getTaskId() != null && !bp.getTaskId().trim().isEmpty()) {
|
return false;
|
}
|
}
|
}
|
|
return true;
|
}
|
|
/**
|
* 获取空闲CTU的目标位置
|
*
|
* @param agvId CTU编号
|
* @param currentPosition 当前位置
|
* @return 目标位置
|
*/
|
private String getIdleAgvTarget(String agvId, String currentPosition) {
|
// 根据CTU ID的哈希值选择位置
|
int hashValue = Math.abs(agvId.hashCode() % 1000);
|
|
// 优先选择充电位置
|
List<String> chargingPositions = taskExtractor.getChargingPositions();
|
if (!chargingPositions.isEmpty()) {
|
for (String pos : chargingPositions) {
|
if (!pos.equals(currentPosition)) {
|
return pos;
|
}
|
}
|
}
|
|
// 其次选择待机位置
|
List<String> standbyPositions = taskExtractor.getStandbyPositions();
|
if (!standbyPositions.isEmpty()) {
|
return standbyPositions.get(hashValue % standbyPositions.size());
|
}
|
|
return null;
|
}
|
|
/**
|
* 生成段落ID
|
*
|
* @param taskId 任务ID
|
* @param agvId CTU编号
|
* @param taskType 任务类型
|
* @return 段落ID
|
*/
|
private String generateSegId(String taskId, String agvId, String taskType) {
|
return taskId + "_" + agvId + "_" + taskType;
|
}
|
|
/**
|
* 路径规划结果类
|
*/
|
public static class PathPlanningResult {
|
private int totalAgvs;
|
private int executingTasksCount;
|
private int plannedPathsCount;
|
private double totalPlanningTime;
|
private int conflictsDetected;
|
private boolean collisionFree;
|
private String algorithm;
|
private List<PlannedPath> plannedPaths;
|
private List<ExecutingTask> executingTasks;
|
|
public PathPlanningResult(int totalAgvs, int executingTasksCount, int plannedPathsCount,
|
double totalPlanningTime, int conflictsDetected, boolean collisionFree,
|
String algorithm, List<PlannedPath> plannedPaths, List<ExecutingTask> executingTasks) {
|
this.totalAgvs = totalAgvs;
|
this.executingTasksCount = executingTasksCount;
|
this.plannedPathsCount = plannedPathsCount;
|
this.totalPlanningTime = totalPlanningTime;
|
this.conflictsDetected = conflictsDetected;
|
this.collisionFree = collisionFree;
|
this.algorithm = algorithm;
|
this.plannedPaths = plannedPaths;
|
this.executingTasks = executingTasks;
|
}
|
|
// Getter方法
|
public int getTotalAgvs() {
|
return totalAgvs;
|
}
|
|
public int getExecutingTasksCount() {
|
return executingTasksCount;
|
}
|
|
public int getPlannedPathsCount() {
|
return plannedPathsCount;
|
}
|
|
public double getTotalPlanningTime() {
|
return totalPlanningTime;
|
}
|
|
public int getConflictsDetected() {
|
return conflictsDetected;
|
}
|
|
public boolean isCollisionFree() {
|
return collisionFree;
|
}
|
|
public String getAlgorithm() {
|
return algorithm;
|
}
|
|
public List<PlannedPath> getPlannedPaths() {
|
return plannedPaths;
|
}
|
|
public List<ExecutingTask> getExecutingTasks() {
|
return executingTasks;
|
}
|
|
@Override
|
public String toString() {
|
return "PathPlanningResult{" +
|
"totalAgvs=" + totalAgvs +
|
", executingTasksCount=" + executingTasksCount +
|
", plannedPathsCount=" + plannedPathsCount +
|
", executingTasksCount=" + executingTasksCount +
|
", totalPlanningTime=" + totalPlanningTime +
|
", conflictsDetected=" + conflictsDetected +
|
", collisionFree=" + executingTasksCount +
|
", algorithm=" + algorithm +
|
", plannedPaths=" + plannedPaths +
|
", executingTasks='" + executingTasks + '\'' +
|
'}';
|
}
|
}
|
|
/**
|
* 并行规划任务路径
|
*/
|
private List<PlannedPath> planTasksInParallel(List<ExecutingTask> tasks,
|
List<double[]> constraints,
|
Map<String, String> spaceTimeOccupancyMap,
|
List<AGVStatus> agvStatusList) {
|
|
List<PlannedPath> allPaths = Collections.synchronizedList(new ArrayList<>());
|
|
// 按优先级排序任务
|
List<ExecutingTask> sortedTasks = new ArrayList<>(tasks);
|
sortedTasks.sort((t1, t2) -> Integer.compare(t2.getPriority(), t1.getPriority()));
|
|
// 分批处理
|
List<List<ExecutingTask>> batches = createBatches(sortedTasks, batchSize);
|
|
for (int batchIndex = 0; batchIndex < batches.size(); batchIndex++) {
|
List<ExecutingTask> batch = batches.get(batchIndex);
|
|
System.out.println("处理批次 " + (batchIndex + 1) + "/" + batches.size() +
|
", 任务数: " + batch.size());
|
|
List<Future<PlannedPath>> futures = new ArrayList<>();
|
|
for (ExecutingTask task : batch) {
|
Future<PlannedPath> future = executorService.submit(() -> {
|
return planSingleTaskPath(task, constraints, spaceTimeOccupancyMap, agvStatusList);
|
});
|
futures.add(future);
|
}
|
|
for (Future<PlannedPath> future : futures) {
|
try {
|
PlannedPath path = future.get(60, TimeUnit.SECONDS);
|
if (path != null) {
|
allPaths.add(path);
|
System.out.println("CTU " + path.getAgvId() + " 路径规划成功");
|
}
|
} catch (TimeoutException e) {
|
System.out.println("路径规划超时,跳过");
|
future.cancel(true);
|
} catch (Exception e) {
|
System.out.println("路径规划异常: " + e.getMessage());
|
}
|
}
|
}
|
|
return allPaths;
|
}
|
|
/**
|
* 为单个任务规划路径
|
*/
|
private PlannedPath planSingleTaskPath(ExecutingTask task,
|
List<double[]> constraints,
|
Map<String, String> spaceTimeOccupancyMap,
|
List<AGVStatus> agvStatusList) {
|
|
String agvId = task.getAgvId();
|
String currentPos = task.getCurrentPosition();
|
String targetPos = task.getTargetPosition();
|
|
if (currentPos == null || targetPos == null) {
|
return null;
|
}
|
|
// 获取CTU状态信息
|
AGVStatus agvStatus = findAgvStatus(agvId, agvStatusList);
|
if (agvStatus == null) {
|
return null;
|
}
|
|
// 规划时空安全的路径
|
PlannedPath plannedPath = planPathWithSpaceTimeConstraints(
|
currentPos, targetPos, constraints, spaceTimeOccupancyMap, agvStatus
|
);
|
|
if (plannedPath != null) {
|
// 设置CTU信息
|
plannedPath.setAgvId(agvId);
|
plannedPath.setSegId(generateSegId(task.getTaskId(), agvId, task.getTaskType()));
|
|
enhancePathCodes(plannedPath, task);
|
}
|
|
return plannedPath;
|
}
|
|
/**
|
* 创建批次
|
*/
|
private List<List<ExecutingTask>> createBatches(List<ExecutingTask> tasks, int batchSize) {
|
List<List<ExecutingTask>> batches = new ArrayList<>();
|
|
for (int i = 0; i < tasks.size(); i += batchSize) {
|
int end = Math.min(i + batchSize, tasks.size());
|
batches.add(new ArrayList<>(tasks.subList(i, end)));
|
}
|
|
return batches;
|
}
|
|
/**
|
* 关闭服务
|
*/
|
public void shutdown() {
|
if (executorService != null && !executorService.isShutdown()) {
|
executorService.shutdown();
|
try {
|
if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
|
executorService.shutdownNow();
|
}
|
} catch (InterruptedException e) {
|
executorService.shutdownNow();
|
Thread.currentThread().interrupt();
|
}
|
}
|
}
|
}
|