| | |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.beans.factory.annotation.Value; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | @Component |
| | | public class StationOutboundDispatchProcessor { |
| | | |
| | | private static final int PENDING_DISPATCH_EXPIRE_SECONDS = 60 * 10; |
| | | private static final int PENDING_DISPATCH_EXPIRE_SECONDS = 60 * 60 * 24; |
| | | private static final long DEFAULT_PENDING_DISCOVER_TIMEOUT_MS = 60_000L; |
| | | private static final long DEFAULT_PENDING_SESSION_PROTECT_MS = 90_000L; |
| | | private static final long DEFAULT_RECENT_DISPATCH_PROTECT_MS = 60_000L; |
| | |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | Object pendingObj = redisUtil.get(RedisKeyType.STATION_OUT_PENDING_DISPATCH_.key + wrkMast.getWrkNo()); |
| | | if (pendingObj != null) { |
| | | if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.OUTBOUND_RUN_COMPLETE.sts)) { |
| | | clearPendingDispatch(wrkMast.getWrkNo()); |
| | | return; |
| | | } |
| | | |
| | | StationObjModel pendingStationObjModel = getOutboundSourceStation(wrkMast); |
| | | if (pendingStationObjModel == null |
| | | || pendingStationObjModel.getDeviceNo() == null |
| | | || pendingStationObjModel.getStationId() == null) { |
| | | clearPendingDispatch(wrkMast.getWrkNo()); |
| | | return; |
| | | } |
| | | |
| | | StationThread pendingStationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, pendingStationObjModel.getDeviceNo()); |
| | | if (pendingStationThread != null) { |
| | | List<Integer> taskNoList = pendingStationThread.getAllTaskNoList(); |
| | | if (taskNoList != null && taskNoList.contains(wrkMast.getWrkNo())) { |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markOutboundStationStart(wrkMast, now); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Devp), pendingStationObjModel.getDeviceNo(), |
| | | String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), |
| | | NotifyMsgType.STATION_OUT_TASK_RUN, null); |
| | | clearPendingDispatch(wrkMast.getWrkNo()); |
| | | News.info("输送设备已发现任务号,任务转运行中。deviceNo={},源站={},工作号={}", |
| | | pendingStationObjModel.getDeviceNo(), pendingStationObjModel.getStationId(), wrkMast.getWrkNo()); |
| | | } |
| | | return; |
| | | } |
| | | } |
| | | |
| | | long createdAt; |
| | | try { |
| | | createdAt = Long.parseLong(String.valueOf(pendingObj)); |
| | | } catch (Exception ignore) { |
| | | createdAt = System.currentTimeMillis(); |
| | | } |
| | | long pendingDiscoverTimeoutMs = resolvePendingDiscoverTimeoutMs(); |
| | | if (System.currentTimeMillis() - createdAt < pendingDiscoverTimeoutMs) { |
| | | return; |
| | | } |
| | | |
| | | PendingDispatchGuardResult guardResult = evaluatePendingDispatchGuard(wrkMast, pendingStationObjModel, pendingStationThread); |
| | | if (guardResult.keepPending()) { |
| | | News.info("输送站点等待源站执行超时后继续保持等待。工作号={},源站={},原因={}", |
| | | wrkMast.getWrkNo(), |
| | | pendingStationObjModel.getStationId(), |
| | | guardResult.reason()); |
| | | markPendingDispatch(wrkMast.getWrkNo()); |
| | | return; |
| | | } |
| | | clearPendingDispatch(wrkMast.getWrkNo()); |
| | | News.warn("输送站点执行确认超时,且未发现有效活动链路,已释放重试资格。工作号={}", wrkMast.getWrkNo()); |
| | | } |
| | | |
| | | StationObjModel stationObjModel = getOutboundSourceStation(wrkMast); |
| | | if (stationObjModel == null || stationObjModel.getDeviceNo() == null || stationObjModel.getStationId() == null) { |
| | | if (shouldStopAfterPendingDispatchCheck(wrkMast)) { |
| | | return; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | return; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = stationMap == null ? null : stationMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | | return; |
| | | } |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId()); |
| | | if (lock != null) { |
| | | return; |
| | | } |
| | | |
| | | if (!(stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0)) { |
| | | return; |
| | | } |
| | | |
| | | Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast); |
| | | List<Integer> outOrderList = stationOutboundDecisionSupport.getAllOutOrderList(); |
| | | OutOrderDispatchDecision dispatchDecision = |
| | | stationOutboundDecisionSupport.resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderList, |
| | | pathLenFactor |
| | | ); |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null) { |
| | | return; |
| | | } |
| | | |
| | | DispatchLimitConfig limitConfig = |
| | | stationDispatchLoadSupport.getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo); |
| | | int currentStationTaskCount = stationDispatchLoadSupport.countCurrentStationTask(); |
| | | LoadGuardState loadGuardState = stationDispatchLoadSupport.buildLoadGuardState(limitConfig); |
| | | LoopHitResult loopHitResult = |
| | | stationDispatchLoadSupport.findPathLoopHit( |
| | | limitConfig, |
| | | stationProtocol.getStationId(), |
| | | moveStaNo, |
| | | loadGuardState, |
| | | wrkMast, |
| | | pathLenFactor |
| | | ); |
| | | if (stationDispatchLoadSupport.isDispatchBlocked( |
| | | limitConfig, |
| | | currentStationTaskCount, |
| | | loadGuardState, |
| | | loopHitResult.isThroughLoop())) { |
| | | return; |
| | | } |
| | | |
| | | StationCommand command = stationOutboundDecisionSupport.buildOutboundMoveCommand( |
| | | stationThread, |
| | | wrkMast, |
| | | stationProtocol.getStationId(), |
| | | moveStaNo, |
| | | pathLenFactor |
| | | ); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | return; |
| | | } |
| | | |
| | | boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "crnStationOutExecute"); |
| | | if (!offered) { |
| | | return; |
| | | } |
| | | if (stationMoveCoordinator != null) { |
| | | stationMoveCoordinator.recordDispatch( |
| | | wrkMast.getWrkNo(), |
| | | stationProtocol.getStationId(), |
| | | "crnStationOutExecute", |
| | | command, |
| | | false |
| | | ); |
| | | } |
| | | markPendingDispatch(wrkMast.getWrkNo()); |
| | | News.info("输送站点出库命令已入设备执行链路,等待源站执行。站点号={},工作号={},命令数据={}", |
| | | stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5); |
| | | loadGuardState.reserveLoopTask(loopHitResult.getLoopNo()); |
| | | stationDispatchLoadSupport.saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult); |
| | | dispatchCrnStationOutTask(wrkMast); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public void confirmPendingCrnStationOutDispatch(WrkMast wrkMast) { |
| | | try { |
| | | shouldStopAfterPendingDispatchCheck(wrkMast); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | public void dispatchNextCrnStationOutTask(Integer sourceStationId) { |
| | | try { |
| | | if (sourceStationId == null) { |
| | | return; |
| | | } |
| | | List<WrkMast> sameSourceTaskList = loadCrnOutboundCompleteTasksBySourceStation(sourceStationId); |
| | | if (sameSourceTaskList.isEmpty()) { |
| | | return; |
| | | } |
| | | WrkMast dispatchCandidate = null; |
| | | int pendingTaskCount = 0; |
| | | int dispatchableTaskCount = 0; |
| | | for (WrkMast sameSourceTask : sameSourceTaskList) { |
| | | if (sameSourceTask == null || sameSourceTask.getWrkNo() == null) { |
| | | continue; |
| | | } |
| | | if (hasPendingDispatch(sameSourceTask.getWrkNo())) { |
| | | pendingTaskCount++; |
| | | continue; |
| | | } |
| | | dispatchableTaskCount++; |
| | | if (dispatchCandidate == null) { |
| | | dispatchCandidate = sameSourceTask; |
| | | } |
| | | } |
| | | if (dispatchCandidate == null) { |
| | | return; |
| | | } |
| | | if (dispatchableTaskCount > 1) { |
| | | News.taskError(dispatchCandidate.getWrkNo(), |
| | | "出库异常:同一源站存在多笔待下发搬运完成任务,不下发命令。源站={},待下发数量={},待确认数量={}", |
| | | sourceStationId, dispatchableTaskCount, pendingTaskCount); |
| | | return; |
| | | } |
| | | if (pendingTaskCount > 0 || sameSourceTaskList.size() > 1) { |
| | | News.info("源站出库候选任务已确定。源站={},候选工作号={},待确认数量={},待下发数量={}", |
| | | sourceStationId, |
| | | dispatchCandidate.getWrkNo(), |
| | | pendingTaskCount, |
| | | dispatchableTaskCount); |
| | | } |
| | | dispatchCrnStationOutTask(dispatchCandidate); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | |
| | | private boolean shouldStopAfterPendingDispatchCheck(WrkMast wrkMast) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return true; |
| | | } |
| | | Object pendingDispatchMarker = redisUtil.get(RedisKeyType.STATION_OUT_PENDING_DISPATCH_.key + wrkMast.getWrkNo()); |
| | | if (pendingDispatchMarker == null) { |
| | | return false; |
| | | } |
| | | return processPendingDispatch(wrkMast, pendingDispatchMarker); |
| | | } |
| | | |
| | | private boolean processPendingDispatch(WrkMast wrkMast, Object pendingDispatchMarker) { |
| | | if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.OUTBOUND_RUN_COMPLETE.sts)) { |
| | | clearPendingDispatch(wrkMast); |
| | | return true; |
| | | } |
| | | |
| | | StationObjModel sourceStationObjModel = getOutboundSourceStation(wrkMast); |
| | | if (sourceStationObjModel == null |
| | | || sourceStationObjModel.getDeviceNo() == null |
| | | || sourceStationObjModel.getStationId() == null) { |
| | | clearPendingDispatch(wrkMast); |
| | | return true; |
| | | } |
| | | |
| | | StationThread sourceStationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, sourceStationObjModel.getDeviceNo()); |
| | | if (sourceStationThread != null) { |
| | | List<Integer> taskNoList = sourceStationThread.getAllTaskNoList(); |
| | | if (taskNoList != null && taskNoList.contains(wrkMast.getWrkNo())) { |
| | | Date now = new Date(); |
| | | wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts); |
| | | wrkMast.setSystemMsg(""); |
| | | wrkMast.setIoTime(now); |
| | | wrkMast.setModiTime(now); |
| | | if (wrkMastService.updateById(wrkMast)) { |
| | | wrkAnalysisService.markOutboundStationStart(wrkMast, now); |
| | | notifyUtils.notify(String.valueOf(SlaveType.Devp), sourceStationObjModel.getDeviceNo(), |
| | | String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), |
| | | NotifyMsgType.STATION_OUT_TASK_RUN, null); |
| | | clearPendingDispatch(wrkMast); |
| | | News.info("输送设备已发现任务号,任务转运行中。deviceNo={},源站={},工作号={}", |
| | | sourceStationObjModel.getDeviceNo(), sourceStationObjModel.getStationId(), wrkMast.getWrkNo()); |
| | | } |
| | | return true; |
| | | } |
| | | } |
| | | |
| | | long createdAt; |
| | | try { |
| | | createdAt = Long.parseLong(String.valueOf(pendingDispatchMarker)); |
| | | } catch (Exception ignore) { |
| | | createdAt = System.currentTimeMillis(); |
| | | } |
| | | long pendingDiscoverTimeoutMs = resolvePendingDiscoverTimeoutMs(); |
| | | if (System.currentTimeMillis() - createdAt < pendingDiscoverTimeoutMs) { |
| | | return true; |
| | | } |
| | | |
| | | PendingDispatchGuardResult guardResult = evaluatePendingDispatchGuard(wrkMast, sourceStationObjModel, sourceStationThread); |
| | | if (guardResult.keepPending()) { |
| | | News.info("输送站点等待源站执行超时后继续保持等待。工作号={},源站={},原因={}", |
| | | wrkMast.getWrkNo(), |
| | | sourceStationObjModel.getStationId(), |
| | | guardResult.reason()); |
| | | markPendingDispatch(wrkMast.getWrkNo()); |
| | | return true; |
| | | } |
| | | clearPendingDispatch(wrkMast); |
| | | News.warn("输送站点执行确认超时,且未发现有效活动链路,已释放重试资格。工作号={}", wrkMast.getWrkNo()); |
| | | return false; |
| | | } |
| | | |
| | | public void dualCrnStationOutExecute(WrkMast wrkMast) { |
| | |
| | | if (wrkMast == null || wrkMast.getSourceStaNo() == null) { |
| | | return null; |
| | | } |
| | | BasStation basStation = basStationService.getById(wrkMast.getSourceStaNo()); |
| | | return getOutboundSourceStation(wrkMast.getSourceStaNo()); |
| | | } |
| | | |
| | | private StationObjModel getOutboundSourceStation(Integer sourceStationId) { |
| | | if (sourceStationId == null) { |
| | | return null; |
| | | } |
| | | BasStation basStation = basStationService.getById(sourceStationId); |
| | | if (basStation == null || basStation.getDeviceNo() == null) { |
| | | return null; |
| | | } |
| | | StationObjModel stationObjModel = new StationObjModel(); |
| | | stationObjModel.setStationId(wrkMast.getSourceStaNo()); |
| | | stationObjModel.setStationId(sourceStationId); |
| | | stationObjModel.setDeviceNo(basStation.getDeviceNo()); |
| | | return stationObjModel; |
| | | } |
| | | |
| | | private void dispatchCrnStationOutTask(WrkMast wrkMast) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | StationObjModel stationObjModel = getOutboundSourceStation(wrkMast); |
| | | if (stationObjModel == null || stationObjModel.getDeviceNo() == null || stationObjModel.getStationId() == null) { |
| | | return; |
| | | } |
| | | |
| | | StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo()); |
| | | if (stationThread == null) { |
| | | return; |
| | | } |
| | | |
| | | Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap(); |
| | | StationProtocol stationProtocol = stationMap == null ? null : stationMap.get(stationObjModel.getStationId()); |
| | | if (stationProtocol == null) { |
| | | return; |
| | | } |
| | | |
| | | Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId()); |
| | | if (lock != null) { |
| | | return; |
| | | } |
| | | |
| | | if (!(stationProtocol.isAutoing() |
| | | && stationProtocol.isLoading() |
| | | && stationProtocol.getTaskNo() == 0)) { |
| | | return; |
| | | } |
| | | |
| | | Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast); |
| | | List<Integer> outOrderList = stationOutboundDecisionSupport.getAllOutOrderList(); |
| | | OutOrderDispatchDecision dispatchDecision = |
| | | stationOutboundDecisionSupport.resolveOutboundDispatchDecision( |
| | | stationProtocol.getStationId(), |
| | | wrkMast, |
| | | outOrderList, |
| | | pathLenFactor |
| | | ); |
| | | Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); |
| | | if (moveStaNo == null) { |
| | | return; |
| | | } |
| | | |
| | | DispatchLimitConfig limitConfig = |
| | | stationDispatchLoadSupport.getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo); |
| | | int currentStationTaskCount = stationDispatchLoadSupport.countCurrentStationTask(); |
| | | LoadGuardState loadGuardState = stationDispatchLoadSupport.buildLoadGuardState(limitConfig); |
| | | LoopHitResult loopHitResult = |
| | | stationDispatchLoadSupport.findPathLoopHit( |
| | | limitConfig, |
| | | stationProtocol.getStationId(), |
| | | moveStaNo, |
| | | loadGuardState, |
| | | wrkMast, |
| | | pathLenFactor |
| | | ); |
| | | if (stationDispatchLoadSupport.isDispatchBlocked( |
| | | limitConfig, |
| | | currentStationTaskCount, |
| | | loadGuardState, |
| | | loopHitResult.isThroughLoop())) { |
| | | return; |
| | | } |
| | | |
| | | StationCommand command = stationOutboundDecisionSupport.buildOutboundMoveCommand( |
| | | stationThread, |
| | | wrkMast, |
| | | stationProtocol.getStationId(), |
| | | moveStaNo, |
| | | pathLenFactor |
| | | ); |
| | | if (command == null) { |
| | | News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败"); |
| | | return; |
| | | } |
| | | |
| | | boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "crnStationOutExecute"); |
| | | if (!offered) { |
| | | return; |
| | | } |
| | | if (stationMoveCoordinator != null) { |
| | | stationMoveCoordinator.recordDispatch( |
| | | wrkMast.getWrkNo(), |
| | | stationProtocol.getStationId(), |
| | | "crnStationOutExecute", |
| | | command, |
| | | false |
| | | ); |
| | | } |
| | | markPendingDispatch(wrkMast.getWrkNo()); |
| | | News.info("输送站点出库命令已入设备执行链路,等待源站执行。站点号={},工作号={},命令数据={}", |
| | | stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command)); |
| | | redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5); |
| | | loadGuardState.reserveLoopTask(loopHitResult.getLoopNo()); |
| | | stationDispatchLoadSupport.saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult); |
| | | } |
| | | |
| | | private List<WrkMast> loadCrnOutboundCompleteTasksBySourceStation(Integer sourceStationId) { |
| | | if (sourceStationId == null) { |
| | | return new ArrayList<>(); |
| | | } |
| | | return wrkMastService.list(new QueryWrapper<WrkMast>() |
| | | .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts) |
| | | .eq("source_sta_no", sourceStationId) |
| | | .isNotNull("crn_no") |
| | | .orderByAsc("io_time", "wrk_no")); |
| | | } |
| | | |
| | | private boolean hasPendingDispatch(Integer wrkNo) { |
| | |
| | | redisUtil.del(RedisKeyType.STATION_OUT_PENDING_DISPATCH_.key + wrkNo); |
| | | } |
| | | |
| | | private void clearPendingDispatch(WrkMast wrkMast) { |
| | | if (wrkMast == null || wrkMast.getWrkNo() == null) { |
| | | return; |
| | | } |
| | | clearPendingDispatch(wrkMast.getWrkNo()); |
| | | } |
| | | |
| | | private PendingDispatchGuardResult evaluatePendingDispatchGuard(WrkMast wrkMast, |
| | | StationObjModel stationObjModel, |
| | | StationThread stationThread) { |