package com.zy.common.utils; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.core.common.SpringUtils; import com.core.exception.CoolException; import com.zy.asrs.entity.DeviceConfig; import com.zy.asrs.service.DeviceConfigService; import com.zy.asrs.utils.Utils; import com.zy.common.model.MapNode; import com.zy.common.model.NavigateNode; import com.zy.common.model.enums.NavigationMapType; import com.zy.core.News; import com.zy.core.cache.SlaveConnection; import com.zy.core.dispatcher.ShuttleDispatchUtils; import com.zy.core.enums.MapNodeType; import com.zy.core.enums.RedisKeyType; import com.zy.core.enums.ShuttleRunDirection; import com.zy.core.enums.SlaveType; import com.zy.core.model.command.ShuttleAssignCommand; import com.zy.core.model.command.ShuttleCommand; import com.zy.core.model.protocol.ShuttleProtocol; import com.zy.core.thread.ShuttleThread; import com.zy.system.entity.Config; import com.zy.system.service.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.*; /** * 四向车操作工具类 * 此工具类只要作用于获取小车的各类命令List,移动命令、搬运货物命令、复位命令等。如命令为移动命令,还将返回行走路径List */ @Component public class ShuttleOperaUtils { @Autowired private NavigateUtils navigateUtils; @Autowired private NavigateMapData navigateMapData; @Autowired private ConfigService configService; @Autowired private ShuttleDispatchUtils shuttleDispatchUtils; @Autowired private DeviceConfigService deviceConfigService; @Autowired private RedisUtil redisUtil; public synchronized List getStartToTargetCommands(String startLocNo, String endLocNo, List mapTypes, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) { return getStartToTargetCommands(startLocNo, endLocNo, mapTypes, null, assignCommand, shuttleThread, "move"); } public synchronized List getStartToTargetCommands(String startLocNo, String endLocNo, List mapTypes, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread, String moveType) { return getStartToTargetCommands(startLocNo, endLocNo, mapTypes, null, assignCommand, shuttleThread, moveType); } public synchronized List getStartToTargetCommands(String startLocNo, String endLocNo, List mapTypes, List whites, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread, String moveType) { ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { return null; } Integer shuttleNo = shuttleProtocol.getShuttleNo(); //获取小车移动速度 Integer runSpeed = 1000; Object speedObj = redisUtil.get(RedisKeyType.SHUTTLE_SPEED_MAP.key); if (speedObj != null) { HashMap shuttleSpeedMap = (HashMap) speedObj; Integer speed = shuttleSpeedMap.get(shuttleNo); if (speed != null) { runSpeed = speed; } } long calcStartTime = System.currentTimeMillis(); List nodeList = navigateUtils.calc(startLocNo, endLocNo, mapTypes, Utils.getShuttlePoints(shuttleNo, Utils.getLev(startLocNo)), whites); if (nodeList == null) { News.error("{} dash {} can't find navigate path!", startLocNo, endLocNo); shuttleThread.offerSystemMsg("{} dash {} can't find navigate path!", startLocNo, endLocNo); return null; } News.info("[RCS Debug] Calc path time:{}", (System.currentTimeMillis() - calcStartTime)); List allNode = new ArrayList<>(); List lockNode = new ArrayList<>(); for (NavigateNode node : nodeList) { allNode.add(node.clone()); if (whites != null) { boolean flag = false; for (int[] white : whites) { if (white[0] == node.getX() && white[1] == node.getY()) { flag = true; break; } } if (flag) {//白名单跳过锁定 continue; } } lockNode.add(node.clone()); } long startTime = System.currentTimeMillis(); List commands = new ArrayList<>(); //获取分段路径 List> data = navigateUtils.getSectionPath(nodeList); long endTime = System.currentTimeMillis(); News.info("[RCS Debug] getSection path time:{}", (endTime - startTime)); long startGetSectionCommandTime = System.currentTimeMillis(); //将每一段路径分成command指令 for (int i = 0; i < data.size(); i++) { long startGetStartAndEndPathTime = System.currentTimeMillis(); List nodes = data.get(i); //开始路径 NavigateNode startPath = nodes.get(0); //目标路径 NavigateNode endPath = nodes.get(nodes.size() - 1); News.info("[RCS Debug] getStartAndEndPath idx:{} time:{}", i, (System.currentTimeMillis() - startGetStartAndEndPathTime)); long startAllDistanceTime = System.currentTimeMillis(); Integer allDistance = navigateUtils.getCurrentPathAllDistance(nodes);//计算当前路径行走总距离 News.info("[RCS Debug] getAllDistance idx:{} time:{}", i, (System.currentTimeMillis() - startAllDistanceTime)); long startGetPosition = System.currentTimeMillis(); //通过xy坐标小车二维码 String startCodeNum = NavigatePositionConvert.xyToPosition(startPath.getX(), startPath.getY(), startPath.getZ()); //通过xy坐标小车二维码 String distCodeNum = NavigatePositionConvert.xyToPosition(endPath.getX(), endPath.getY(), endPath.getZ()); News.info("[RCS Debug] xyToPosition idx:{} time:{}", i, (System.currentTimeMillis() - startGetPosition)); long startGetMoveCommandTime = System.currentTimeMillis(); //获取移动命令 ShuttleCommand command = shuttleThread.getMoveCommand(assignCommand.getDeviceTaskNo(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed, nodes); News.info("[RCS Debug] getMoveCommand idx:{} time:{}", i, (System.currentTimeMillis() - startGetMoveCommandTime)); if (i + 1 == data.size()) { long startGetInOutLiftTime = System.currentTimeMillis(); if (moveType.equals("inLift")) { command = shuttleThread.getMoveLiftCommand(assignCommand.getDeviceTaskNo(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed, nodes, true); }else if (moveType.equals("outLift")) { command = shuttleThread.getMoveLiftCommand(assignCommand.getDeviceTaskNo(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed, nodes, false); } News.info("[RCS Debug] getInOutLiftTime idx:{} time:{}", i, (System.currentTimeMillis() - startGetInOutLiftTime)); } command.setNodes(nodes);//将行走节点添加到每一步命令中 commands.add(command); } News.info("[RCS Debug] getSectionCommand path time:{}", (System.currentTimeMillis() - startGetSectionCommandTime)); assignCommand.setNodes(allNode);//当前任务所占用的节点list assignCommand.setMapTypes(mapTypes); //小车移动连续下发指令 boolean shuttleMoveCommandsContinuously = false; Object object = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); if (object != null) { HashMap systemConfigMap = (HashMap) object; String shuttleMoveCommandsContinuouslyConfig = systemConfigMap.get("shuttleMoveCommandsContinuously"); if (shuttleMoveCommandsContinuouslyConfig != null && shuttleMoveCommandsContinuouslyConfig.equals("Y")) { shuttleMoveCommandsContinuously = true; } } assignCommand.setShuttleMoveCommandsContinuously(shuttleMoveCommandsContinuously); News.info("{}任务,{}小车,{} - {} 路径命令包计算成功,耗时:{}ms", assignCommand.getTaskNo(), shuttleProtocol.getShuttleNo(), startLocNo, endLocNo, System.currentTimeMillis() - startTime); return commands; } /** * 获取充电命令 */ public synchronized List getShuttleChargeCommand(ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread, Boolean openCharge) { List commands = new ArrayList<>(); //获取充电命令 ShuttleCommand command = shuttleThread.getChargeCommand(assignCommand.getDeviceTaskNo(), openCharge); commands.add(command); return commands; } /** * 获取顶升命令 */ public synchronized List getShuttleLiftCommand(ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread, Boolean lift) { List commands = new ArrayList<>(); //获取顶升命令 ShuttleCommand command = shuttleThread.getLiftCommand(assignCommand.getDeviceTaskNo(), lift); commands.add(command); return commands; } /** * 检测障碍物车 * @return 0:无障碍 1:有障碍调度成功 2:有障碍调度失败 */ public synchronized int checkObstacle(String locNo, List whiteShuttles, List whiteNodes) { int innerCircle = 0; int outerCircle = 3; Config avoidInnerCircleConfig = configService.selectOne(new EntityWrapper().eq("code", "avoidInnerCircle")); if (avoidInnerCircleConfig != null) { innerCircle = Integer.parseInt(avoidInnerCircleConfig.getValue()); } Config avoidOuterCircleConfig = configService.selectOne(new EntityWrapper().eq("code", "avoidOuterCircle")); if (avoidOuterCircleConfig != null) { outerCircle = Integer.parseInt(avoidOuterCircleConfig.getValue()); } ArrayList whiteShuttlePointList = new ArrayList<>(); for (Integer shuttle : whiteShuttles) { //获取四向穿梭车线程 ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, shuttle); if (shuttleThread == null) { continue; } ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { continue; } String currentLocNo = shuttleProtocol.getCurrentLocNo(); int[] shuttleArr = NavigatePositionConvert.positionToXY(currentLocNo); whiteShuttlePointList.add(shuttleArr); } //获取内圈节点 List innerNodes = getInnerNodes(locNo, innerCircle, whiteShuttlePointList); List nodesCar = findNodesCar(innerNodes); if (nodesCar.isEmpty()) { return 0;//内圈中无车 } //获取外圈节点 List outerNodes = getOuterNodes(locNo, outerCircle, whiteShuttlePointList, innerNodes, whiteNodes); //将内圈节点中障碍小车调离 for (Integer shuttleNo : nodesCar) { ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, shuttleNo); if (shuttleThread == null) { continue; } ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { continue; } if (!shuttleThread.isIdle()) { continue; } String targetLocNo = null; for (NavigateNode node : outerNodes) { String dispatchLocNo = NavigatePositionConvert.nodeToLocNo(node); //获取内圈节点 List avoidInnerNodes = getInnerNodes(dispatchLocNo, innerCircle, new ArrayList<>()); //计算内圈是否有小车 List avoidNodesCar = findNodesCar(avoidInnerNodes); if (!avoidNodesCar.isEmpty()) { continue; } targetLocNo = dispatchLocNo; break; } if (targetLocNo == null) { continue; } boolean dispatched = shuttleDispatchUtils.dispatchShuttle(null, targetLocNo, shuttleNo); return dispatched ? 1 : 2; } return 2;//内圈中有车 } private HashMap findCarMap() { HashMap carMap = new HashMap<>(); List shuttleList = deviceConfigService.selectList(new EntityWrapper() .eq("device_type", String.valueOf(SlaveType.Shuttle))); for (DeviceConfig device : shuttleList) { ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, device.getDeviceNo()); if (shuttleThread == null) { continue; } ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { continue; } String currentLocNo = shuttleProtocol.getCurrentLocNo(); if (currentLocNo == null) { continue; } carMap.put(currentLocNo, device.getDeviceNo()); } return carMap; } //搜索节点内的小车编号 private List findNodesCar(List nodes) { List list = new ArrayList<>(); HashMap carMap = findCarMap(); for (NavigateNode node : nodes) { String locNo = NavigatePositionConvert.nodeToLocNo(node); if (carMap.containsKey(locNo)) { list.add(carMap.get(locNo)); } } return list; } private List getOuterNodes(String locNo, int outerCircle, List whiteShuttlePointList, List innerNodes, List whiteNodes) { List outerNodes = new ArrayList<>(); List outerNodesTmp = new ArrayList<>(); int lev = Utils.getLev(locNo); int[] pointArr = NavigatePositionConvert.positionToXY(locNo); NavigateNode currentNode = new NavigateNode(pointArr[0], pointArr[1]); currentNode.setZ(lev); List> lists = navigateMapData.getJsonData(lev, NavigationMapType.getMapTypes(NavigationMapType.NONE), null, null);//获取完整地图 int[][] map = navigateMapData.parseJsonDataArr(lists); int nodeValue = map[pointArr[0]][pointArr[1]]; currentNode.setNodeValue(nodeValue); for (int i = 0; i < outerCircle; i++) { int idx = i + 1; List list = extend_outer_nodes(map, currentNode, idx); if (list.isEmpty()) { continue; } outerNodesTmp.addAll(list); } for (NavigateNode node : outerNodesTmp) { boolean flag = false; for (NavigateNode innerNode : innerNodes) { if (node.getX() == innerNode.getX() && node.getY() == innerNode.getY()) { flag = true; break; } } if (flag) { continue; } for (NavigateNode whiteNode : whiteNodes) { if (node.getX() == whiteNode.getX() && node.getY() == whiteNode.getY()) { flag = true; break; } } if (flag) { continue; } outerNodes.add(node); } return outerNodes; } private List getInnerNodes(String locNo, int innerCircle, List whiteShuttlePointList) { List innerNodes = new ArrayList<>(); int lev = Utils.getLev(locNo); int[] pointArr = NavigatePositionConvert.positionToXY(locNo); NavigateNode currentNode = new NavigateNode(pointArr[0], pointArr[1]); currentNode.setZ(lev); innerNodes.add(currentNode); List> lists = navigateMapData.getJsonData(lev, NavigationMapType.getMapTypes(NavigationMapType.NONE), null, null);//获取完整地图 int[][] map = navigateMapData.parseJsonDataArr(lists); int nodeValue = map[pointArr[0]][pointArr[1]]; currentNode.setNodeValue(nodeValue); for (int i = 0; i < innerCircle; i++) { int idx = i + 1; List list = extend_inner_nodes(map, currentNode, idx); if (list.isEmpty()) { continue; } for (NavigateNode node : list) { boolean flag = false; for (int[] shuttlePoint : whiteShuttlePointList) { if(node.getX() == shuttlePoint[0] && node.getY() == shuttlePoint[1]) { flag = true; break; } } if(flag) { continue; } innerNodes.add(node); } } return innerNodes; } private List extend_inner_nodes(int[][] map, NavigateNode startNode, int innerCircleIdx) { //默认地图母轨方向x String mapDirection = "x"; ConfigService configService = SpringUtils.getBean(ConfigService.class); if (configService != null) { Config config = configService.selectOne(new EntityWrapper() .eq("code", "direction_map") .eq("status", 1)); if (config != null) { mapDirection = config.getValue(); } } ArrayList nodes = new ArrayList<>(); int x = startNode.getX(); int y = startNode.getY(); int z = startNode.getZ(); if (mapDirection.equals("x")) {//母轨x方向 if (map[x][y] == MapNodeType.MAIN_PATH.id) { //母轨才能进行上下移动 if (is_valid(map, x + innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x + innerCircleIdx, y); node.setNodeValue(map[x + innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x - innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x - innerCircleIdx, y); node.setNodeValue(map[x - innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.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.CHARGE.id || map[x][y] == MapNodeType.LIFT.id) { //子轨和母轨、小车可走输送线、充电桩、提升机才能进行左右移动 if (is_valid(map, x, y + innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y + innerCircleIdx); node.setNodeValue(map[x][y + innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x, y - innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y - innerCircleIdx); node.setNodeValue(map[x][y - innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } } }else if (mapDirection.equals("y")) {//母轨y方向 if (map[x][y] == MapNodeType.MAIN_PATH.id) { //母轨才能进行左右移动 if (is_valid(map, x, y + innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y + innerCircleIdx); node.setNodeValue(map[x][y + innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x, y - innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y - innerCircleIdx); node.setNodeValue(map[x][y - innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.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.CHARGE.id || map[x][y] == MapNodeType.LIFT.id) { //子轨和母轨、小车可走输送线、充电桩、提升机才能进行上下移动 if (is_valid(map, x + innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x + innerCircleIdx, y); node.setNodeValue(map[x + innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x - innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x - innerCircleIdx, y); node.setNodeValue(map[x - innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } } }else { throw new CoolException("异常地图方向"); } return nodes; } private List extend_outer_nodes(int[][] map, NavigateNode startNode, int innerCircleIdx) { ArrayList nodes = new ArrayList<>(); int x = startNode.getX(); int y = startNode.getY(); int z = startNode.getZ(); if (is_valid(map, x + innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x + innerCircleIdx, y); node.setNodeValue(map[x + innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x - innerCircleIdx, y)) { NavigateNode node = new NavigateNode(x - innerCircleIdx, y); node.setNodeValue(map[x - innerCircleIdx][y]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x, y + innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y + innerCircleIdx); node.setNodeValue(map[x][y + innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } if (is_valid(map, x, y - innerCircleIdx)) { NavigateNode node = new NavigateNode(x, y - innerCircleIdx); node.setNodeValue(map[x][y - innerCircleIdx]); node.setZ(z); if (node.getNodeValue().equals(startNode.getNodeValue())) { nodes.add(node); } } return nodes; } private boolean is_valid(int[][] map, int x, int y) { if (x < 0 || x >= map.length || y < 0 || y >= map[1].length) { return false; } // 如果结点的位置小于0,则不合法 if (map[x][y] < 0) { return false; } //以上情况都没有则合法 return true; } // private boolean checkSimilarityPath(Motion motion, ShuttleAssignCommand assignCommand) { // String movePath = motion.getMovePath(); // if (Cools.isEmpty(movePath)) { // return false; // } // // Double similarityRef = 0.9D; // Dict similarityRefDict = dictService.getOne(new LambdaQueryWrapper() // .eq(Dict::getFlag, "similarityRef") // .eq(Dict::getStatus, 1)); // if (similarityRefDict != null) { // similarityRef = Double.parseDouble(similarityRefDict.getValue()); // } // // List originPath = JSON.parseArray(movePath, NavigateNode.class); // List finalPath = assignCommand.getNodes(); // // if (finalPath == null) { // return false; // } // // Double similarity = navigateUtils.similarityPath(originPath, finalPath); // if (similarity <= similarityRef) { // Object object = redisUtil.get(DeviceRedisConstant.SIMILARITY_TIMES + motion.getTaskNo()); // if (object == null) { // redisUtil.set(DeviceRedisConstant.SIMILARITY_TIMES + motion.getTaskNo(), System.currentTimeMillis(), 60 * 60 * 24); // }else { // long similarityTimeoutRef = 20L;//默认超时20s // Dict similarityTimeoutDict = dictService.getOne(new LambdaQueryWrapper() // .eq(Dict::getFlag, "similarityTimeout") // .eq(Dict::getStatus, 1)); // if (similarityTimeoutDict != null) { // similarityTimeoutRef = Long.parseLong(similarityTimeoutDict.getValue()); // } // // long recordTime = Long.parseLong(object.toString()); // if (System.currentTimeMillis() - recordTime > (60 * similarityTimeoutRef)) { // //超时,直接放行 // return true; // } // } // News.error("{} dash {} path similarity mismatch!", motion.getOrigin(), motion.getTarget()); // return false; // } // // return true; // } }