|  |  | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         // 3.5. 检查空闲AGV是否需要让行 | 
 |  |  |         System.out.println("检查空闲AGV是否需要让行"); | 
 |  |  |         List<AGVStatus> yieldingAgvs = identifyYieldingAgvs(agvStatusList, plannedPaths); | 
 |  |  |         if (!yieldingAgvs.isEmpty()) { | 
 |  |  |             System.out.println("  发现 " + yieldingAgvs.size() + " 个需要让行的空闲AGV"); | 
 |  |  |             for (AGVStatus yieldAgv : yieldingAgvs) { | 
 |  |  |                 PlannedPath yieldPath = planYieldPath(yieldAgv, plannedPaths, spaceTimeOccupancyMap, constraints); | 
 |  |  |                 if (yieldPath != null) { | 
 |  |  |                     plannedPaths.add(yieldPath); | 
 |  |  |                     plannedAgvIds.put(yieldAgv.getAgvId(), "AVOIDING"); | 
 |  |  |                     System.out.println("  AGV " + yieldAgv.getAgvId() + " 让行路径规划成功,从 " +  | 
 |  |  |                         yieldAgv.getPosition() + " 移开"); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } else { | 
 |  |  |             System.out.println("  无需让行的AGV"); | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         // 4. 为需要新路径的CTU提取执行中任务 | 
 |  |  |         System.out.println("步骤3: 提取需要新路径的CTU任务"); | 
 |  |  |         List<ExecutingTask> newTasks = taskExtractor.extractExecutingTasks(ctuNeedingNewPaths); | 
 |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 识别需要让行的AGV | 
 |  |  |      * 检查空闲AGV是否占用了其他AGV剩余路径上的位置 | 
 |  |  |      * | 
 |  |  |      * @param agvStatusList 所有AGV状态列表 | 
 |  |  |      * @param plannedPaths  已规划的路径列表(包含剩余路径) | 
 |  |  |      * @return 需要让行的AGV列表 | 
 |  |  |      */ | 
 |  |  |     private List<AGVStatus> identifyYieldingAgvs(List<AGVStatus> agvStatusList, List<PlannedPath> plannedPaths) { | 
 |  |  |         List<AGVStatus> yieldingAgvs = new ArrayList<>(); | 
 |  |  |          | 
 |  |  |         // 收集所有已规划路径上的位置 | 
 |  |  |         Set<String> occupiedPositions = new HashSet<>(); | 
 |  |  |         for (PlannedPath path : plannedPaths) { | 
 |  |  |             if (path.getCodeList() != null) { | 
 |  |  |                 for (PathCode code : path.getCodeList()) { | 
 |  |  |                     if (code.getCode() != null) { | 
 |  |  |                         occupiedPositions.add(code.getCode()); | 
 |  |  |                     } | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         // 检查每个AGV | 
 |  |  |         for (AGVStatus agv : agvStatusList) { | 
 |  |  |             // 如果这个AGV已经有剩余路径,跳过(已在任务) | 
 |  |  |             if (agv.hasRemainingPath()) { | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |              | 
 |  |  |             // 检查AGV是否有位置信息 | 
 |  |  |             if (agv.getPosition() == null || agv.getPosition().trim().isEmpty()) { | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |              | 
 |  |  |             // 检查AGV是否是空闲状态(status=0) | 
 |  |  |             // 只要没有剩余路径且有位置,就检查是否需要让行 | 
 |  |  |             int status = agv.getStatus(); | 
 |  |  |             if (status != 0) { | 
 |  |  |                 // status=1或2(问题或忙碌)等其他状态跳过 | 
 |  |  |                 continue; | 
 |  |  |             } | 
 |  |  |              | 
 |  |  |             // 检查该AGV的位置是否在其他AGV的路径上 | 
 |  |  |             String currentPos = agv.getPosition(); | 
 |  |  |             if (occupiedPositions.contains(currentPos)) { | 
 |  |  |                 System.out.println(" CTU " + agv.getAgvId() +  | 
 |  |  |                     " (status=" + status + ") 在位置 " + currentPos + " 占用了其他CTU路径,需要让行"); | 
 |  |  |                 yieldingAgvs.add(agv); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         return yieldingAgvs; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 为让行AGV规划避让路径 | 
 |  |  |      * | 
 |  |  |      * @param yieldAgv         需要让行的AGV | 
 |  |  |      * @param existingPaths    已存在的路径列表 | 
 |  |  |      * @param occupancyMap     时空占用表 | 
 |  |  |      * @param constraints      路径约束 | 
 |  |  |      * @return 让行路径 | 
 |  |  |      */ | 
 |  |  |     private PlannedPath planYieldPath(AGVStatus yieldAgv, List<PlannedPath> existingPaths, | 
 |  |  |                                      Map<String, String> occupancyMap, List<double[]> constraints) { | 
 |  |  |         String currentPos = yieldAgv.getPosition(); | 
 |  |  |         String agvId = yieldAgv.getAgvId(); | 
 |  |  |          | 
 |  |  |         // 1. 找到安全的目标位置 | 
 |  |  |         Set<String> blockedPositions = new HashSet<>(); | 
 |  |  |         for (PlannedPath path : existingPaths) { | 
 |  |  |             if (path.getCodeList() != null) { | 
 |  |  |                 for (PathCode code : path.getCodeList()) { | 
 |  |  |                     if (code.getCode() != null) { | 
 |  |  |                         blockedPositions.add(code.getCode()); | 
 |  |  |                     } | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         // 2. 寻找合适让行目标位置 | 
 |  |  |         String targetPos = findYieldTargetPosition(currentPos, blockedPositions, yieldAgv); | 
 |  |  |          | 
 |  |  |         if (targetPos == null || targetPos.equals(currentPos)) { | 
 |  |  |             System.out.println("    AGV " + agvId + " 无法找到合适的让行位置"); | 
 |  |  |             return null; | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         // 3. 规划避让路径 | 
 |  |  |         PlannedPath yieldPath = planPathWithSpaceTimeConstraints( | 
 |  |  |             currentPos, targetPos, constraints, occupancyMap, yieldAgv | 
 |  |  |         ); | 
 |  |  |          | 
 |  |  |         if (yieldPath != null) { | 
 |  |  |             yieldPath.setAgvId(agvId); | 
 |  |  |             yieldPath.setSegId(generateSegId("AVOID", agvId, "avoiding")); | 
 |  |  |              | 
 |  |  |             // 设置路径代码信息 | 
 |  |  |             enhancePathCodesForYielding(yieldPath); | 
 |  |  |              | 
 |  |  |             // 更新占用表 | 
 |  |  |             updateSpaceTimeOccupancyMap(yieldPath, occupancyMap, yieldAgv); | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         return yieldPath; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 寻找让行的目标位置 | 
 |  |  |      */ | 
 |  |  |     private String findYieldTargetPosition(String currentPos, Set<String> blockedPositions, AGVStatus agv) { | 
 |  |  |         // 使用BFS搜索最近的空闲位置 | 
 |  |  |         final int MAX_SEARCH_DEPTH = 10; | 
 |  |  |          | 
 |  |  |         Queue<String> queue = new LinkedList<>(); | 
 |  |  |         Map<String, Integer> visited = new HashMap<>(); // 记录位置和距离 | 
 |  |  |          | 
 |  |  |         queue.offer(currentPos); | 
 |  |  |         visited.put(currentPos, 0); | 
 |  |  |          | 
 |  |  |         while (!queue.isEmpty()) { | 
 |  |  |             String pos = queue.poll(); | 
 |  |  |             int depth = visited.get(pos); | 
 |  |  |  | 
 |  |  |             if (depth >= MAX_SEARCH_DEPTH) { | 
 |  |  |                 break; | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             List<Map<String, String>> neighbors = pathPlanner.getNeighbors(pos); | 
 |  |  |             for (Map<String, String> neighbor : neighbors) { | 
 |  |  |                 String neighborPos = neighbor.get("code"); | 
 |  |  |                  | 
 |  |  |                 if (neighborPos == null || visited.containsKey(neighborPos)) { | 
 |  |  |                     continue; | 
 |  |  |                 } | 
 |  |  |                  | 
 |  |  |                 // 找到一个未被占用的位置 | 
 |  |  |                 if (!blockedPositions.contains(neighborPos)) { | 
 |  |  |                     System.out.println("  找到避让位置: " + neighborPos + " (距离=" + (depth + 1) + "步)"); | 
 |  |  |                     return neighborPos; | 
 |  |  |                 } | 
 |  |  |                  | 
 |  |  |                 // 加入队列继续搜索 | 
 |  |  |                 queue.offer(neighborPos); | 
 |  |  |                 visited.put(neighborPos, depth + 1); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         System.out.println("  未找到合适的避让位置 "); | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     private void enhancePathCodesForYielding(PlannedPath yieldPath) { | 
 |  |  |         if (yieldPath == null || yieldPath.getCodeList() == null) { | 
 |  |  |             return; | 
 |  |  |         } | 
 |  |  |          | 
 |  |  |         List<PathCode> codes = yieldPath.getCodeList(); | 
 |  |  |         for (int i = 0; i < codes.size(); i++) { | 
 |  |  |             PathCode code = codes.get(i); | 
 |  |  |             code.setActionType("0");  | 
 |  |  |             code.setPosType(null); | 
 |  |  |             code.setLev(0); | 
 |  |  |             code.setTargetPoint(i == codes.size() - 1);  | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     /** | 
 |  |  |      * 路径规划结果类 | 
 |  |  |      */ | 
 |  |  |     public static class PathPlanningResult { |