| | |
| | | import com.zy.acs.common.domain.protocol.*; |
| | | import com.zy.acs.common.domain.protocol.action.*; |
| | | import com.zy.acs.common.enums.*; |
| | | import com.zy.acs.common.utils.Base62; |
| | | import com.zy.acs.common.utils.GsonUtils; |
| | | import com.zy.acs.common.utils.RedisSupport; |
| | | import com.zy.acs.common.utils.Utils; |
| | |
| | | , (task, agvNo, sta) -> { |
| | | Long agvId = agvService.getAgvId(agvNo); |
| | | |
| | | // List<Travel> travelList = travelService.list(new LambdaQueryWrapper<Travel>() |
| | | // .eq(Travel::getAgvId, agvId) |
| | | // .eq(Travel::getState, TravelStateType.RUNNING.toString()) |
| | | // .orderByDesc(Travel::getCreateTime)); |
| | | // if (Cools.isEmpty(travelList)) { |
| | | // throw new BusinessException("[Agv: " + agvNo + "]allocate inbound failed to find travel"); |
| | | // } |
| | | // Travel travel = travelList.get(0); |
| | | // |
| | | // List<Segment> currSegments = segmentService.list(new LambdaQueryWrapper<Segment>() |
| | | // .eq(Segment::getAgvId, agvId) |
| | | // .eq(Segment::getTravelId, travel.getId()) |
| | | // ); |
| | | // load segment |
| | | Segment currSeg = segmentService.getRollerWaiting(agvId, sta.getCode(), TaskPosDto.PosType.ORI_STA); |
| | | Travel travel = travelService.findRunningTravel(agvId); |
| | | if (null == travel) { |
| | | throw new CoolException("[Agv:" + agvNo + "] allocate inbound failed: no roller waiting travel"); |
| | | } |
| | | |
| | | // curr segment |
| | | Segment currSeg = segmentService.getCurrWaitingSeg(travel.getId(), agvId); |
| | | if (null == currSeg) { |
| | | throw new BusinessException("[Agv:" + agvNo + "] allocate inbound failed: no roller waiting segment"); |
| | | throw new CoolException("[Agv:" + agvNo + "] allocate inbound failed: no waiting segment"); |
| | | } |
| | | |
| | | // get backpack lev |
| | | int backpackLev = 0; |
| | | int backpack = agvService.getBackpack(agvId); |
| | | int backpackCap = agvService.getBackpack(agvId); |
| | | List<Integer> usedBackpacks = segmentService.selectUsedBackpacks(currSeg.getTravelId(), agvId); |
| | | if (usedBackpacks.size() >= backpack) { |
| | | if (usedBackpacks.size() >= backpackCap) { |
| | | throw new CoolException("[Agv:" + agvNo + "] allocate inbound segment failed: no idle backpack to use"); |
| | | } |
| | | for (int lev = 1; lev <= backpack; lev++) { |
| | | for (int lev = 1; lev <= backpackCap; lev++) { |
| | | if (!usedBackpacks.contains(lev)) { |
| | | backpackLev = lev; |
| | | break; |
| | |
| | | return o2.getPriority() - o1.getPriority(); |
| | | } |
| | | }); |
| | | Integer backpack = agvService.getBackpack(agvId); |
| | | if (taskList.size() > backpack) { |
| | | taskList = taskList.subList(0, backpack); |
| | | Integer backpackCap = agvService.getBackpack(agvId); |
| | | if (taskList.size() > backpackCap) { |
| | | taskList = taskList.subList(0, backpackCap); |
| | | } |
| | | |
| | | AgvDetail agvDetail = agvDetailService.selectByAgvId(agvId); |
| | |
| | | |
| | | } |
| | | |
| | | if (backpackLev > backpack) { |
| | | if (backpackLev > backpackCap) { |
| | | throw new BusinessException("解析Task失败,AGV背篓已满......"); |
| | | } |
| | | |
| | |
| | | task.setAgvId(agvId); |
| | | task.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | | List<Task> lastTasks = taskService.list(new LambdaQueryWrapper<Task>().orderByDesc(Task::getId)); |
| | | task.setSeqNum(Utils.generateSeqNum(Cools.isEmpty(lastTasks)?null:lastTasks.get(0).getSeqNum())); |
| | | // task.setSeqNum(Utils.generateSeqNum(Cools.isEmpty(lastTasks)?null:lastTasks.get(0).getSeqNum())); |
| | | task.setSeqNum(Base62.encode(snowflakeIdWorker.nextId())); |
| | | task.setOriCode(agvDetail.getCode()); |
| | | task.setDestCode(endCode.getId()); |
| | | // lane |
| | |
| | | if (!lastCode.getData().equals(code.getData())) { |
| | | |
| | | // 走行路径节点 |
| | | // List<String> pathList = mapService.checkoutPath(agv.getUuid(), lastCode, code); |
| | | List<String> pathListPart = pathList.subList(pathList.indexOf(lastCode.getData()), pathList.indexOf(code.getData()) + 1); |
| | | |
| | | for (int i = 0; i < pathListPart.size(); i++) { |
| | |
| | | |
| | | // 巷道强制转弯,优先级 > workDirection |
| | | if (null != nextLaneDir) { |
| | | nextDirection = nextLaneDir; |
| | | final double oppNextLaneDir = (nextLaneDir + 180) % 360; |
| | | |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | if (!lastDirection.equals(nextLaneDir)) { |
| | | if (!lastCode.getCornerBool()) { |
| | | throw new CoolException(agvNo + "号小车进入巷道需调整方向为 " + nextDirection + "°,请推至转弯点手动调整"); |
| | | throw new CoolException(agvNo + "号小车进入巷道需调整方向为 " + nextLaneDir + "°,请推至转弯点手动调整"); |
| | | } |
| | | // turn |
| | | actionList.add(new Action( |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, nextLaneDir).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | String.valueOf(nextLaneDir), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | lastDirection = nextDirection; |
| | | lastDirection = nextLaneDir; |
| | | } |
| | | } else if (null != lastLaneDir) { |
| | | nextDirection = lastLaneDir; |
| | | |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | if (nextDirection.equals(oppNextLaneDir)) { |
| | | reverse = true; |
| | | } |
| | | nextDirection = nextLaneDir; // 防止 第一个动作一定是 turn 出问题 |
| | | } else if (null != lastLaneDir) { // turn 环形直角不应该存在于lane中 |
| | | final double oppLastLaneDir = (lastLaneDir + 180) % 360; |
| | | |
| | | if (!lastDirection.equals(lastLaneDir)) { |
| | | if (!lastCode.getCornerBool()) { |
| | | throw new CoolException(agvNo + "号小车离开巷道需调整方向为 " + nextDirection + "°,请推至转弯点手动调整"); |
| | | } |
| | | } |
| | | |
| | | if (nextDirection.equals(oppLastLaneDir)) { |
| | | reverse = true; |
| | | } |
| | | nextDirection = lastLaneDir; // 防止 第一个动作一定是 turn 出问题 |
| | | } else { |
| | | |
| | | // 如果下一个方向正好是作业方向的相反方向,则重置下一个方向为作业方向,标记 reverse = true |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, nextDirection).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, nextDirection).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | // 初始方向值补丁 |
| | | if (first) { |
| | | if (Cools.isEmpty(actionList) || !actionList.get(0).getActionType().equals(ActionTypeType.TurnCorner.val())) { |
| | | Double firstTurnDir = workDirection; |
| | | |
| | | // 巷道逻辑 |
| | | if (!laneBuilder.isInitialized()) { |
| | | throw new CoolException("lanes are not initialized"); |
| | | } |
| | | LaneDto lastLaneDto = laneBuilder.search(lastCode.getData()); |
| | | // 进入巷道角度 |
| | | Double lastLaneDir = laneService.getLaneDirection(lastLaneDto); |
| | | if (lastLaneDir != null) { |
| | | firstTurnDir = lastLaneDir; |
| | | } |
| | | |
| | | if (!lastDirection.equals(firstTurnDir)) { |
| | | if (!lastCode.getCornerBool()) { |
| | | throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整"); |
| | | } |
| | | } |
| | | |
| | | // turn |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, firstTurnDir).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(workDirection), // 动作参数 |
| | | String.valueOf(firstTurnDir), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | |
| | | lastDirection = workDirection; |
| | | lastDirection = firstTurnDir; |
| | | |
| | | } |
| | | first = false; |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, oriStaWorkDirection).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(oriStaWorkDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, destStaWorkDirection).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(destStaWorkDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | (double) MapService.calcSpinDirection(lastCode, lastDirection, chargeDirection).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(chargeDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | staReserveService.rollbackWaitingToReserved(sta, currTask, StaReserveType.OUT); |
| | | break; |
| | | } |
| | | log.info("AGV[{}] load permitted at conveyor station [{}]", protocol.getAgvNo(), sta.getStaNo()); |
| | | success = true; |
| | | break; |
| | | case DEST_STA: |
| | |
| | | staReserveService.rollbackWaitingToReserved(sta, currTask, StaReserveType.IN); |
| | | break; |
| | | } |
| | | log.info("AGV[{}] unload permitted at conveyor station [{}]", protocol.getAgvNo(), sta.getStaNo()); |
| | | success = true; |
| | | break; |
| | | default: |
| | |
| | | agv_06_down.setSerialNo(agv_06_up.getSerialNo()); |
| | | agv_06_down.setActionCode(agv_06_up.getActionCode()); |
| | | agv_06_down.setResult(success ? 1 : 0); |
| | | |
| | | redis.push(RedisConstant.AGV_PATH_DOWN_FLAG, AgvProtocol.build(protocol.getAgvNo()).setMessageBody(agv_06_down)); |
| | | } |
| | | |