package com.algo.service; import com.algo.model.*; import com.algo.util.JsonUtils; import org.springframework.stereotype.Service; import java.util.*; import java.util.concurrent.*; /** * 路径规划服务 */ public class PathPlanningService { /** * 路径映射表 */ private Map> pathMapping; /** * 环境配置 */ private Map environmentConfig; /** * 执行中任务提取 */ private ExecutingTaskExtractor taskExtractor; /** * 路径规划 */ private PathPlanner pathPlanner; /** * 碰撞检测 */ private CollisionDetector collisionDetector; /** * 碰撞解决 */ private CollisionResolver collisionResolver; /** * 剩余路径处理 */ private RemainingPathProcessor remainingPathProcessor; /** * 线程池大小 */ private final int threadPoolSize; /** * 线程池 */ private final ExecutorService executorService; /** * CTU批处理大小 */ private final int batchSize = 10; /** * 构造函数 * * @param pathMapping 路径映射表 * @param environmentConfig 环境配置 * @param taskDataList 任务数据列表 */ public PathPlanningService(Map> pathMapping, Map environmentConfig, List taskDataList) { this.pathMapping = pathMapping; this.environmentConfig = environmentConfig; this.threadPoolSize = Math.max(4, Runtime.getRuntime().availableProcessors()); this.executorService = Executors.newFixedThreadPool(threadPoolSize); // 初始化 initializeComponents(taskDataList); System.out.println("路径规划服务初始化完成(线程池大小: " + threadPoolSize + ")"); } /** * 初始化各个组件 * * @param taskDataList 任务数据列表 */ private void initializeComponents(List taskDataList) { // 初始化任务提取器 this.taskExtractor = new ExecutingTaskExtractor(pathMapping, taskDataList); // 初始化路径规划器 this.pathPlanner = new AStarPathPlanner(pathMapping); // 初始化碰撞检测器 this.collisionDetector = new CollisionDetector(pathMapping); // 初始化碰撞解决器 this.collisionResolver = new CollisionResolver(collisionDetector); // 初始化剩余路径处理器 this.remainingPathProcessor = new RemainingPathProcessor(pathMapping); } /** * 为所有CTU规划路径 * * @param agvStatusList CTU状态列表 * @param includeIdleAgv 是否包含空闲CTU * @param constraints 路径约束条件 * @return 路径规划结果 */ public PathPlanningResult planAllAgvPaths(List agvStatusList, boolean includeIdleAgv, List constraints) { long startTime = System.currentTimeMillis(); System.out.println("开始为 " + agvStatusList.size() + " 个CTU规划"); // 1. 构建现有剩余路径的时空占用表 System.out.println("步骤1: 构建时空占用表"); Map spaceTimeOccupancyMap = remainingPathProcessor.buildSpaceTimeOccupancyMap(agvStatusList); System.out.println("时空占用表构建完成,包含 " + spaceTimeOccupancyMap.size() + " 个占用点"); // 2. 分类处理CTU:有剩余路径的和需要新路径的 List ctuWithRemainingPaths = new ArrayList<>(); List 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 plannedPaths = new ArrayList<>(); List executingTasks = new ArrayList<>(); Map 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 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 idlePaths = planIdleAgvPathsWithConstraints( ctuNeedingNewPaths, plannedAgvIds, constraints, spaceTimeOccupancyMap ); plannedPaths.addAll(idlePaths); System.out.println("空闲CTU路径规划完成,数量: " + idlePaths.size()); } // 7. 最终碰撞检测和解决(针对同时生成的新路径) System.out.println("步骤6: 最终碰撞检测"); List conflicts = collisionDetector.detectConflicts(plannedPaths); if (!conflicts.isEmpty()) { System.out.println("发现 " + conflicts.size() + " 个碰撞,开始解决"); plannedPaths = collisionResolver.resolveConflicts(plannedPaths, conflicts, executingTasks); // 重新检测冲突 List 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 remainingCodes = new ArrayList<>(); // 从当前位置开始,获取剩余路径 List 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 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 constraints, Map occupancyMap, AGVStatus agvStatus) { // 首先尝试基本路径规划 PlannedPath basicPath = pathPlanner.planPath(startPos, endPos, constraints); if (basicPath == null) { return null; } // 找到安全的起始时间 long safeStartTime = remainingPathProcessor.findSafeStartTime( basicPath, occupancyMap, agvStatus.getPhysicalConfig() ); // 设置路径的时间信息 enhancePathWithTimeInfo(basicPath, safeStartTime, agvStatus.getPhysicalConfig()); return basicPath; } /** * 路径的时间信息 * * @param path 路径 * @param startTime 起始时间 * @param config 物理配置 */ private void enhancePathWithTimeInfo(PlannedPath path, long startTime, CTUPhysicalConfig config) { List 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 occupancyMap, AGVStatus agvStatus) { // 将新路径添加到占用表中 List codeList = path.getCodeList(); CTUPhysicalConfig config = agvStatus.getPhysicalConfig(); long currentTime = System.currentTimeMillis() / 1000; // 转换为秒 for (PathCode pathCode : codeList) { int[] coord = JsonUtils.getCoordinate(pathCode.getCode(), pathMapping); 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 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 planIdleAgvPathsWithConstraints(List agvStatusList, Map plannedAgvIds, List constraints, Map occupancyMap) { List 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 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 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 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 chargingPositions = taskExtractor.getChargingPositions(); if (!chargingPositions.isEmpty()) { for (String pos : chargingPositions) { if (!pos.equals(currentPosition)) { return pos; } } } // 其次选择待机位置 List 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 plannedPaths; private List executingTasks; public PathPlanningResult(int totalAgvs, int executingTasksCount, int plannedPathsCount, double totalPlanningTime, int conflictsDetected, boolean collisionFree, String algorithm, List plannedPaths, List 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 getPlannedPaths() { return plannedPaths; } public List 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 planTasksInParallel(List tasks, List constraints, Map spaceTimeOccupancyMap, List agvStatusList) { List allPaths = Collections.synchronizedList(new ArrayList<>()); // 按优先级排序任务 List sortedTasks = new ArrayList<>(tasks); sortedTasks.sort((t1, t2) -> Integer.compare(t2.getPriority(), t1.getPriority())); // 分批处理 List> batches = createBatches(sortedTasks, batchSize); for (int batchIndex = 0; batchIndex < batches.size(); batchIndex++) { List batch = batches.get(batchIndex); System.out.println("处理批次 " + (batchIndex + 1) + "/" + batches.size() + ", 任务数: " + batch.size()); List> futures = new ArrayList<>(); for (ExecutingTask task : batch) { Future future = executorService.submit(() -> { return planSingleTaskPath(task, constraints, spaceTimeOccupancyMap, agvStatusList); }); futures.add(future); } for (Future 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 constraints, Map spaceTimeOccupancyMap, List 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> createBatches(List tasks, int batchSize) { List> 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(); } } } }