| | |
| | | import com.core.common.Cools; |
| | | import com.core.exception.CoolException; |
| | | import com.zy.asrs.domain.enums.NotifyMsgType; |
| | | import com.zy.asrs.domain.path.StationPathResolvedPolicy; |
| | | import com.zy.asrs.domain.vo.StationCycleCapacityVo; |
| | | import com.zy.asrs.domain.vo.StationCycleLoopVo; |
| | | import com.zy.asrs.entity.*; |
| | |
| | | @Component |
| | | public class StationOperateProcessUtils { |
| | | private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120; |
| | | private static final int OUT_ORDER_DISPATCH_LIMIT_SECONDS = 10; |
| | | private static final int STATION_IDLE_RECOVER_SECONDS = 10; |
| | | private static final int STATION_IDLE_RECOVER_LIMIT_SECONDS = 10; |
| | | private static final int STATION_IDLE_TRACK_EXPIRE_SECONDS = 60 * 60; |
| | | |
| | | @Autowired |
| | | private BasDevpService basDevpService; |
| | |
| | | private BasStationService basStationService; |
| | | @Autowired |
| | | private StationCycleCapacityService stationCycleCapacityService; |
| | | @Autowired |
| | | private StationPathPolicyService stationPathPolicyService; |
| | | @Autowired |
| | | private BasStationOptService basStationOptService; |
| | | |
| | | //执行输送站点入库任务 |
| | | public synchronized void stationInExecute() { |
| | | try { |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(); |
| | | DispatchLimitConfig baseLimitConfig = getDispatchLimitConfig(null, null); |
| | | int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()}; |
| | | LoadGuardState loadGuardState = buildLoadGuardState(limitConfig); |
| | | LoadGuardState loadGuardState = buildLoadGuardState(baseLimitConfig); |
| | | |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | |
| | | continue; |
| | | } |
| | | |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), targetStationId); |
| | | LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), targetStationId, loadGuardState); |
| | | |
| | | if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) { |
| | |
| | | //执行堆垛机输送站点出库任务 |
| | | public synchronized void crnStationOutExecute() { |
| | | try { |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(); |
| | | DispatchLimitConfig baseLimitConfig = getDispatchLimitConfig(null, null); |
| | | int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()}; |
| | | LoadGuardState loadGuardState = buildLoadGuardState(limitConfig); |
| | | LoadGuardState loadGuardState = buildLoadGuardState(baseLimitConfig); |
| | | |
| | | List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts) |
| | |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0 |
| | | ) { |
| | | Integer moveStaNo = wrkMast.getStaNo(); |
| | | |
| | | if (!outOrderList.isEmpty()) { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(stationProtocol.getStationId(), wrkMast.getStaNo()); |
| | | for (int i = nodes.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = nodes.get(i); |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | if (outOrderList.contains(stationId)) { |
| | | moveStaNo = stationId; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderList |
| | | ); |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null) { |
| | | continue; |
| | | } |
| | | |
| | | DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo); |
| | | LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState); |
| | | |
| | | if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) { |
| | |
| | | for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) { |
| | | runBlockReassignLocStationList.add(stationObjModel.getStationId()); |
| | | } |
| | | List<Integer> outOrderStationIds = basDevp.getOutOrderIntList(); |
| | | |
| | | List<StationProtocol> list = stationThread.getStatus(); |
| | | for (StationProtocol stationProtocol : list) { |
| | |
| | | } |
| | | } else { |
| | | //运行堵塞,重新计算路线 |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0); |
| | | OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderStationIds |
| | | ); |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null || Objects.equals(moveStaNo, stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | syncOutOrderWatchState(wrkMast, stationProtocol.getStationId(), outOrderStationIds, dispatchDecision, command); |
| | | News.info("输送站点堵塞后重新计算路径命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | //检测输送站点任务停留超时后重新计算路径 |
| | | public synchronized void checkStationIdleRecover() { |
| | | try { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | |
| | | List<StationProtocol> list = stationThread.getStatus(); |
| | | for (StationProtocol stationProtocol : list) { |
| | | if (stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() > 0 |
| | | && !stationProtocol.isRunBlock() |
| | | ) { |
| | | checkStationIdleRecover(basDevp, stationThread, stationProtocol, basDevp.getOutOrderIntList()); |
| | | } |
| | | } |
| | | } |
| | |
| | | public synchronized void checkStationOutOrder() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getId()); |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | | Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap(); |
| | | List<StationObjModel> orderList = basDevp.getOutOrderList$(); |
| | | List<Integer> outOrderStationIds = basDevp.getOutOrderIntList(); |
| | | for (StationObjModel stationObjModel : orderList) { |
| | | StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | |
| | | if (wrkMast == null) { |
| | | continue; |
| | | } |
| | | |
| | | if (Cools.isEmpty(wrkMast.getBatch())) { |
| | | if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)) { |
| | | continue; |
| | | } |
| | | if (Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) { |
| | | continue; |
| | | } |
| | | |
| | | if (Cools.isEmpty(wrkMast.getBatchSeq())) { |
| | | if (isWatchingCircleArrival(wrkMast.getWrkNo(), stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | |
| | | List<WrkMast> batchWrkList = wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .notIn("wrk_sts", WrkStsType.STATION_RUN_COMPLETE.sts, WrkStsType.COMPLETE_OUTBOUND.sts) |
| | | .eq("batch", wrkMast.getBatch()) |
| | | .orderBy(true, true, "batch") |
| | | OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderStationIds |
| | | ); |
| | | if (batchWrkList.isEmpty()) { |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null || Objects.equals(moveStaNo, stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | WrkMast firstWrkMast = batchWrkList.get(0); |
| | | Integer currentBatchSeq = firstWrkMast.getBatchSeq(); |
| | | |
| | | List<NavigateNode> initPath = navigateUtils.calcByStationId(wrkMast.getSourceStaNo(), wrkMast.getStaNo()); |
| | | |
| | | String commandType = "none"; |
| | | Integer seq = getOutStationBatchSeq(initPath, stationProtocol.getStationId(), wrkMast.getBatch()); |
| | | if (seq == null) { |
| | | if (currentBatchSeq.equals(wrkMast.getBatchSeq())) { |
| | | commandType = "toTarget"; |
| | | }else { |
| | | commandType = "toCircle"; |
| | | } |
| | | }else { |
| | | seq++; |
| | | if (seq.equals(wrkMast.getBatchSeq()) && currentBatchSeq.equals(wrkMast.getBatchSeq())) { |
| | | commandType = "toTarget"; |
| | | }else { |
| | | commandType = "toCircle"; |
| | | } |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | |
| | | if (commandType.equals("toTarget")) { |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | News.info("{}任务直接去目标点", wrkMast.getWrkNo()); |
| | | } else if (commandType.equals("toCircle")) { |
| | | Integer circleTarget = null; |
| | | for (NavigateNode node : initPath) { |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | try { |
| | | List<NavigateNode> enableMovePath = navigateUtils.calcByStationId(stationProtocol.getStationId(), stationId); |
| | | if (enableMovePath.isEmpty()) { |
| | | continue; |
| | | } |
| | | } catch (Exception e) { |
| | | continue; |
| | | } |
| | | |
| | | circleTarget = stationId; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (circleTarget == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), circleTarget, 0); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | redisUtil.set(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkMast.getWrkNo(), JSON.toJSONString(command, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | News.info("{}任务进行绕圈", wrkMast.getWrkNo()); |
| | | if (!tryAcquireOutOrderDispatchLock(wrkMast.getWrkNo(), stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | syncOutOrderWatchState(wrkMast, stationProtocol.getStationId(), outOrderStationIds, dispatchDecision, command); |
| | | MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command)); |
| | | News.info(dispatchDecision.isCircle() ? "{}任务进行绕圈" : "{}任务直接去目标点", wrkMast.getWrkNo()); |
| | | } |
| | | } |
| | | } |
| | |
| | | public synchronized void watchCircleStation() { |
| | | List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>()); |
| | | for (BasDevp basDevp : basDevps) { |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getId()); |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); |
| | | if (stationThread == null) { |
| | | continue; |
| | | } |
| | |
| | | continue; |
| | | } |
| | | |
| | | Object circleObj = redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + stationProtocol.getTaskNo()); |
| | | if (circleObj == null) { |
| | | StationCommand circleCommand = getWatchCircleCommand(stationProtocol.getTaskNo()); |
| | | if (circleCommand == null) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand circleCommand = JSON.parseObject(circleObj.toString(), StationCommand.class); |
| | | if (!stationProtocol.getStationId().equals(circleCommand.getTargetStaNo())) { |
| | | continue; |
| | | } |
| | |
| | | if (wrkMast == null) { |
| | | continue; |
| | | } |
| | | if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)) { |
| | | continue; |
| | | } |
| | | if (Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) { |
| | | continue; |
| | | } |
| | | |
| | | Integer moveStaNo = wrkMast.getStaNo(); |
| | | |
| | | if (!outOrderList.isEmpty()) { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(stationProtocol.getStationId(), wrkMast.getStaNo()); |
| | | for (int i = nodes.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = nodes.get(i); |
| | | JSONObject v = JSONObject.parseObject(node.getNodeValue()); |
| | | if (v != null) { |
| | | Integer stationId = v.getInteger("stationId"); |
| | | if (outOrderList.contains(stationId)) { |
| | | moveStaNo = stationId; |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderList |
| | | ); |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null || Objects.equals(moveStaNo, stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | |
| | | StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0); |
| | |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | continue; |
| | | } |
| | | if (!tryAcquireOutOrderDispatchLock(wrkMast.getWrkNo(), stationProtocol.getStationId())) { |
| | | continue; |
| | | } |
| | | syncOutOrderWatchState(wrkMast, stationProtocol.getStationId(), outOrderList, dispatchDecision, command); |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | } |
| | | } |
| | |
| | | return list; |
| | | } |
| | | |
| | | private OutOrderDispatchDecision resolveOutboundDispatchDecision(Integer currentStationId, |
| | | WrkMast wrkMast, |
| | | List<Integer> outOrderStationIds) { |
| | | if (wrkMast == null || wrkMast.getStaNo() == null) { |
| | | return null; |
| | | } |
| | | if (!shouldApplyOutOrder(wrkMast, outOrderStationIds)) { |
| | | return new OutOrderDispatchDecision(wrkMast.getStaNo(), false); |
| | | } |
| | | if (isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds)) { |
| | | return resolveCurrentOutOrderDispatchDecision(currentStationId, wrkMast, outOrderStationIds); |
| | | } |
| | | |
| | | Integer moveStaNo = resolveDispatchOutOrderTarget(currentStationId, wrkMast.getStaNo(), outOrderStationIds, true); |
| | | if (moveStaNo == null) { |
| | | return null; |
| | | } |
| | | return new OutOrderDispatchDecision(moveStaNo, false); |
| | | } |
| | | |
| | | private OutOrderDispatchDecision resolveCurrentOutOrderDispatchDecision(Integer currentStationId, |
| | | WrkMast wrkMast, |
| | | List<Integer> outOrderStationIds) { |
| | | if (!isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds)) { |
| | | return null; |
| | | } |
| | | |
| | | List<WrkMast> batchWrkList = wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .eq("io_type", WrkIoType.OUT.id) |
| | | .notIn("wrk_sts", |
| | | WrkStsType.STATION_RUN_COMPLETE.sts, |
| | | WrkStsType.COMPLETE_OUTBOUND.sts, |
| | | WrkStsType.SETTLE_OUTBOUND.sts) |
| | | .eq("batch", wrkMast.getBatch()) |
| | | .orderByAsc("batch_seq") |
| | | .orderByAsc("wrk_no")); |
| | | if (batchWrkList.isEmpty()) { |
| | | return new OutOrderDispatchDecision(wrkMast.getStaNo(), false); |
| | | } |
| | | |
| | | WrkMast firstWrkMast = batchWrkList.get(0); |
| | | Integer currentBatchSeq = firstWrkMast.getBatchSeq(); |
| | | if (currentBatchSeq == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "批次:{} 首个未完成任务缺少批次序号,当前任务暂不放行", wrkMast.getBatch()); |
| | | return null; |
| | | } |
| | | |
| | | List<NavigateNode> initPath; |
| | | try { |
| | | initPath = navigateUtils.calcByStationId(wrkMast.getSourceStaNo(), wrkMast.getStaNo()); |
| | | } catch (Exception e) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "批次:{} 计算排序路径失败,当前站点={}", wrkMast.getBatch(), currentStationId); |
| | | return null; |
| | | } |
| | | |
| | | Integer seq = getOutStationBatchSeq(initPath, currentStationId, wrkMast.getBatch()); |
| | | boolean toTarget; |
| | | if (seq == null) { |
| | | toTarget = currentBatchSeq.equals(wrkMast.getBatchSeq()); |
| | | } else { |
| | | toTarget = Integer.valueOf(seq + 1).equals(wrkMast.getBatchSeq()) |
| | | && currentBatchSeq.equals(wrkMast.getBatchSeq()); |
| | | } |
| | | if (toTarget) { |
| | | return new OutOrderDispatchDecision(wrkMast.getStaNo(), false); |
| | | } |
| | | |
| | | Integer circleTarget = resolveNextCircleOrderTarget(currentStationId, outOrderStationIds); |
| | | if (circleTarget == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "未找到可执行的下一排序检测点,当前站点={}", currentStationId); |
| | | return null; |
| | | } |
| | | return new OutOrderDispatchDecision(circleTarget, true); |
| | | } |
| | | |
| | | private boolean shouldApplyOutOrder(WrkMast wrkMast, List<Integer> outOrderStationIds) { |
| | | return wrkMast != null |
| | | && wrkMast.getStaNo() != null |
| | | && Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id) |
| | | && !Cools.isEmpty(wrkMast.getBatch()) |
| | | && wrkMast.getBatchSeq() != null |
| | | && outOrderStationIds != null |
| | | && !outOrderStationIds.isEmpty(); |
| | | } |
| | | |
| | | private boolean isCurrentOutOrderDispatchStation(Integer currentStationId, |
| | | WrkMast wrkMast, |
| | | List<Integer> outOrderStationIds) { |
| | | return currentStationId != null |
| | | && shouldApplyOutOrder(wrkMast, outOrderStationIds) |
| | | && !Objects.equals(currentStationId, wrkMast.getStaNo()) |
| | | && outOrderStationIds.contains(currentStationId); |
| | | } |
| | | |
| | | private void syncOutOrderWatchState(WrkMast wrkMast, |
| | | Integer currentStationId, |
| | | List<Integer> outOrderStationIds, |
| | | OutOrderDispatchDecision dispatchDecision, |
| | | StationCommand command) { |
| | | if (dispatchDecision == null || command == null) { |
| | | return; |
| | | } |
| | | if (!isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds)) { |
| | | return; |
| | | } |
| | | if (dispatchDecision.isCircle()) { |
| | | saveWatchCircleCommand(wrkMast.getWrkNo(), command); |
| | | } else { |
| | | clearWatchCircleCommand(wrkMast.getWrkNo()); |
| | | } |
| | | } |
| | | |
| | | private Integer resolveDispatchOutOrderTarget(Integer currentStationId, |
| | | Integer finalTargetStationId, |
| | | List<Integer> outOrderList, |
| | | boolean skipCurrentStation) { |
| | | if (finalTargetStationId == null) { |
| | | return null; |
| | | } |
| | | if (currentStationId == null || outOrderList == null || outOrderList.isEmpty()) { |
| | | return finalTargetStationId; |
| | | } |
| | | |
| | | try { |
| | | List<NavigateNode> nodes = navigateUtils.calcByStationId(currentStationId, finalTargetStationId); |
| | | for (int i = nodes.size() - 1; i >= 0; i--) { |
| | | Integer stationId = getStationIdFromNode(nodes.get(i)); |
| | | if (stationId == null) { |
| | | continue; |
| | | } |
| | | if (skipCurrentStation && currentStationId.equals(stationId)) { |
| | | continue; |
| | | } |
| | | if (outOrderList.contains(stationId)) { |
| | | return stationId; |
| | | } |
| | | } |
| | | } catch (Exception ignore) {} |
| | | return finalTargetStationId; |
| | | } |
| | | |
| | | private Integer resolveNextCircleOrderTarget(Integer currentStationId, List<Integer> orderedOutStationList) { |
| | | if (currentStationId == null || orderedOutStationList == null || orderedOutStationList.size() <= 1) { |
| | | return null; |
| | | } |
| | | |
| | | int startIndex = orderedOutStationList.indexOf(currentStationId); |
| | | int total = orderedOutStationList.size(); |
| | | for (int offset = 1; offset < total; offset++) { |
| | | int candidateIndex = (startIndex + offset + total) % total; |
| | | Integer candidateStationId = orderedOutStationList.get(candidateIndex); |
| | | if (candidateStationId == null || currentStationId.equals(candidateStationId)) { |
| | | continue; |
| | | } |
| | | try { |
| | | List<NavigateNode> path = navigateUtils.calcByStationId(currentStationId, candidateStationId); |
| | | if (path != null && !path.isEmpty()) { |
| | | return candidateStationId; |
| | | } |
| | | } catch (Exception ignore) {} |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean tryAcquireOutOrderDispatchLock(Integer wrkNo, Integer stationId) { |
| | | if (wrkNo == null || wrkNo <= 0 || stationId == null) { |
| | | return true; |
| | | } |
| | | String key = RedisKeyType.STATION_OUT_ORDER_DISPATCH_LIMIT_.key + wrkNo + "_" + stationId; |
| | | Object lock = redisUtil.get(key); |
| | | if (lock != null) { |
| | | return false; |
| | | } |
| | | redisUtil.set(key, "lock", OUT_ORDER_DISPATCH_LIMIT_SECONDS); |
| | | return true; |
| | | } |
| | | |
| | | private boolean isWatchingCircleArrival(Integer wrkNo, Integer stationId) { |
| | | StationCommand command = getWatchCircleCommand(wrkNo); |
| | | return command != null && stationId != null && stationId.equals(command.getTargetStaNo()); |
| | | } |
| | | |
| | | private StationCommand getWatchCircleCommand(Integer wrkNo) { |
| | | if (wrkNo == null || wrkNo <= 0) { |
| | | return null; |
| | | } |
| | | Object circleObj = redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo); |
| | | if (circleObj == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return JSON.parseObject(circleObj.toString(), StationCommand.class); |
| | | } catch (Exception ignore) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private void saveWatchCircleCommand(Integer wrkNo, StationCommand command) { |
| | | if (wrkNo == null || wrkNo <= 0 || command == null) { |
| | | return; |
| | | } |
| | | redisUtil.set(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo, |
| | | JSON.toJSONString(command, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24); |
| | | } |
| | | |
| | | private void clearWatchCircleCommand(Integer wrkNo) { |
| | | if (wrkNo == null || wrkNo <= 0) { |
| | | return; |
| | | } |
| | | redisUtil.del(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo); |
| | | } |
| | | |
| | | private void checkStationIdleRecover(BasDevp basDevp, |
| | | StationThread stationThread, |
| | | StationProtocol stationProtocol, |
| | | List<Integer> outOrderList) { |
| | | if (stationProtocol == null || stationProtocol.getTaskNo() == null || stationProtocol.getTaskNo() <= 0) { |
| | | return; |
| | | } |
| | | if (!Objects.equals(stationProtocol.getStationId(), stationProtocol.getTargetStaNo())) { |
| | | return; |
| | | } |
| | | |
| | | StationTaskIdleTrack idleTrack = touchStationTaskIdleTrack(stationProtocol.getTaskNo(), stationProtocol.getStationId()); |
| | | if (idleTrack == null || !idleTrack.isTimeout(STATION_IDLE_RECOVER_SECONDS)) { |
| | | return; |
| | | } |
| | | |
| | | WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); |
| | | if (!canRecoverIdleStationTask(wrkMast, stationProtocol.getStationId())) { |
| | | return; |
| | | } |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.CHECK_STATION_IDLE_RECOVER_LIMIT_.key + stationProtocol.getTaskNo()); |
| | | if (lock != null) { |
| | | return; |
| | | } |
| | | |
| | | if (hasFollowUpMoveCommandAfterStay(idleTrack, stationProtocol.getTaskNo(), stationProtocol.getStationId())) { |
| | | return; |
| | | } |
| | | |
| | | OutOrderDispatchDecision dispatchDecision = null; |
| | | Integer moveStaNo; |
| | | if (Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)) { |
| | | dispatchDecision = resolveOutboundDispatchDecision(stationProtocol.getStationId(), wrkMast, outOrderList); |
| | | moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | } else { |
| | | moveStaNo = wrkMast.getStaNo(); |
| | | } |
| | | if (moveStaNo == null || Objects.equals(moveStaNo, stationProtocol.getStationId())) { |
| | | return; |
| | | } |
| | | |
| | | redisUtil.set(RedisKeyType.CHECK_STATION_IDLE_RECOVER_LIMIT_.key + stationProtocol.getTaskNo(), "lock", STATION_IDLE_RECOVER_LIMIT_SECONDS); |
| | | |
| | | StationCommand command = stationThread.getCommand( |
| | | StationCommandType.MOVE, |
| | | wrkMast.getWrkNo(), |
| | | stationProtocol.getStationId(), |
| | | moveStaNo, |
| | | 0 |
| | | ); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "站点任务停留超时后重算路径失败,当前站点={},目标站点={}", stationProtocol.getStationId(), moveStaNo); |
| | | return; |
| | | } |
| | | |
| | | MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command)); |
| | | syncOutOrderWatchState(wrkMast, stationProtocol.getStationId(), outOrderList, dispatchDecision, command); |
| | | saveStationTaskIdleTrack(new StationTaskIdleTrack(wrkMast.getWrkNo(), stationProtocol.getStationId(), System.currentTimeMillis())); |
| | | News.info("输送站点任务停留{}秒未运行,已重新计算路径并重启运行,站点号={},目标站={},工作号={},命令数据={}", |
| | | STATION_IDLE_RECOVER_SECONDS, stationProtocol.getStationId(), moveStaNo, wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | } |
| | | |
| | | private boolean canRecoverIdleStationTask(WrkMast wrkMast, Integer currentStationId) { |
| | | if (wrkMast == null || currentStationId == null || wrkMast.getStaNo() == null) { |
| | | return false; |
| | | } |
| | | if (Objects.equals(currentStationId, wrkMast.getStaNo())) { |
| | | return false; |
| | | } |
| | | return Objects.equals(wrkMast.getWrkSts(), WrkStsType.INBOUND_DEVICE_RUN.sts) |
| | | || Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts); |
| | | } |
| | | |
| | | private boolean hasFollowUpMoveCommandAfterStay(StationTaskIdleTrack idleTrack, |
| | | Integer taskNo, |
| | | Integer stationId) { |
| | | if (idleTrack == null || taskNo == null || stationId == null || idleTrack.firstSeenTime == null) { |
| | | return false; |
| | | } |
| | | if (basStationOptService == null) { |
| | | return false; |
| | | } |
| | | |
| | | List<BasStationOpt> optList; |
| | | try { |
| | | optList = basStationOptService.list(new QueryWrapper<BasStationOpt>() |
| | | .select("id", "target_station_id") |
| | | .eq("task_no", taskNo) |
| | | .eq("source_station_id", stationId) |
| | | .eq("mode", String.valueOf(StationCommandType.MOVE)) |
| | | .eq("send", 1) |
| | | .ge("send_time", new Date(idleTrack.firstSeenTime)) |
| | | .orderByDesc("send_time") |
| | | .last("limit 3")); |
| | | } catch (Exception e) { |
| | | return false; |
| | | } |
| | | |
| | | if (optList == null || optList.isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | for (BasStationOpt opt : optList) { |
| | | if (opt != null && opt.getTargetStationId() != null && !Objects.equals(opt.getTargetStationId(), stationId)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private StationTaskIdleTrack touchStationTaskIdleTrack(Integer taskNo, Integer stationId) { |
| | | if (taskNo == null || taskNo <= 0 || stationId == null) { |
| | | return null; |
| | | } |
| | | long now = System.currentTimeMillis(); |
| | | StationTaskIdleTrack idleTrack = getStationTaskIdleTrack(taskNo); |
| | | if (idleTrack == null || !Objects.equals(idleTrack.stationId, stationId)) { |
| | | idleTrack = new StationTaskIdleTrack(taskNo, stationId, now); |
| | | saveStationTaskIdleTrack(idleTrack); |
| | | } |
| | | return idleTrack; |
| | | } |
| | | |
| | | private StationTaskIdleTrack getStationTaskIdleTrack(Integer taskNo) { |
| | | if (taskNo == null || taskNo <= 0) { |
| | | return null; |
| | | } |
| | | Object obj = redisUtil.get(RedisKeyType.STATION_TASK_IDLE_TRACK_.key + taskNo); |
| | | if (obj == null) { |
| | | return null; |
| | | } |
| | | try { |
| | | return JSON.parseObject(obj.toString(), StationTaskIdleTrack.class); |
| | | } catch (Exception e) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private void saveStationTaskIdleTrack(StationTaskIdleTrack idleTrack) { |
| | | if (idleTrack == null || idleTrack.taskNo == null || idleTrack.taskNo <= 0) { |
| | | return; |
| | | } |
| | | redisUtil.set( |
| | | RedisKeyType.STATION_TASK_IDLE_TRACK_.key + idleTrack.taskNo, |
| | | JSON.toJSONString(idleTrack, SerializerFeature.DisableCircularReferenceDetect), |
| | | STATION_IDLE_TRACK_EXPIRE_SECONDS |
| | | ); |
| | | } |
| | | |
| | | public Integer getOutStationBatchSeq(List<NavigateNode> pathList, Integer searchStationId, String searchBatch) { |
| | | if (pathList == null || pathList.isEmpty() || searchStationId == null || Cools.isEmpty(searchBatch)) { |
| | | return null; |
| | | } |
| | | List<Integer> checkList = new ArrayList<>(); |
| | | for (int i = pathList.size() - 1; i >= 0; i--) { |
| | | NavigateNode node = pathList.get(i); |
| | |
| | | } |
| | | |
| | | state.totalStationCount = toNonNegative(capacityVo.getTotalStationCount()); |
| | | state.projectedTaskStationCount = toNonNegative(capacityVo.getTaskStationCount()); |
| | | Integer occupiedStationCount = capacityVo.getOccupiedStationCount(); |
| | | state.projectedTaskStationCount = toNonNegative(occupiedStationCount != null ? occupiedStationCount : capacityVo.getTaskStationCount()); |
| | | |
| | | List<StationCycleLoopVo> loopList = capacityVo.getLoopList(); |
| | | if (loopList != null) { |
| | |
| | | return value; |
| | | } |
| | | |
| | | private static class OutOrderDispatchDecision { |
| | | private final Integer targetStationId; |
| | | private final boolean circle; |
| | | |
| | | private OutOrderDispatchDecision(Integer targetStationId, boolean circle) { |
| | | this.targetStationId = targetStationId; |
| | | this.circle = circle; |
| | | } |
| | | |
| | | private Integer getTargetStationId() { |
| | | return targetStationId; |
| | | } |
| | | |
| | | private boolean isCircle() { |
| | | return circle; |
| | | } |
| | | } |
| | | |
| | | private void saveLoopLoadReserve(Integer wrkNo, LoopHitResult loopHitResult) { |
| | | if (wrkNo == null || wrkNo <= 0 || loopHitResult == null || !loopHitResult.isThroughLoop()) { |
| | | return; |
| | |
| | | redisUtil.expire(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, LOOP_LOAD_RESERVE_EXPIRE_SECONDS); |
| | | } |
| | | |
| | | private DispatchLimitConfig getDispatchLimitConfig() { |
| | | private DispatchLimitConfig getDispatchLimitConfig(Integer startStationId, Integer endStationId) { |
| | | DispatchLimitConfig config = new DispatchLimitConfig(); |
| | | Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (!(systemConfigMapObj instanceof Map)) { |
| | | return config; |
| | | if (systemConfigMapObj instanceof Map) { |
| | | Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj; |
| | | config.circleMaxLoadLimit = parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.circleMaxLoadLimit); |
| | | String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable"); |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable"); |
| | | } |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "isCircleMode"); |
| | | } |
| | | config.loopModeEnable = parseBoolean(loopModeValue, config.loopModeEnable); |
| | | } |
| | | Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj; |
| | | |
| | | config.circleMaxLoadLimit = parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.circleMaxLoadLimit); |
| | | String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable"); |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable"); |
| | | if (stationPathPolicyService != null && startStationId != null && endStationId != null) { |
| | | try { |
| | | StationPathResolvedPolicy resolvedPolicy = stationPathPolicyService.resolvePolicy(startStationId, endStationId); |
| | | if (resolvedPolicy != null && resolvedPolicy.getProfileConfig() != null) { |
| | | config.circleMaxLoadLimit = parseLoadLimit(String.valueOf(resolvedPolicy.getProfileConfig().getCircleMaxLoadLimit()), config.circleMaxLoadLimit); |
| | | } |
| | | } catch (Exception ignore) { |
| | | } |
| | | } |
| | | if (isBlank(loopModeValue)) { |
| | | loopModeValue = getConfigValue(systemConfigMap, "isCircleMode"); |
| | | } |
| | | config.loopModeEnable = parseBoolean(loopModeValue, config.loopModeEnable); |
| | | |
| | | return config; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private static class StationTaskIdleTrack { |
| | | private Integer taskNo; |
| | | private Integer stationId; |
| | | private Long firstSeenTime; |
| | | |
| | | private StationTaskIdleTrack() {} |
| | | |
| | | private StationTaskIdleTrack(Integer taskNo, Integer stationId, Long firstSeenTime) { |
| | | this.taskNo = taskNo; |
| | | this.stationId = stationId; |
| | | this.firstSeenTime = firstSeenTime; |
| | | } |
| | | |
| | | private boolean isTimeout(int seconds) { |
| | | if (firstSeenTime == null) { |
| | | return false; |
| | | } |
| | | return System.currentTimeMillis() - firstSeenTime >= seconds * 1000L; |
| | | } |
| | | |
| | | public Integer getTaskNo() { |
| | | return taskNo; |
| | | } |
| | | |
| | | public void setTaskNo(Integer taskNo) { |
| | | this.taskNo = taskNo; |
| | | } |
| | | |
| | | public Integer getStationId() { |
| | | return stationId; |
| | | } |
| | | |
| | | public void setStationId(Integer stationId) { |
| | | this.stationId = stationId; |
| | | } |
| | | |
| | | public Long getFirstSeenTime() { |
| | | return firstSeenTime; |
| | | } |
| | | |
| | | public void setFirstSeenTime(Long firstSeenTime) { |
| | | this.firstSeenTime = firstSeenTime; |
| | | } |
| | | } |
| | | |
| | | } |