|  |  | 
 |  |  | package com.zy.common.utils; | 
 |  |  |  | 
 |  |  | import com.alibaba.fastjson.JSON; | 
 |  |  | import com.baomidou.mybatisplus.mapper.EntityWrapper; | 
 |  |  | import com.core.common.SpringUtils; | 
 |  |  | import com.core.exception.CoolException; | 
 |  |  | import com.zy.common.model.NavigateNode; | 
 |  |  | import com.zy.common.model.enums.NavigationMapType; | 
 |  |  | import com.zy.core.enums.MapNodeType; | 
 |  |  | import com.zy.core.model.PythonResult; | 
 |  |  | import com.zy.system.entity.Config; | 
 |  |  | import com.zy.system.service.ConfigService; | 
 |  |  |  | 
 |  |  | import java.util.ArrayList; | 
 |  |  | import java.util.List; | 
 |  |  | import java.util.PriorityQueue; | 
 |  |  | import java.io.BufferedReader; | 
 |  |  | import java.io.IOException; | 
 |  |  | import java.io.InputStreamReader; | 
 |  |  | import java.util.*; | 
 |  |  |  | 
 |  |  | /** | 
 |  |  |  * 四向库核心 | 
 |  |  |  * A*算法实现类 | 
 |  |  |  */ | 
 |  |  | public class NavigateSolution { | 
 |  |  |  | 
 |  |  |     // -1 -> 墙壁, 1 -> 起点  2 -> 终点  3-> 母轨  4->站点 | 
 |  |  |     // -1 -> 墙壁, 0 -> 货位, 1 -> 起点  2 -> 终点  3-> 母轨  4->输送站点  5->充电桩 6->小车可走输送站点  66->小车  67->提升机 | 
 |  |  |  | 
 |  |  |     int[][] map = {{}}; | 
 |  |  |  | 
 |  |  |     public NavigateSolution(Integer mapType, Integer lev, List<int[]> whitePoints, List<int[]> shuttlePoints) { | 
 |  |  |     public NavigateSolution(List<NavigationMapType> mapTypes, Integer lev, List<int[]> whitePoints, List<int[]> shuttlePoints) { | 
 |  |  |         //载入地图指定层高地图 | 
 |  |  |         NavigateMapData navigateMapData = SpringUtils.getBean(NavigateMapData.class); | 
 |  |  |         int[][] data = navigateMapData.getDataFromRedis(lev, mapType, whitePoints, shuttlePoints); | 
 |  |  |         NavigateMapData mapData = SpringUtils.getBean(NavigateMapData.class); | 
 |  |  |         int[][] data = mapData.getDataFromRedis(lev, mapTypes, whitePoints, shuttlePoints); | 
 |  |  |         if (data == null) { | 
 |  |  |             data = navigateMapData.getData(lev, mapType, whitePoints, shuttlePoints); | 
 |  |  |             throw new CoolException("地图未载入!"); | 
 |  |  |         } | 
 |  |  |         this.map = data; | 
 |  |  |     } | 
 |  |  | 
 |  |  |     public PriorityQueue<NavigateNode> Open = new PriorityQueue<NavigateNode>(); | 
 |  |  |     //Close表用普通的数组 | 
 |  |  |     public ArrayList<NavigateNode> Close = new ArrayList<NavigateNode>(); | 
 |  |  |     //Exist表用来存放已经出现过的结点。 | 
 |  |  |     public ArrayList<NavigateNode> Exist = new ArrayList<NavigateNode>(); | 
 |  |  |     //用来存放已经出现过的结点。 | 
 |  |  |     Map<String, Integer> bestGMap = new HashMap<>(); | 
 |  |  |  | 
 |  |  |     public NavigateNode astarSearch(NavigateNode start, NavigateNode end) { | 
 |  |  |     public String astarSearchPython(NavigateNode start, NavigateNode end, String pythonScriptPath) { | 
 |  |  |         String maps = JSON.toJSONString(map); | 
 |  |  |         String startStr = start.getX() + "-" + start.getY(); | 
 |  |  |         String targetStr = end.getX() + "-" + end.getY(); | 
 |  |  |  | 
 |  |  |         //默认地图母轨方向x | 
 |  |  |         String mapDirection = "x"; | 
 |  |  |         ConfigService configService = SpringUtils.getBean(ConfigService.class); | 
 |  |  |         if (configService != null) { | 
 |  |  |             Config config = configService.selectOne(new EntityWrapper<Config>() | 
 |  |  |                     .eq("code", "direction_map") | 
 |  |  |                     .eq("status", 1)); | 
 |  |  |             if (config != null) { | 
 |  |  |                 mapDirection = config.getValue(); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         ProcessBuilder processBuilder = new ProcessBuilder("python", pythonScriptPath, maps, startStr, targetStr, mapDirection); | 
 |  |  |         processBuilder.redirectErrorStream(true); | 
 |  |  |  | 
 |  |  |         try { | 
 |  |  |             Process process = processBuilder.start(); | 
 |  |  |  | 
 |  |  |             // 读取Python脚本的输出 | 
 |  |  |             BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); | 
 |  |  |             String line; | 
 |  |  |             StringBuilder builder = new StringBuilder(); | 
 |  |  |             while ((line = reader.readLine()) != null) { | 
 |  |  |                 builder.append(line); | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             // 等待Python脚本执行完成 | 
 |  |  |             int exitCode = process.waitFor(); | 
 |  |  |             if (exitCode != 0) { | 
 |  |  |                 System.out.println("Python script exited with error code: " + exitCode); | 
 |  |  |                 return null; | 
 |  |  |             } | 
 |  |  |             reader.close(); | 
 |  |  |  | 
 |  |  |             if (builder.length() <= 0) { | 
 |  |  |                 return null; | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             PythonResult result = JSON.parseObject(builder.toString(), PythonResult.class); | 
 |  |  |             if (result.getCalcResult() != 200) { | 
 |  |  |                 return null; | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             String path = result.getPath(); | 
 |  |  |             return path; | 
 |  |  |         } catch (IOException | InterruptedException e) { | 
 |  |  |             e.printStackTrace(); | 
 |  |  |         } | 
 |  |  |         return null; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public NavigateNode astarSearchJava(NavigateNode start, NavigateNode end) { | 
 |  |  |         //默认地图母轨方向x | 
 |  |  |         String mapDirection = "x"; | 
 |  |  |         ConfigService configService = SpringUtils.getBean(ConfigService.class); | 
 |  |  |         if (configService != null) { | 
 |  |  |             Config config = configService.selectOne(new EntityWrapper<Config>() | 
 |  |  |                     .eq("code", "direction_map") | 
 |  |  |                     .eq("status", 1)); | 
 |  |  |             if (config != null) { | 
 |  |  |                 mapDirection = config.getValue(); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         //把第一个开始的结点加入到Open表中 | 
 |  |  |         this.Open.add(start); | 
 |  |  |         //把出现过的结点加入到Exist表中 | 
 |  |  |         this.Exist.add(start); | 
 |  |  |         //主循环 | 
 |  |  |         while (Open.size() > 0) { | 
 |  |  |             //取优先队列顶部元素并且把这个元素从Open表中删除 | 
 |  |  |             NavigateNode current_node = Open.poll(); | 
 |  |  |  | 
 |  |  |             if (current_node.getX() == end.getX() && current_node.getY() == end.getY()) {//找到目标结点就返回 | 
 |  |  |                 return current_node; | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             //将这个结点加入到Close表中 | 
 |  |  |             Close.add(current_node); | 
 |  |  |             //对当前结点进行扩展,得到一个四周结点的数组 | 
 |  |  |             ArrayList<NavigateNode> neighbour_node = extend_current_node(current_node); | 
 |  |  |             ArrayList<NavigateNode> neighbour_node = extend_current_node(current_node, mapDirection); | 
 |  |  |             //对这个结点遍历,看是否有目标结点出现 | 
 |  |  |             for (NavigateNode node : neighbour_node) { | 
 |  |  |                 // G + H + E (对启发函数增加去拐点方案calcNodeExtraCost) | 
 |  |  |                 int gCost = calcNodeCost(current_node, node) + calcNodeExtraCost(current_node, node, end); | 
 |  |  |                 if (node.getX() == end.getX() && node.getY() == end.getY()) {//找到目标结点就返回 | 
 |  |  |                     //init_node操作把这个邻居结点的父节点设置为当前结点 | 
 |  |  |                     //并且计算出G, F, H等值 | 
 |  |  |                     node.init_node(current_node, end); | 
 |  |  |                     return node; | 
 |  |  |                 } | 
 |  |  |                 int gCost = calcNodeExtraCost(current_node, node, end); | 
 |  |  |  | 
 |  |  |                 //(对启发函数增加去拐点方案calcNodeExtraCost) | 
 |  |  |                 if (is_exist(node)) { | 
 |  |  |                     if (gCost < node.getG()) { | 
 |  |  |                         node.setFather(current_node); | 
 |  |  |                         node.setG(gCost); | 
 |  |  |                         node.setF(node.getG() + node.getH()); | 
 |  |  |                     } | 
 |  |  |                 }else { | 
 |  |  |                     //没出现过的结点加入到Open表中并且设置父节点 | 
 |  |  |                     //进行计算对G, F, H 等值 | 
 |  |  |                     node.init_node(current_node, end); | 
 |  |  |                     node.setG(gCost); | 
 |  |  |                     node.setH(calcNodeCost(node, end)); | 
 |  |  |                     node.setF(node.getG() + node.getH()); | 
 |  |  |                 //进行计算对G, F, H 等值 | 
 |  |  |                 node.setG(1 + gCost); | 
 |  |  |                 node.init_node(current_node, end); | 
 |  |  |                 node.setH(calcNodeCost(node, end)); | 
 |  |  |                 node.setF(node.getG() + node.getH()); | 
 |  |  |  | 
 |  |  |                 String key = node.getX() + "_" + node.getY(); | 
 |  |  |                 Integer recordedG = bestGMap.get(key); | 
 |  |  |                 if (recordedG == null || node.getG() <= recordedG) { | 
 |  |  |                     bestGMap.put(key, node.getG()); | 
 |  |  |  | 
 |  |  |                     Open.add(node); | 
 |  |  |                     Exist.add(node); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     public ArrayList<NavigateNode> extend_current_node(NavigateNode current_node) { | 
 |  |  |     public ArrayList<NavigateNode> extend_current_node(NavigateNode current_node, String mapDirection) { | 
 |  |  |         //获取当前结点的x, y | 
 |  |  |         int x = current_node.getX(); | 
 |  |  |         int y = current_node.getY(); | 
 |  |  | 
 |  |  | //                neighbour_node.add(node); | 
 |  |  | //            } | 
 |  |  | //        } | 
 |  |  |         if (map[x][y] == 3) { | 
 |  |  |             //母轨才能进行左右移动 | 
 |  |  |             if (is_valid(x, y + 1)) | 
 |  |  |             { | 
 |  |  |                 NavigateNode node = new NavigateNode(x, y + 1); | 
 |  |  |                 neighbour_node.add(node); | 
 |  |  |             } | 
 |  |  |             if (is_valid(x, y - 1)) | 
 |  |  |             { | 
 |  |  |                 NavigateNode node = new NavigateNode(x, y - 1); | 
 |  |  |                 neighbour_node.add(node); | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         if (map[x][y] == 0 || map[x][y] == 3 || map[x][y] == 4 || map[x][y] == 5) { | 
 |  |  |             //子轨和母轨、输送线、充电桩才能进行上下移动 | 
 |  |  |             if (is_valid(x + 1, y)) | 
 |  |  |             { | 
 |  |  |                 NavigateNode node = new NavigateNode(x + 1, y); | 
 |  |  |                 neighbour_node.add(node); | 
 |  |  |         if (mapDirection.equals("x")) {//母轨x方向 | 
 |  |  |             if (map[x][y] == MapNodeType.MAIN_PATH.id) { | 
 |  |  |                 //母轨才能进行上下移动 | 
 |  |  |                 if (is_valid(x + 1, y)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x + 1, y); | 
 |  |  |                     node.setNodeValue(map[x + 1][y]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |                 if (is_valid(x - 1, y)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x - 1, y); | 
 |  |  |                     node.setNodeValue(map[x - 1][y]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |             if (is_valid(x - 1, y)) | 
 |  |  |             { | 
 |  |  |                 NavigateNode node = new NavigateNode(x -1, y); | 
 |  |  |                 neighbour_node.add(node); | 
 |  |  |  | 
 |  |  |             if (map[x][y] == MapNodeType.NORMAL_PATH.id || map[x][y] == MapNodeType.MAIN_PATH.id || map[x][y] == MapNodeType.CONVEYOR_CAR_GO.id || map[x][y] == MapNodeType.CONVEYOR.id || map[x][y] == MapNodeType.CHARGE.id || map[x][y] == MapNodeType.LIFT.id) { | 
 |  |  |                 //子轨和母轨、小车可走输送线、充电桩、提升机才能进行左右移动 | 
 |  |  |                 if (is_valid(x, y + 1)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x, y + 1); | 
 |  |  |                     node.setNodeValue(map[x][y + 1]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |                 if (is_valid(x, y - 1)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x, y - 1); | 
 |  |  |                     node.setNodeValue(map[x][y - 1]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } else if (mapDirection.equals("y")) {//母轨y方向 | 
 |  |  |             if (map[x][y] == MapNodeType.MAIN_PATH.id) { | 
 |  |  |                 //母轨才能进行左右移动 | 
 |  |  |                 if (is_valid(x, y + 1)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x, y + 1); | 
 |  |  |                     node.setNodeValue(map[x][y + 1]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |                 if (is_valid(x, y - 1)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x, y - 1); | 
 |  |  |                     node.setNodeValue(map[x][y - 1]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             if (map[x][y] == MapNodeType.NORMAL_PATH.id || map[x][y] == MapNodeType.MAIN_PATH.id || map[x][y] == MapNodeType.CONVEYOR_CAR_GO.id || map[x][y] == MapNodeType.CONVEYOR.id || map[x][y] == MapNodeType.CHARGE.id || map[x][y] == MapNodeType.LIFT.id) { | 
 |  |  |                 //子轨和母轨、小车可走输送线、充电桩、提升机才能进行上下移动 | 
 |  |  |                 if (is_valid(x + 1, y)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x + 1, y); | 
 |  |  |                     node.setNodeValue(map[x + 1][y]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |                 if (is_valid(x - 1, y)) { | 
 |  |  |                     NavigateNode node = new NavigateNode(x - 1, y); | 
 |  |  |                     node.setNodeValue(map[x - 1][y]); | 
 |  |  |                     neighbour_node.add(node); | 
 |  |  |                 } | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |  | 
 |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public boolean is_valid(int x, int y) { | 
 |  |  |         // 如果结点的位置小于0,则不合法 | 
 |  |  |         if (map[x][y] < 0) return false; | 
 |  |  |         for (NavigateNode node : Exist) { | 
 |  |  |             //如果结点出现过,不合法 | 
 |  |  |             if (node.getX() == x && node.getY() == y) { | 
 |  |  |                 return false; | 
 |  |  |             } | 
 |  |  |             if (is_exist(new NavigateNode(x, y))) { | 
 |  |  |                 return false; | 
 |  |  |             } | 
 |  |  |         if (x < 0 || x >= this.map.length | 
 |  |  |                 || y < 0 || y >= this.map[0].length) { | 
 |  |  |             return false; | 
 |  |  |         } | 
 |  |  |         // 如果结点的位置小于0,则不合法 | 
 |  |  |         if (map[x][y] < 0) { | 
 |  |  |             return false; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         if (map[x][y] == MapNodeType.CAR.id) {//节点是小车 | 
 |  |  |             return false; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         //以上情况都没有则合法 | 
 |  |  |         return true; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     public boolean is_exist(NavigateNode node) | 
 |  |  |     { | 
 |  |  |         for (NavigateNode exist_node : Exist) { | 
 |  |  |             if (node.getX() == exist_node.getX() && node.getY() == exist_node.getY()) { | 
 |  |  |                 return true; | 
 |  |  |             } | 
 |  |  |         } | 
 |  |  |         return false; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     //------------------A*启发函数------------------// | 
 |  |  | 
 |  |  |             return 1; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         // 拐向主轨道的点 | 
 |  |  |         if (map[nextNode.getX()][nextNode.getY()] == 3) { | 
 |  |  |             //------------------三凯独特判断------------------// | 
 |  |  |             if (nextNode.getX() == 15) { | 
 |  |  |                 return 2;//影响算法,不要在15排这个主轨道换向 | 
 |  |  |             } | 
 |  |  |             //------------------三凯独特判断------------------// | 
 |  |  |  | 
 |  |  |             return 0; | 
 |  |  |         } | 
 |  |  |  | 
 |  |  |         // 普通拐点 | 
 |  |  |         /* | 
 |  |  |         拐点判断逻辑 | 
 |  |  |         拿到父节点和下一节点 | 
 |  |  |         通过判断父节点和下一节点的x数据和y数据都不相同时,则表明当前坐标是一个拐点 | 
 |  |  |          */ | 
 |  |  |         return 3; | 
 |  |  |         return 1; | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     //------------------A*启发函数-end------------------// |