| | |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.zy.acs.common.constant.RedisConstant; |
| | | import com.zy.acs.common.domain.AgvAction; |
| | | import com.zy.acs.common.domain.AgvActionItem; |
| | |
| | | import com.zy.acs.common.domain.BaseResult; |
| | | import com.zy.acs.common.domain.protocol.*; |
| | | import com.zy.acs.common.domain.protocol.action.*; |
| | | import com.zy.acs.common.enums.AgvBackpackType; |
| | | import com.zy.acs.common.enums.AgvCompleteType; |
| | | import com.zy.acs.common.enums.AgvDirectionType; |
| | | import com.zy.acs.common.enums.AgvSpeedType; |
| | | import com.zy.acs.common.enums.*; |
| | | import com.zy.acs.common.utils.GsonUtils; |
| | | import com.zy.acs.common.utils.RedisSupport; |
| | | import com.zy.acs.common.utils.Utils; |
| | |
| | | import com.zy.acs.manager.common.domain.param.HandlerPublishParam; |
| | | import com.zy.acs.manager.common.exception.BusinessException; |
| | | import com.zy.acs.manager.core.domain.AgvBackpackDto; |
| | | import com.zy.acs.manager.core.domain.Lane; |
| | | import com.zy.acs.manager.core.domain.LaneDto; |
| | | import com.zy.acs.manager.core.domain.TaskPosDto; |
| | | import com.zy.acs.manager.core.integrate.conveyor.ConveyorStationService; |
| | | import com.zy.acs.manager.core.service.astart.MapDataDispatcher; |
| | | import com.zy.acs.manager.manager.controller.param.OpenBusSubmitParam; |
| | | import com.zy.acs.manager.manager.entity.*; |
| | |
| | | @Autowired |
| | | private SegmentService segmentService; |
| | | @Autowired |
| | | private TrafficService trafficService; |
| | | private LaneService laneService; |
| | | @Autowired |
| | | private AgvModelService agvModelService; |
| | | @Autowired |
| | | private LaneService laneService; |
| | | private LaneBuilder laneBuilder; |
| | | @Autowired |
| | | private ActionSorter actionSorter; |
| | | @Autowired |
| | |
| | | return; |
| | | } |
| | | |
| | | List<Long> taskIds = taskList.stream().map(Task::getId).distinct().collect(Collectors.toList()); |
| | | for (Task task : taskList) { |
| | | Agv agv = allocateService.execute(task); |
| | | if (null == agv) { |
| | | // log.warn("Task[{}] has an issue, because it failed to check out agv which is idle...", task.getSeqNum()); |
| | | continue; |
| | | } |
| | | task.setAgvId(agv.getId()); |
| | | task.setTaskSts(TaskStsType.WAITING.val()); |
| | | task.setIoTime(now); |
| | | task.setUpdateTime(now); |
| | | if (!taskService.updateById(task)) { |
| | | throw new BusinessException("seqNum: " + task.getSeqNum() + " failed to update"); |
| | | } |
| | | // List<Long> taskIds = taskList.stream().map(Task::getId).distinct().collect(Collectors.toList()); |
| | | for (Task t : taskList) { |
| | | String resultAgvNo = allocateService.execute(t |
| | | // inbound |
| | | , (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); |
| | | if (null == currSeg) { |
| | | throw new BusinessException("[Agv:" + agvNo + "] allocate inbound failed: no roller waiting segment"); |
| | | } |
| | | |
| | | // get backpack lev |
| | | int backpackLev = 0; |
| | | int backpack = agvService.getBackpack(agvId); |
| | | List<Integer> usedBackpacks = segmentService.selectUsedBackpacks(currSeg.getTravelId(), agvId); |
| | | if (usedBackpacks.size() >= backpack) { |
| | | throw new CoolException("[Agv:" + agvNo + "] allocate inbound segment failed: no idle backpack to use"); |
| | | } |
| | | for (int lev = 1; lev <= backpack; lev++) { |
| | | if (!usedBackpacks.contains(lev)) { |
| | | backpackLev = lev; |
| | | break; |
| | | } |
| | | } |
| | | if (backpackLev == 0) { |
| | | throw new BusinessException("[Agv:" + agvNo + "] backpack full, can't allocate inbound"); |
| | | } |
| | | |
| | | // update curr seg |
| | | currSeg.setState(SegmentStateType.INIT.toString()); |
| | | currSeg.setUpdateTime(now); |
| | | if (!segmentService.updateById(currSeg)) { |
| | | // log.error("Segment [{}] failed to update !!!", currSeg.getGroupId() + " - " + currSeg.getSerial()); |
| | | throw new CoolException("failed to update segment [" + currSeg.getGroupId() + "-" + currSeg.getSerial() + "] failed"); |
| | | } |
| | | |
| | | // new oriSta seg |
| | | Segment loadSeg = new Segment(); |
| | | loadSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | | loadSeg.setTravelId(currSeg.getTravelId()); |
| | | loadSeg.setAgvId(agvId); |
| | | loadSeg.setTaskId(task.getId()); |
| | | loadSeg.setSerial(currSeg.getSerial() - 1); |
| | | loadSeg.setEndNode(sta.getCode()); |
| | | loadSeg.setPosType(TaskPosDto.PosType.ORI_STA.toString()); |
| | | loadSeg.setState(SegmentStateType.WAITING.toString()); |
| | | loadSeg.setBackpack(backpackLev); |
| | | loadSeg.setCreateTime(now); |
| | | loadSeg.setUpdateTime(now); |
| | | if (!segmentService.save(loadSeg)) { |
| | | // log.error("Segment [{}] failed to save !!!", loadSeg.getTravelId() + " - " + loadSeg.getSerial()); |
| | | throw new CoolException("failed to save segment [" + loadSeg.getGroupId() + "-" + loadSeg.getSerial() + "] failed"); |
| | | } |
| | | |
| | | // place segment |
| | | TaskTypeType taskType = Objects.requireNonNull(TaskTypeType.get(task.getTaskTypeEl())); |
| | | TaskPosDto.PosType posType; |
| | | Code endCode; |
| | | switch (taskType) { |
| | | case STA_TO_LOC: |
| | | posType = TaskPosDto.PosType.DEST_LOC; |
| | | Loc destLoc = locService.getById(task.getDestLoc()); |
| | | endCode = codeService.getCacheById(destLoc.getCode()); |
| | | break; |
| | | case STA_TO_STA: |
| | | posType = TaskPosDto.PosType.DEST_STA; |
| | | Sta destSta = staService.getById(task.getDestSta()); |
| | | endCode = codeService.getCacheById(destSta.getCode()); |
| | | break; |
| | | default: |
| | | throw new BusinessException("[Agv:" + agvNo + "] allocate inbound only supports STA_TO_LOC/STA_TO_STA"); |
| | | } |
| | | |
| | | // calc placeSeg serial, through code position |
| | | List<Segment> destSegList = segmentService.list(new LambdaQueryWrapper<Segment>() |
| | | .eq(Segment::getAgvId, agvId) |
| | | .eq(Segment::getTravelId, currSeg.getTravelId()) |
| | | .ne(Segment::getState, SegmentStateType.FINISH.toString()) |
| | | .in(Segment::getPosType, |
| | | TaskPosDto.PosType.DEST_LOC.toString(), |
| | | TaskPosDto.PosType.DEST_STA.toString()) |
| | | .orderByAsc(Segment::getSerial) |
| | | ); |
| | | |
| | | // build destPosList and sort |
| | | List<TaskPosDto> destPosList = new ArrayList<>(); |
| | | for (Segment destSeg : destSegList) { |
| | | Code code = codeService.getCacheById(destSeg.getEndNode()); |
| | | if (code == null) continue; |
| | | |
| | | TaskPosDto dto = new TaskPosDto(destSeg.getTaskId(), new Double[]{code.getX(), code.getY()}, TaskPosDto.PosType.DEST_LOC); |
| | | dto.setCodeId(code.getId()); |
| | | dto.setSegId(destSeg.getId()); |
| | | destPosList.add(dto); |
| | | } |
| | | TaskPosDto newDto = new TaskPosDto(task.getId(), new Double[]{endCode.getX(), endCode.getY()}, TaskPosDto.PosType.DEST_LOC); |
| | | newDto.setCodeId(endCode.getId()); |
| | | destPosList.add(newDto); |
| | | |
| | | // sort - reference to buildMarjoTask |
| | | final String sameGroupXy = configService.getVal("sameGroupXy", String.class); |
| | | |
| | | // DEST-only sort |
| | | destPosList.sort((a, b) -> { |
| | | |
| | | // 主轴 |
| | | double aFirst = a.getFirstWeight(sameGroupXy); |
| | | double bFirst = b.getFirstWeight(sameGroupXy); |
| | | int c = Double.compare(aFirst, bFirst); |
| | | if (c != 0) return c; |
| | | |
| | | // 副轴 |
| | | double aSecond = a.getSecondWeight(sameGroupXy); |
| | | double bSecond = b.getSecondWeight(sameGroupXy); |
| | | c = Double.compare(aSecond, bSecond); |
| | | if (c != 0) return c; |
| | | |
| | | return Long.compare(a.getTaskId(), b.getTaskId()); |
| | | }); |
| | | |
| | | // get placeSeg serial |
| | | int placeSegSerial; |
| | | // query placeSeg idx |
| | | int idx = -1; |
| | | for (int i = 0; i < destPosList.size(); i++) { |
| | | if (Objects.equals(destPosList.get(i).getCodeId(), newDto.getCodeId()) |
| | | && Objects.equals(destPosList.get(i).getTaskId(), newDto.getTaskId())) { |
| | | idx = i; |
| | | break; |
| | | } |
| | | } |
| | | if (idx < 0) { |
| | | throw new BusinessException("failed to locate new destination in sorted list"); |
| | | } |
| | | |
| | | // 后继点 |
| | | if (idx + 1 == destPosList.size()) { |
| | | placeSegSerial = destSegList.get(destSegList.size() - 1).getSerial() + 1; |
| | | } else { |
| | | TaskPosDto afterTaskPos = destPosList.get(idx + 1); |
| | | Segment afterSeg = destSegList.stream() |
| | | .filter(destSeg -> Objects.equals(destSeg.getId(), afterTaskPos.getSegId())) |
| | | .findFirst() |
| | | .orElse(null); |
| | | if (null == afterSeg) { |
| | | placeSegSerial = destSegList.get(destSegList.size() - 1).getSerial() + 1; |
| | | } else { |
| | | placeSegSerial = afterSeg.getSerial(); |
| | | } |
| | | } |
| | | // placeSegSerial = Math.max(placeSegSerial, currSeg.getSerial() + 1); |
| | | |
| | | // shift serial after |
| | | segmentService.update(new LambdaUpdateWrapper<Segment>() |
| | | .eq(Segment::getAgvId, agvId) |
| | | .eq(Segment::getTravelId, currSeg.getTravelId()) |
| | | .ge(Segment::getSerial, placeSegSerial) |
| | | .ne(Segment::getState, SegmentStateType.FINISH.toString()) |
| | | .setSql("serial = serial + 1") |
| | | .set(Segment::getUpdateTime, now) |
| | | ); |
| | | |
| | | // save place segment |
| | | Segment placeSeg = new Segment(); |
| | | placeSeg.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | | placeSeg.setTravelId(currSeg.getTravelId()); |
| | | placeSeg.setAgvId(agvId); |
| | | placeSeg.setTaskId(task.getId()); |
| | | placeSeg.setSerial(placeSegSerial); |
| | | placeSeg.setEndNode(endCode.getId()); |
| | | placeSeg.setPosType(posType.toString()); |
| | | placeSeg.setState(SegmentStateType.INIT.toString()); |
| | | placeSeg.setBackpack(backpackLev); |
| | | placeSeg.setCreateTime(now); |
| | | placeSeg.setUpdateTime(now); |
| | | if (!segmentService.save(placeSeg)) { |
| | | // log.error("Segment [{}] failed to save !!!", placeSeg.getTravelId() + " - " + placeSeg.getSerial()); |
| | | throw new CoolException("failed to save segment [" + placeSeg.getGroupId() + "-" + placeSeg.getSerial() + "] failed"); |
| | | } |
| | | |
| | | // update task |
| | | task.setAgvId(agvId); |
| | | task.setTaskSts(TaskStsType.ASSIGN.val()); |
| | | task.setIoTime(now); |
| | | task.setUpdateTime(now); |
| | | if (!taskService.updateById(task)) { |
| | | throw new BusinessException("seqNum: " + task.getSeqNum() + " failed to update"); |
| | | } |
| | | // update reserve |
| | | staReserveService.allocateCallBack(task, agvId); |
| | | } |
| | | // normal |
| | | , (task, agvNo, sta) -> { |
| | | Long agvId = agvService.getAgvId(agvNo); |
| | | // update task |
| | | task.setAgvId(agvId); |
| | | task.setTaskSts(TaskStsType.WAITING.val()); |
| | | task.setIoTime(now); |
| | | task.setUpdateTime(now); |
| | | if (!taskService.updateById(task)) { |
| | | throw new BusinessException("seqNum: " + task.getSeqNum() + " failed to update"); |
| | | } |
| | | // update reserve |
| | | staReserveService.allocateCallBack(task, agvId); |
| | | }); |
| | | // if (Cools.isEmpty(agvNo)) { |
| | | //// log.warn("Task[{}] has an issue, because it failed to check out agv which is idle...", task.getSeqNum()); |
| | | // continue; |
| | | // } |
| | | // task.setAgvId(agvService.getAgvId(agvNo)); |
| | | // task.setTaskSts(TaskStsType.WAITING.val()); |
| | | // task.setIoTime(now); |
| | | // task.setUpdateTime(now); |
| | | // if (!taskService.updateById(task)) { |
| | | // throw new BusinessException("seqNum: " + task.getSeqNum() + " failed to update"); |
| | | // } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("mainService.infuseAgvForTask", e); |
| | |
| | | return o2.getPriority() - o1.getPriority(); |
| | | } |
| | | }); |
| | | Integer backpack = agvService.getBackpack(agv); |
| | | Integer backpack = agvService.getBackpack(agvId); |
| | | if (taskList.size() > backpack) { |
| | | taskList = taskList.subList(0, backpack); |
| | | } |
| | |
| | | * val: new TaskPosDto(taskId, new Double[]{code.getX(), code.getY()}, posType) |
| | | */ |
| | | Map<String, List<TaskPosDto>> groups = new HashMap<>(); |
| | | final String sameGroupXy = configService.getVal( "sameGroupXy", String.class); |
| | | final String sameGroupXy = configService.getVal("sameGroupXy", String.class); |
| | | |
| | | int backpackLev = 0; |
| | | for (Task task : taskList) { |
| | |
| | | |
| | | |
| | | // generate travel |
| | | travelService.finishAll(agvId); |
| | | Travel travel = new Travel(); |
| | | travel.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | | travel.setTravelId(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | |
| | | travel.setTaskContent(JSON.toJSONString(list)); |
| | | travel.setTaskIds(JSON.toJSONString(taskList.stream().map(Task::getId).collect(Collectors.toList()))); |
| | | travel.setState(TravelStateType.RUNNING.toString()); |
| | | travel.setCreateTime(now); |
| | | travel.setUpdateTime(now); |
| | | if (!travelService.save(travel)) { |
| | | throw new BusinessException("任务组保存失败"); |
| | | } |
| | |
| | | segment.setPosType(taskPosDto.getPosType().toString()); |
| | | segment.setBackpack(backpackType.lev); |
| | | segment.setState(SegmentStateType.INIT.toString()); |
| | | segment.setCreateTime(now); |
| | | segment.setUpdateTime(now); |
| | | segmentList.add(segment); |
| | | } |
| | | } |
| | |
| | | task.setOriCode(agvDetail.getCode()); |
| | | task.setDestCode(endCode.getId()); |
| | | // lane |
| | | Lane destLane = laneService.search(endCode.getData()); |
| | | if (null != destLane) { |
| | | task.setDestLaneHash(destLane.getHashCode()); |
| | | LaneDto destLaneDto = laneBuilder.search(endCode.getData()); |
| | | if (null != destLaneDto) { |
| | | task.setDestLaneHash(destLaneDto.getHashCode()); |
| | | } |
| | | task.setPriority(taskType.equals(TaskTypeType.TO_CHARGE)?2:1); |
| | | task.setTaskSts(TaskStsType.ASSIGN.val()); |
| | |
| | | } |
| | | |
| | | // generate travel |
| | | travelService.finishAll(agvId); |
| | | Travel travel = new Travel(); |
| | | travel.setUuid(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | | travel.setTravelId(String.valueOf(snowflakeIdWorker.nextId()).substring(3)); |
| | |
| | | |
| | | AgvModel agvModel = agvModelService.getByAgvId(agvId); |
| | | Double workDirection = agvModel.getWorkDirection(); |
| | | final double oppWorkDir = (workDirection + 180) % 360; |
| | | boolean backupAction = null != agvModel.getBackupAction() && agvModel.getBackupActionBool(); |
| | | boolean needUndocking = null != agvModel.getNeedUndocking() && agvModel.getNeedUndockingBool(); |
| | | AgvSpeedType agvSpeedType = AgvSpeedType.query(agvModel.getTravelSpeed()); |
| | | assert agvSpeedType != null; |
| | | |
| | |
| | | String next = pathListPart.get(i); |
| | | |
| | | Code nextCode = codeService.getCacheByData(next); |
| | | // 车头朝前的下一个行走方向 |
| | | Double nextDirection = mapService.calculateDirection(lastCode, nextCode, angleOffsetVal); |
| | | |
| | | // 第一步:如果下一个方向正好是作业方向的相反方向,则重置下一个方向为作业方向,标记 reverse = true |
| | | // 反向角 |
| | | final double oppLastDir = (lastDirection + 180) % 360; |
| | | // 是否倒退行走 |
| | | boolean reverse = false; |
| | | if (nextDirection.equals((workDirection + 180) % 360)) { |
| | | nextDirection = workDirection; |
| | | reverse = true; |
| | | } |
| | | |
| | | // 第二步:判断当前节点是否可以旋转 |
| | | if (!lastCode.getCornerBool()) { |
| | | // 如果是作业方向,但是小车在巷道内方向错误,则停止 |
| | | if (reverse && !lastDirection.equals(nextDirection)) { |
| | | // throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整"); |
| | | // 巷道逻辑 |
| | | if (!laneBuilder.isInitialized()) { |
| | | throw new CoolException("lanes are not initialized"); |
| | | } |
| | | LaneDto lastLaneDto = laneBuilder.search(lastCode.getData()); |
| | | LaneDto nextLaneDto = laneBuilder.search(nextCode.getData()); |
| | | // 进入巷道角度 |
| | | Double lastLaneDir = laneService.getLaneDirection(lastLaneDto); |
| | | Double nextLaneDir = laneService.getLaneDirection(nextLaneDto); |
| | | |
| | | // 巷道强制转弯,优先级 > workDirection |
| | | if (null != nextLaneDir) { |
| | | nextDirection = nextLaneDir; |
| | | |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | if (!lastCode.getCornerBool()) { |
| | | throw new CoolException(agvNo + "号小车进入巷道需调整方向为 " + nextDirection + "°,请推至转弯点手动调整"); |
| | | } |
| | | // turn |
| | | actionList.add(new Action( |
| | | null, // 编号s |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | lastDirection = nextDirection; |
| | | } |
| | | // 如果不是作业方向,判断是否相反方向,如果反方向则倒退行走 |
| | | if (nextDirection.equals((lastDirection + 180) % 360)) { |
| | | nextDirection = lastDirection; |
| | | reverse = true; |
| | | } else if (null != lastLaneDir) { |
| | | nextDirection = lastLaneDir; |
| | | |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | if (!lastCode.getCornerBool()) { |
| | | throw new CoolException(agvNo + "号小车离开巷道需调整方向为 " + nextDirection + "°,请推至转弯点手动调整"); |
| | | } |
| | | } |
| | | } else { |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | // 如果下个节点方向与当前agv方向相反,则倒退行走,但是如果当前agv方向正好与工作方向相反,则旋转至工作方向 |
| | | if (nextDirection.equals((lastDirection + 180) % 360) && !workDirection.equals((lastDirection + 180) % 360)) { |
| | | |
| | | // 如果下一个方向正好是作业方向的相反方向,则重置下一个方向为作业方向,标记 reverse = true |
| | | if (nextDirection.equals(oppWorkDir)) { |
| | | nextDirection = workDirection; |
| | | reverse = true; |
| | | } |
| | | |
| | | // 判断当前节点是否可以旋转 |
| | | if (!lastCode.getCornerBool()) { |
| | | // 如果是作业方向,但是小车在巷道内方向错误,则停止 |
| | | if (reverse && !lastDirection.equals(nextDirection)) { |
| | | throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整"); |
| | | } |
| | | // 如果不是作业方向(另一组相反方向),判断是否相反方向,如果反方向则倒退行走 |
| | | if (nextDirection.equals(oppLastDir)) { |
| | | // 倒退时,因为agv方向没变,所以下一个方向还是agv方向,故nextDirection = lastDirection; |
| | | nextDirection = lastDirection; |
| | | reverse = true; |
| | | } else { |
| | | // turn |
| | | actionList.add(new Action( |
| | | null, // 编号s |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | // 总结:1.如果是作业组(差180°)方向,那么agv方向必须是作业方向,如果 reverse 则说明倒退,这时候nextDirection肯定会等于作业方向(前面赋值了),如果不相同,则报错 |
| | | // 2.如果不是作业组方向(另一组相反方向差180°),因为此函数不能旋转,所以差180°时只能倒退,倒退的时候因为agv不会旋转,所以nextDirection要变成agv方向 |
| | | } else { |
| | | if (!lastDirection.equals(nextDirection)) { |
| | | // 如果下一个方向与agv方向相反,则倒退行走,避免进行毫无意义的转弯动作。 |
| | | // 但是要注意:如果agv方向与工作方向正好相反,则需要旋转至工作方向,也就是为什么要加!workDirection.equals(oppLastDir)判断 |
| | | if (nextDirection.equals(oppLastDir) && !workDirection.equals(oppLastDir)) { |
| | | // 倒退时,因为agv方向没变,所以下一个方向还是agv方向,故nextDirection = lastDirection; |
| | | nextDirection = lastDirection; |
| | | reverse = true; |
| | | } else { |
| | | // turn |
| | | actionList.add(new Action( |
| | | null, // 编号s |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | |
| | | lastDirection = nextDirection; |
| | | lastDirection = nextDirection; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(nextDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | } |
| | | |
| | | // run |
| | | ActionTypeType actionType = ActionTypeType.StraightAheadTurnable; |
| | | if (reverse) { |
| | | actionType = ActionTypeType.StraightBackTurnable; |
| | | } |
| | | ActionTypeType actionType = reverse ? ActionTypeType.StraightBackTurnable : ActionTypeType.StraightAheadTurnable; |
| | | CodeGap gap = codeGapService.findByCodeOfBoth(lastCode.getId(), nextCode.getId()); |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(workDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 暂存点放货 |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyReleaseToAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyReleaseToAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 背篓放货 |
| | | if (backupAction) { |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyReleaseToAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyReleaseToAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | break; |
| | | case DEST_LOC: |
| | | assert backpackType != null; |
| | |
| | | if (!lastDirection.equals(workDirection)) { |
| | | throw new CoolException(agvNo + "号小车方向错误,请推至转弯点手动调整"); |
| | | } |
| | | // 暂存点取货货 |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyTakeFromAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyTakeFromAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 背篓取货 |
| | | if (backupAction) { |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyTakeFromAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyTakeFromAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | |
| | | // 货架放货 |
| | | Loc destLoc = locService.getById(task.getDestLoc()); |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(oriStaWorkDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 暂存点放货 |
| | | assert backpackType != null; |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyReleaseToAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyReleaseToAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 背篓放货 |
| | | if (backupAction) { |
| | | assert backpackType != null; |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyReleaseToAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyReleaseToAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | break; |
| | | case DEST_STA: |
| | | // 站点放货 |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(destStaWorkDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | )); |
| | | lastDirection = destStaWorkDirection; |
| | | } |
| | | // 暂存点取货 |
| | | assert backpackType != null; |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyTakeFromAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyTakeFromAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | // 背篓取货 |
| | | if (backupAction) { |
| | | assert backpackType != null; |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | | task.getBusId(), // 总线 |
| | | task.getId(), // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.ReadyTakeFromAgvSite.desc, // 名称 |
| | | (double) backpackType.lev, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(backpackType.height), // 动作参数 |
| | | ActionTypeType.ReadyTakeFromAgvSite.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | // 计算货叉工作方向 |
| | | staWorkDirection = mapService.calculateAgvWorkDirectionByStation(destStaWorkDirection, lastDirection); |
| | | actionList.add(new Action( |
| | |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.TurnCorner.desc, // 名称 |
| | | mapService.isTurnCorner(lastCode.getData()) ? 1D : 0D, // 属性值 |
| | | (double) mapService.spinDirection(lastCode).val, // 属性值 |
| | | lastCode.getData(), // 地面码 |
| | | String.valueOf(chargeDirection), // 动作参数 |
| | | ActionTypeType.TurnCorner.val(), // 动作类型 |
| | |
| | | |
| | | } |
| | | |
| | | // 如果充电中,则先断开充电 |
| | | if (needUndocking && agvDetail.getAgvStatus().equals(AgvStatusType.CHARGE)) { |
| | | String undockingCode = Cools.isEmpty(actionList) ? lastCode.getData() : actionList.get(0).getCode(); |
| | | // undocking charge |
| | | actionList.add(0, new Action( |
| | | null, // 编号 |
| | | null, // 总线 |
| | | null, // 任务 |
| | | null, // 动作号 |
| | | null, // 优先级 |
| | | ActionTypeType.UndockingCharge.desc, // 名称 |
| | | null, // 属性值 |
| | | undockingCode, // 地面码 |
| | | null, // 动作参数 |
| | | ActionTypeType.UndockingCharge.val(), // 动作类型 |
| | | actionPrepareSts, // 动作进度 |
| | | agvId, // AGV |
| | | now // 工作时间 |
| | | )); |
| | | } |
| | | |
| | | // finish |
| | | actionList.add(new Action( |
| | | null, // 编号 |
| | |
| | | now // 工作时间 |
| | | )); |
| | | |
| | | List<Action> newActionList = actionSorter.optimizeSort(actionList); |
| | | String groupId = String.valueOf(snowflakeIdWorker.nextId()).substring(3); |
| | | |
| | | // update segment |
| | | for (Segment item : segmentList) { |
| | | LambdaUpdateWrapper<Segment> uw = new LambdaUpdateWrapper<Segment>() |
| | | .eq(Segment::getId, item.getId()) |
| | | .eq(Segment::getState, item.getState()) |
| | | .set(Segment::getGroupId, groupId) |
| | | .set(Segment::getState, SegmentStateType.RUNNING.toString()) |
| | | .set(Segment::getStartTime, now) |
| | | .set(Segment::getUpdateTime, now); |
| | | if (algoStartTime != null) { |
| | | uw.set(Segment::getAlgoTime, (int) (now.getTime() - algoStartTime.getTime())); |
| | | } |
| | | if (!segmentService.update(uw)) { |
| | | // segment 原子性保证 |
| | | throw new CoolException("更新Segment失败: segmentId=" + item.getId() + " state not WAITING"); |
| | | } |
| | | } |
| | | |
| | | // update segment |
| | | // for (Segment item : segmentList) { |
| | | // item.setGroupId(groupId); |
| | | // item.setState(SegmentStateType.RUNNING.toString()); |
| | | // item.setStartTime(now); |
| | | // item.setUpdateTime(now); |
| | | // if (null != algoStartTime) { |
| | | // item.setAlgoTime((int) (now.getTime() - algoStartTime.getTime())); |
| | | // } |
| | | // if (!segmentService.updateById(item)) { |
| | | // throw new CoolException("更新Segment失败"); |
| | | // } |
| | | // } |
| | | |
| | | // optimize action list, must have backpack |
| | | List<Action> newActionList = backupAction ? actionSorter.optimizeSort(actionList) : actionList; |
| | | |
| | | // save action |
| | | int i = newActionList.size(); |
| | |
| | | } |
| | | if (!actionService.saveBatch(newActionList)) { |
| | | throw new BusinessException("group[" + groupId + "] 动作保存失败"); |
| | | } |
| | | |
| | | // update segment |
| | | for (Segment item : segmentList) { |
| | | item.setGroupId(groupId); |
| | | item.setState(SegmentStateType.RUNNING.toString()); |
| | | item.setStartTime(now); |
| | | item.setUpdateTime(now); |
| | | if (null != algoStartTime) { |
| | | item.setAlgoTime((int) (now.getTime() - algoStartTime.getTime())); |
| | | } |
| | | if (!segmentService.updateById(item)) { |
| | | throw new CoolException("更新Segment失败"); |
| | | } |
| | | } |
| | | |
| | | log.info("{}号Agv动作组装完成,指令数量:{}", agvNo, newActionList.size()); |
| | |
| | | break; |
| | | case DockingCharge: |
| | | agvAction.add(new AgvActionItem<>(DockingChargeAction.class) |
| | | .setQrCode(action.getCode()) |
| | | ); |
| | | break; |
| | | case UndockingCharge: |
| | | agvAction.add(new AgvActionItem<>(UndockingChargeAction.class) |
| | | .setQrCode(action.getCode()) |
| | | ); |
| | | break; |
| | |
| | | success = true; |
| | | break; |
| | | case ORI_STA: |
| | | // load from sta |
| | | sta = staService.getById(currTask.getOriSta()); |
| | | success = staReserveService.waitingStaReserve(sta, currTask, 1, StaReserveType.OUT); |
| | | |
| | | boolean permit = conveyorStationService.allowAgvWork(sta, currTask, currSeg, StaReserveType.OUT); |
| | | |
| | | // load sta |
| | | // remove expiredTime |
| | | // reserve to be waiting |
| | | if (!staReserveService.waitingStaReserve(sta, currTask, 1, StaReserveType.OUT)) { |
| | | break; |
| | | } |
| | | // convey plc valid |
| | | if (!conveyorStationService.allowAgvWork(sta, currTask, currSeg, StaReserveType.OUT)) { |
| | | // reserve rollback |
| | | staReserveService.rollbackWaitingToReserved(sta, currTask, StaReserveType.OUT); |
| | | break; |
| | | } |
| | | success = true; |
| | | break; |
| | | case DEST_STA: |
| | | // place sta |
| | | // place to sta |
| | | sta = staService.getById(currTask.getDestSta()); |
| | | success = staReserveService.waitingStaReserve(sta, currTask, 1, StaReserveType.IN); |
| | | // reserve to be waiting |
| | | if (!staReserveService.waitingStaReserve(sta, currTask, 1, StaReserveType.IN)) { |
| | | break; |
| | | } |
| | | // convey plc valid |
| | | if (!conveyorStationService.allowAgvWork(sta, currTask, currSeg, StaReserveType.IN)) { |
| | | // reserve rollback |
| | | staReserveService.rollbackWaitingToReserved(sta, currTask, StaReserveType.IN); |
| | | break; |
| | | } |
| | | success = true; |
| | | break; |
| | | default: |
| | | log.error("agv[{}] has wrong posType [{}], segment [{}]", protocol.getAgvNo(), posType.toString(), currSeg.getId()); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("agv[{}] failed to deal with action request, qrCode={}", agv.getUuid(), agv_06_up.getVal(), e); |
| | | } |
| | |
| | | } |
| | | |
| | | if (taskComplete) { |
| | | // locService.taskCallBack(task); |
| | | |
| | | task.setTaskSts(TaskStsType.COMPLETE.val()); |
| | | task.setEndTime(now); |
| | | task.setUpdateTime(now); |