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.BasShuttle; import com.zy.asrs.service.BasShuttleService; import com.zy.asrs.utils.Utils; 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.ShuttleRunDirection; import com.zy.core.enums.SlaveType; import com.zy.core.model.ShuttleSlave; 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.properties.SlaveProperties; 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 BasShuttleService basShuttleService; @Autowired private NavigateUtils navigateUtils; @Autowired private NavigateMapUtils navigateMapUtils; @Autowired private NavigateMapData navigateMapData; @Autowired private ConfigService configService; @Autowired private SlaveProperties slaveProperties; @Autowired private ShuttleDispatchUtils shuttleDispatchUtils; public synchronized List getStartToTargetCommands(String startLocNo, String endLocNo, Integer mapType, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) { return getStartToTargetCommands(startLocNo, endLocNo, mapType, null, assignCommand, shuttleThread); } public synchronized List getStartToTargetCommands(String startLocNo, String endLocNo, Integer mapType, List whites, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) { ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { return null; } Integer shuttleNo = shuttleProtocol.getShuttleNo(); //获取小车移动速度 Integer runSpeed = Optional.ofNullable(basShuttleService.selectOne(new EntityWrapper().eq("shuttle_no", shuttleNo)).getRunSpeed()).orElse(1000); List nodeList = navigateUtils.calc(startLocNo, endLocNo, mapType, 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; } 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()); } List commands = new ArrayList<>(); //获取分段路径 ArrayList> data = navigateUtils.getSectionPath(nodeList); //将每一段路径分成command指令 for (ArrayList nodes : data) { //开始路径 NavigateNode startPath = nodes.get(0); //目标路径 NavigateNode endPath = nodes.get(nodes.size() - 1); Integer allDistance = navigateUtils.getCurrentPathAllDistance(nodes);//计算当前路径行走总距离 //通过xy坐标小车二维码 String startCodeNum = NavigatePositionConvert.xyToPosition(startPath.getX(), startPath.getY(), startPath.getZ()); //通过xy坐标小车二维码 String distCodeNum = NavigatePositionConvert.xyToPosition(endPath.getX(), endPath.getY(), endPath.getZ()); //获取移动命令 ShuttleCommand command = shuttleThread.getMoveCommand(assignCommand.getDeviceTaskNo(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed, nodes); command.setNodes(nodes);//将行走节点添加到每一步命令中 commands.add(command); } assignCommand.setNodes(allNode);//当前任务所占用的节点list boolean result = navigateMapUtils.writeNavigateNodeToRedisMap(Utils.getLev(startLocNo), shuttleNo, lockNode, true);//锁定路径 if (!result) { News.error("{} dash {} can't lock path!", startLocNo, endLocNo); shuttleThread.offerSystemMsg("{} dash {} can't lock path!", startLocNo, endLocNo); return null;//路径锁定失败 } return commands; } public synchronized List shuttleInOutLiftCommand(String startLocNo, String endLocNo, Integer mapType, ShuttleAssignCommand assignCommand, ShuttleThread shuttleThread) { NavigateNode startNode = NavigatePositionConvert.locNoToNode(startLocNo); NavigateNode endNode = NavigatePositionConvert.locNoToNode(endLocNo); List unlockPath = new ArrayList<>(); unlockPath.add(startNode); unlockPath.add(endNode); ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { return null; } Integer shuttleNo = shuttleProtocol.getShuttleNo(); //所使用的路径进行锁定/解锁 boolean lockResult = navigateMapUtils.writeNavigateNodeToRedisMap(Utils.getLev(endLocNo), shuttleProtocol.getShuttleNo(), unlockPath, false);//所使用的路径进行解锁 if (!lockResult) { News.error("{} dash {} can't find unlock path!", startLocNo, endLocNo); shuttleThread.offerSystemMsg("{} dash {} can't find unlock path!", startLocNo, endLocNo); return null;//解锁失败 } //获取小车移动速度 Integer runSpeed = Optional.ofNullable(basShuttleService.selectOne(new EntityWrapper().eq("shuttle_no", shuttleNo)).getRunSpeed()).orElse(1000); List nodeList = navigateUtils.calc(startLocNo, endLocNo, mapType, Utils.getShuttlePoints(shuttleNo, Utils.getLev(startLocNo)), null); 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; } List allNode = new ArrayList<>(); for (NavigateNode node : nodeList) { allNode.add(node.clone()); } List commands = new ArrayList<>(); //获取分段路径 ArrayList> data = navigateUtils.getSectionPath(nodeList); //将每一段路径分成command指令 for (ArrayList nodes : data) { //开始路径 NavigateNode startPath = nodes.get(0); //目标路径 NavigateNode endPath = nodes.get(nodes.size() - 1); Integer allDistance = navigateUtils.getCurrentPathAllDistance(nodes);//计算当前路径行走总距离 //通过xy坐标小车二维码 String startCodeNum = NavigatePositionConvert.xyToPosition(startPath.getX(), startPath.getY(), startPath.getZ()); //通过xy坐标小车二维码 String distCodeNum = NavigatePositionConvert.xyToPosition(endPath.getX(), endPath.getY(), endPath.getZ()); //获取移动命令 ShuttleCommand command = shuttleThread.getMoveCommand(assignCommand.getDeviceTaskNo(), startCodeNum, distCodeNum, allDistance, ShuttleRunDirection.get(startPath.getDirection()).id.intValue(), runSpeed, nodes); command.setNodes(nodes);//将行走节点添加到每一步命令中 commands.add(command); } assignCommand.setNodes(allNode);//当前任务所占用的节点list 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; } //检测障碍物车 public synchronized boolean checkObstacle(String locNo, List whiteShuttles) { 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 false;//内圈中无车 } //获取外圈节点 List outerNodes = getOuterNodes(locNo, outerCircle, whiteShuttlePointList, innerNodes); //将内圈节点中障碍小车调离 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; } int nextInt = new Random().nextInt(outerNodes.size()); NavigateNode randomNode = outerNodes.get(nextInt); String randomLocNo = NavigatePositionConvert.nodeToLocNo(randomNode); shuttleDispatchUtils.dispatchShuttle(null, randomLocNo, shuttleNo); } return true;//内圈中有车 } //搜索节点内的小车编号 private List findNodesCar(List nodes) { List list = new ArrayList<>(); HashMap carMap = new HashMap<>(); for (ShuttleSlave slave : slaveProperties.getShuttle()) { ShuttleThread shuttleThread = (ShuttleThread) SlaveConnection.get(SlaveType.Shuttle, slave.getId()); if (shuttleThread == null) { continue; } ShuttleProtocol shuttleProtocol = shuttleThread.getStatus(); if (shuttleProtocol == null) { continue; } String currentLocNo = shuttleProtocol.getCurrentLocNo(); if (currentLocNo == null) { continue; } carMap.put(currentLocNo, slave.getId()); } 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 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]); int[][] map = navigateMapData.getDataFromRedis(lev, NavigationMapType.NORMAL.id, null, whiteShuttlePointList); int nodeValue = map[pointArr[0]][pointArr[1]]; currentNode.setNodeValue(nodeValue); for (int i = 0; i < outerCircle; i++) { int idx = i + 1; List list = extend_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; } 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); int[][] map = navigateMapData.getDataFromRedis(lev, NavigationMapType.NORMAL.id, null, whiteShuttlePointList); int nodeValue = map[pointArr[0]][pointArr[1]]; currentNode.setNodeValue(nodeValue); for (int i = 0; i < innerCircle; i++) { int idx = i + 1; List list = extend_nodes(map, currentNode, idx); if (list.isEmpty()) { continue; } innerNodes.addAll(list); } return innerNodes; } private List extend_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); 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); 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); 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); 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); 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); 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); 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); nodes.add(node); } } }else { throw new CoolException("异常地图方向"); } return nodes; } private boolean is_valid(int[][] map, int x, int y) { if (x < 0 || x >= map.length || y < 0 || y >= map[0].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; // } }