package com.zy.core.utils.station; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.core.common.Cools; import com.core.exception.CoolException; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.LocMast; import com.zy.asrs.entity.WrkMast; import com.zy.asrs.service.LocMastService; import com.zy.asrs.service.WrkMastService; import com.zy.common.entity.FindCrnNoResult; import com.zy.common.model.NavigateNode; import com.zy.common.model.StartupDto; import com.zy.common.service.CommonService; import com.zy.common.utils.NavigateUtils; import com.zy.common.utils.RedisUtil; import com.zy.core.News; import com.zy.core.cache.SlaveConnection; import com.zy.core.dispatch.StationCommandDispatchResult; import com.zy.core.dispatch.StationCommandDispatcher; import com.zy.core.enums.RedisKeyType; import com.zy.core.enums.SlaveType; import com.zy.core.enums.StationCommandType; import com.zy.core.enums.WrkIoType; import com.zy.core.enums.WrkStsType; import com.zy.core.model.StationObjModel; import com.zy.core.model.command.StationCommand; import com.zy.core.model.protocol.StationProtocol; import com.zy.core.model.protocol.StationTaskBufferItem; import com.zy.core.move.StationMoveCoordinator; import com.zy.core.thread.StationThread; import com.zy.core.utils.station.model.OutOrderDispatchDecision; import com.zy.core.utils.station.model.RerouteCommandPlan; import com.zy.core.utils.station.model.RerouteContext; import com.zy.core.utils.station.model.RerouteDecision; import com.zy.core.utils.station.model.RerouteExecutionResult; import com.zy.core.utils.station.model.RerouteSceneType; import com.zy.core.utils.WmsOperateUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Objects; @Slf4j @Component public class StationRerouteProcessor { private static final int OUT_ORDER_DISPATCH_LIMIT_SECONDS = 2; private static final long STATION_MOVE_RESET_WAIT_MS = 1000L; private static final int RUN_BLOCK_DIRECT_REASSIGN_LIMIT_SECONDS = 8 * 60; private static final int RUN_BLOCK_DIRECT_REASSIGN_NEAREST_CACHE_SECONDS = 60 * 60 * 24; private static final long CHECK_STATION_OUT_ORDER_SLOW_THRESHOLD_MS = 200L; private static final long EXECUTE_REROUTE_PLAN_SLOW_THRESHOLD_MS = 200L; private static final String RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING_PREFIX = "堵塞重分配请求WMS失败"; private static final String RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING = RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING_PREFIX + ",请检查WMS重新分配库位接口"; @Autowired private WrkMastService wrkMastService; @Autowired private CommonService commonService; @Autowired private RedisUtil redisUtil; @Autowired private LocMastService locMastService; @Autowired private WmsOperateUtils wmsOperateUtils; @Autowired private StationMoveCoordinator stationMoveCoordinator; @Autowired private StationCommandDispatcher stationCommandDispatcher; @Autowired private StationOutboundDecisionSupport stationOutboundDecisionSupport; @Autowired private StationDispatchRuntimeStateSupport stationDispatchRuntimeStateSupport; @Autowired private NavigateUtils navigateUtils; public void checkStationRunBlock(BasDevp basDevp, Integer stationId) { try { if (basDevp == null || basDevp.getDevpNo() == null || stationId == null) { return; } if (shouldSkipRunBlockStation(basDevp, stationId)) { return; } StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); if (stationThread == null) { return; } Map statusMap = stationThread.getStatusMap(); StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(stationId); if (stationProtocol == null || !stationProtocol.isAutoing() || !stationProtocol.isLoading() || stationProtocol.getTaskNo() <= 0 || !stationProtocol.isRunBlock()) { return; } List runBlockReassignLocStationList = new ArrayList<>(); for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) { runBlockReassignLocStationList.add(stationObjModel.getStationId()); } List outOrderStationIds = basDevp.getOutOrderIntList(); WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); if (wrkMast == null) { News.info("输送站点号={} 运行阻塞,但无法找到对应任务,工作号={}", stationProtocol.getStationId(), stationProtocol.getTaskNo()); return; } Object lock = redisUtil.get(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo()); if (lock != null) { return; } redisUtil.set(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo(), "lock", 30); if (shouldUseRunBlockDirectReassign(wrkMast, stationProtocol.getStationId(), runBlockReassignLocStationList)) { if (stationMoveCoordinator != null) { stationMoveCoordinator.withTaskDispatchLock(stationProtocol.getTaskNo(), () -> { executeRunBlockDirectReassign(basDevp, stationThread, stationProtocol, wrkMast); return null; }); } else { executeRunBlockDirectReassign(basDevp, stationThread, stationProtocol, wrkMast); } return; } Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast); RerouteContext context = RerouteContext.create( RerouteSceneType.RUN_BLOCK_REROUTE, basDevp, stationThread, stationProtocol, wrkMast, outOrderStationIds, pathLenFactor, "checkStationRunBlock_reroute" ).withRunBlockCommand() .withSuppressDispatchGuard() .withCancelSessionBeforeDispatch() .withResetSegmentCommandsBeforeDispatch(); executeSharedReroute(context); } catch (Exception e) { e.printStackTrace(); } } public void checkStationOutOrder(BasDevp basDevp, StationObjModel stationObjModel) { long totalStartMs = System.currentTimeMillis(); try { if (basDevp == null || basDevp.getDevpNo() == null || stationObjModel == null || stationObjModel.getStationId() == null) { return; } long runtimeStartMs = System.currentTimeMillis(); StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); if (stationThread == null) { return; } Map statusMap = stationThread.getStatusMap(); StationProtocol stationProtocol = statusMap == null ? null : statusMap.get(stationObjModel.getStationId()); long runtimeCostMs = System.currentTimeMillis() - runtimeStartMs; if (stationProtocol == null || !stationProtocol.isAutoing() || !stationProtocol.isLoading() || stationProtocol.getTaskNo() <= 0 || stationProtocol.isRunBlock() || !stationProtocol.getStationId().equals(stationProtocol.getTargetStaNo())) { return; } long loadWrkStartMs = System.currentTimeMillis(); WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo()); long loadWrkCostMs = System.currentTimeMillis() - loadWrkStartMs; if (wrkMast == null || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts) || Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) { return; } long skipCheckStartMs = System.currentTimeMillis(); boolean skipForExistingRoute = stationOutboundDecisionSupport .shouldSkipOutOrderDispatchForExistingRoute(wrkMast.getWrkNo(), stationProtocol.getStationId()); long skipCheckCostMs = System.currentTimeMillis() - skipCheckStartMs; if (skipForExistingRoute) { return; } long pathFactorStartMs = System.currentTimeMillis(); Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast); long pathFactorCostMs = System.currentTimeMillis() - pathFactorStartMs; RerouteContext context = RerouteContext.create( RerouteSceneType.OUT_ORDER, basDevp, stationThread, stationProtocol, wrkMast, basDevp.getOutOrderIntList(), pathLenFactor, "checkStationOutOrder" ).withDispatchDeviceNo(stationObjModel.getDeviceNo()) .withSuppressDispatchGuard() .withOutOrderDispatchLock() .withResetSegmentCommandsBeforeDispatch(); long rerouteStartMs = System.currentTimeMillis(); RerouteExecutionResult result = executeSharedReroute(context); long rerouteCostMs = System.currentTimeMillis() - rerouteStartMs; long totalCostMs = System.currentTimeMillis() - totalStartMs; log.info("checkStationOutOrder profile, taskNo={}, stationId={}, deviceNo={}, batch={}, batchSeq={}, finalTargetStationId={}, pathLenFactor={}, runtimeCostMs={}ms, loadWrkCostMs={}ms, skipCheckCostMs={}ms, pathFactorCostMs={}ms, rerouteCostMs={}ms, totalCostMs={}ms, result={}", stationProtocol.getTaskNo(), stationProtocol.getStationId(), stationObjModel.getDeviceNo(), wrkMast.getBatch(), wrkMast.getBatchSeq(), wrkMast.getStaNo(), pathLenFactor, runtimeCostMs, loadWrkCostMs, skipCheckCostMs, pathFactorCostMs, rerouteCostMs, totalCostMs, result == null ? null : result.skipReason()); if (totalCostMs > CHECK_STATION_OUT_ORDER_SLOW_THRESHOLD_MS) { log.warn("checkStationOutOrder slow, taskNo={}, stationId={}, totalCostMs={}ms, rerouteCostMs={}ms, loadWrkCostMs={}ms, pathFactorCostMs={}ms", stationProtocol.getTaskNo(), stationProtocol.getStationId(), totalCostMs, rerouteCostMs, loadWrkCostMs, pathFactorCostMs); } } catch (Exception e) { e.printStackTrace(); } } public RerouteCommandPlan buildRerouteCommandPlan(RerouteContext context, RerouteDecision decision) { if (context == null) { return RerouteCommandPlan.skip("missing-context"); } if (decision == null) { return RerouteCommandPlan.skip("missing-decision"); } if (decision.skip()) { return RerouteCommandPlan.skip(decision.skipReason()); } if (context.stationThread() == null || context.stationProtocol() == null || context.wrkMast() == null) { return RerouteCommandPlan.skip("missing-runtime-dependency"); } Integer currentStationId = context.stationProtocol().getStationId(); Integer targetStationId = decision.targetStationId(); if (currentStationId == null || targetStationId == null) { return RerouteCommandPlan.skip("missing-target-station"); } if (Objects.equals(currentStationId, targetStationId)) { return RerouteCommandPlan.skip("same-station"); } StationCommand command = context.useRunBlockCommand() ? context.stationThread().getRunBlockRerouteCommand( context.wrkMast().getWrkNo(), currentStationId, targetStationId, 0, context.pathLenFactor() ) : stationOutboundDecisionSupport.buildOutboundMoveCommand( context.stationThread(), context.wrkMast(), currentStationId, targetStationId, context.pathLenFactor() ); if (command == null) { if (context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE) { News.taskInfo(context.wrkMast().getWrkNo(), "输送站点堵塞重规划未找到可下发路线,当前站点={},目标站点={}", currentStationId, targetStationId); } else if (context.sceneType() == RerouteSceneType.IDLE_RECOVER) { News.taskInfo(context.wrkMast().getWrkNo(), "站点任务停留超时后重算路径失败,当前站点={},目标站点={}", currentStationId, targetStationId); } else { News.taskInfo(context.wrkMast().getWrkNo(), "获取输送线命令失败"); } return RerouteCommandPlan.skip("missing-command"); } return RerouteCommandPlan.dispatch(command, decision, context.dispatchScene()); } public RerouteExecutionResult executeReroutePlan(RerouteContext context, RerouteCommandPlan plan) { if (context == null) { return RerouteExecutionResult.skip("missing-context"); } if (plan == null) { return RerouteExecutionResult.skip("missing-plan"); } if (plan.skip()) { return RerouteExecutionResult.skip(plan.skipReason()); } StationProtocol stationProtocol = context.stationProtocol(); if (stationProtocol == null) { return RerouteExecutionResult.skip("missing-station-protocol"); } Integer taskNo = stationProtocol.getTaskNo(); Integer stationId = stationProtocol.getStationId(); if (taskNo == null || taskNo <= 0 || stationId == null) { return RerouteExecutionResult.skip("invalid-station-task"); } if (stationMoveCoordinator != null) { return stationMoveCoordinator.withTaskDispatchLock(taskNo, () -> executeReroutePlanWithTaskLock(context, plan, stationProtocol, taskNo, stationId)); } return executeReroutePlanWithTaskLock(context, plan, stationProtocol, taskNo, stationId); } public RerouteDecision resolveSharedRerouteDecision(RerouteContext context) { long startMs = System.currentTimeMillis(); if (context == null || context.wrkMast() == null || context.stationProtocol() == null) { return RerouteDecision.skip("missing-runtime-dependency"); } Integer currentStationId = context.stationProtocol().getStationId(); if (currentStationId == null) { return RerouteDecision.skip("missing-current-station"); } if (context.sceneType() == RerouteSceneType.IDLE_RECOVER && !Objects.equals(context.wrkMast().getWrkSts(), WrkStsType.STATION_RUN.sts)) { Integer targetStationId = context.wrkMast().getStaNo(); RerouteDecision decision = targetStationId == null || Objects.equals(targetStationId, currentStationId) ? RerouteDecision.skip("same-station") : RerouteDecision.proceed(targetStationId); log.info("resolveSharedRerouteDecision profile, sceneType={}, taskNo={}, currentStationId={}, targetStationId={}, decision={}, decisionCostMs={}ms", context.sceneType(), context.wrkMast().getWrkNo(), currentStationId, targetStationId, decision.skip() ? decision.skipReason() : "proceed", System.currentTimeMillis() - startMs); return decision; } OutOrderDispatchDecision dispatchDecision = stationOutboundDecisionSupport.resolveOutboundDispatchDecision( currentStationId, context.wrkMast(), context.outOrderStationIds(), context.pathLenFactor() ); Integer targetStationId = dispatchDecision == null ? null : dispatchDecision.getTargetStationId(); RerouteDecision decision = targetStationId == null || Objects.equals(targetStationId, currentStationId) ? RerouteDecision.skip("same-station") : RerouteDecision.proceed(targetStationId, dispatchDecision); log.info("resolveSharedRerouteDecision profile, sceneType={}, taskNo={}, currentStationId={}, targetStationId={}, decision={}, circle={}, decisionCostMs={}ms", context.sceneType(), context.wrkMast().getWrkNo(), currentStationId, targetStationId, decision.skip() ? decision.skipReason() : "proceed", dispatchDecision != null && dispatchDecision.isCircle(), System.currentTimeMillis() - startMs); return decision; } public boolean shouldUseRunBlockDirectReassign(WrkMast wrkMast, Integer stationId, List runBlockReassignLocStationList) { if (wrkMast == null || stationId == null) { return false; } if (!Objects.equals(wrkMast.getIoType(), WrkIoType.IN.id)) { return false; } if (runBlockReassignLocStationList == null || !runBlockReassignLocStationList.contains(stationId)) { return false; } if (shouldForceRunBlockPathReroute(wrkMast, stationId, runBlockReassignLocStationList)) { return false; } return true; } private boolean shouldForceRunBlockPathReroute(WrkMast wrkMast, Integer stationId, List runBlockReassignLocStationList) { if (wrkMast == null || stationId == null) { return false; } Integer nearestStationId = resolveNearestRunBlockDirectReassignStationId(wrkMast, runBlockReassignLocStationList); return nearestStationId != null && !Objects.equals(stationId, nearestStationId); } private Integer resolveNearestRunBlockDirectReassignStationId(WrkMast wrkMast, List runBlockReassignLocStationList) { if (wrkMast == null || wrkMast.getStaNo() == null || navigateUtils == null || runBlockReassignLocStationList == null || runBlockReassignLocStationList.isEmpty()) { return null; } Integer targetStationId = wrkMast.getStaNo(); Integer cachedStationId = loadCachedNearestRunBlockDirectReassignStationId(targetStationId, runBlockReassignLocStationList); if (cachedStationId != null) { return cachedStationId; } Integer nearestStationId = null; int nearestPathLen = Integer.MAX_VALUE; for (Integer candidateStationId : runBlockReassignLocStationList) { if (candidateStationId == null) { continue; } List path = navigateUtils.calcOptimalPathByStationId(candidateStationId, targetStationId, wrkMast.getWrkNo(), null); if (path == null || path.isEmpty()) { continue; } int pathLen = countStationNodes(path); if (pathLen <= 0) { continue; } if (pathLen < nearestPathLen || (pathLen == nearestPathLen && nearestStationId != null && candidateStationId < nearestStationId)) { nearestStationId = candidateStationId; nearestPathLen = pathLen; } } cacheNearestRunBlockDirectReassignStationId(targetStationId, runBlockReassignLocStationList, nearestStationId); return nearestStationId; } private Integer loadCachedNearestRunBlockDirectReassignStationId(Integer targetStationId, List runBlockReassignLocStationList) { String cacheKey = buildNearestRunBlockDirectReassignCacheKey(targetStationId, runBlockReassignLocStationList); if (cacheKey == null || redisUtil == null) { return null; } Object cacheValue = redisUtil.get(cacheKey); if (cacheValue == null) { return null; } try { Integer stationId = Integer.valueOf(String.valueOf(cacheValue)); return runBlockReassignLocStationList.contains(stationId) ? stationId : null; } catch (Exception ignore) { return null; } } private void cacheNearestRunBlockDirectReassignStationId(Integer targetStationId, List runBlockReassignLocStationList, Integer nearestStationId) { String cacheKey = buildNearestRunBlockDirectReassignCacheKey(targetStationId, runBlockReassignLocStationList); if (cacheKey == null || nearestStationId == null || redisUtil == null) { return; } redisUtil.set(cacheKey, nearestStationId, RUN_BLOCK_DIRECT_REASSIGN_NEAREST_CACHE_SECONDS); } private String buildNearestRunBlockDirectReassignCacheKey(Integer targetStationId, List runBlockReassignLocStationList) { if (targetStationId == null || runBlockReassignLocStationList == null || runBlockReassignLocStationList.isEmpty()) { return null; } List normalizedStationIdList = new ArrayList<>(); for (Integer stationId : runBlockReassignLocStationList) { if (stationId != null && !normalizedStationIdList.contains(stationId)) { normalizedStationIdList.add(stationId); } } if (normalizedStationIdList.isEmpty()) { return null; } Collections.sort(normalizedStationIdList); return RedisKeyType.STATION_RUN_BLOCK_DIRECT_REASSIGN_NEAREST_CACHE_.key + targetStationId + "_" + JSON.toJSONString(normalizedStationIdList); } private int countStationNodes(List path) { if (path == null || path.isEmpty()) { return 0; } int count = 0; for (NavigateNode node : path) { if (extractStationId(node) != null) { count++; } } return count; } private Integer extractStationId(NavigateNode node) { if (node == null || Cools.isEmpty(node.getNodeValue())) { return null; } try { JSONObject valueObject = JSONObject.parseObject(node.getNodeValue()); return valueObject == null ? null : valueObject.getInteger("stationId"); } catch (Exception ignore) { return null; } } private boolean shouldSkipRunBlockStation(BasDevp basDevp, Integer stationId) { if (basDevp == null || stationId == null) { return false; } return containsStation(basDevp.getBarcodeStationList$(), stationId) || containsStation(basDevp.getInStationList$(), stationId); } private boolean containsStation(List stationList, Integer stationId) { if (stationList == null || stationList.isEmpty() || stationId == null) { return false; } for (StationObjModel stationObjModel : stationList) { if (stationObjModel != null && Objects.equals(stationObjModel.getStationId(), stationId)) { return true; } } return false; } private RerouteExecutionResult executeReroutePlanWithTaskLock(RerouteContext context, RerouteCommandPlan plan, StationProtocol stationProtocol, Integer taskNo, Integer stationId) { long startMs = System.currentTimeMillis(); boolean runBlockReroute = context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE; int currentTaskBufferCommandCount = countCurrentTaskBufferCommands(stationProtocol.getTaskBufferItems(), taskNo); if (currentTaskBufferCommandCount > 0 && !runBlockReroute) { return RerouteExecutionResult.skip("buffer-has-current-task"); } if (currentTaskBufferCommandCount > 0 && runBlockReroute) { News.info("输送站点运行堵塞重规划检测到旧分段命令残留,已先清理本地状态后继续重发。站点号={},工作号={},当前任务命令数={}", stationId, taskNo, currentTaskBufferCommandCount); } long suppressStartMs = System.currentTimeMillis(); boolean suppressDispatch = !runBlockReroute && context.checkSuppressDispatch() && stationMoveCoordinator != null && stationMoveCoordinator.shouldSuppressDispatch(taskNo, stationId, plan.command()); long suppressCostMs = System.currentTimeMillis() - suppressStartMs; if (suppressDispatch) { return RerouteExecutionResult.skip("dispatch-suppressed"); } long outOrderLockStartMs = System.currentTimeMillis(); boolean outOrderLockAcquired = !context.requireOutOrderDispatchLock() || stationDispatchRuntimeStateSupport.tryAcquireOutOrderDispatchLock(taskNo, stationId, OUT_ORDER_DISPATCH_LIMIT_SECONDS); long outOrderLockCostMs = System.currentTimeMillis() - outOrderLockStartMs; if (!outOrderLockAcquired) { return RerouteExecutionResult.skip("out-order-lock"); } long sceneLockStartMs = System.currentTimeMillis(); boolean sceneLockAcquired = isBlank(context.executionLockKey()) || stationDispatchRuntimeStateSupport.tryAcquireLock(context.executionLockKey(), context.executionLockSeconds()); long sceneLockCostMs = System.currentTimeMillis() - sceneLockStartMs; if (!sceneLockAcquired) { return RerouteExecutionResult.skip("scene-lock"); } long resetCostMs = 0L; if (context.resetSegmentCommandsBeforeDispatch()) { long resetStartMs = System.currentTimeMillis(); stationDispatchRuntimeStateSupport.signalSegmentReset(taskNo, STATION_MOVE_RESET_WAIT_MS); resetCostMs = System.currentTimeMillis() - resetStartMs; } int clearedCommandCount = 0; long cancelSessionCostMs = 0L; if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) { long cancelSessionStartMs = System.currentTimeMillis(); stationMoveCoordinator.markCancelPending(taskNo, "reroute_pending"); stationMoveCoordinator.cancelSession(taskNo); cancelSessionCostMs = System.currentTimeMillis() - cancelSessionStartMs; } long registerDispatchStartMs = System.currentTimeMillis(); preRegisterDispatchSession(context, plan); long registerDispatchCostMs = System.currentTimeMillis() - registerDispatchStartMs; long offerDispatchStartMs = System.currentTimeMillis(); boolean offered = offerDevpCommandWithDedup(context.dispatchDeviceNo(), plan.command(), plan.dispatchScene()); long offerDispatchCostMs = System.currentTimeMillis() - offerDispatchStartMs; if (!offered) { return RerouteExecutionResult.skip("dispatch-dedup"); } applyRerouteDispatchEffects(context, plan, clearedCommandCount); long totalCostMs = System.currentTimeMillis() - startMs; log.info("executeReroutePlanWithTaskLock profile, sceneType={}, taskNo={}, stationId={}, suppressCostMs={}ms, outOrderLockCostMs={}ms, sceneLockCostMs={}ms, resetCostMs={}ms, cancelSessionCostMs={}ms, registerDispatchCostMs={}ms, offerDispatchCostMs={}ms, totalCostMs={}ms", context.sceneType(), taskNo, stationId, suppressCostMs, outOrderLockCostMs, sceneLockCostMs, resetCostMs, cancelSessionCostMs, registerDispatchCostMs, offerDispatchCostMs, totalCostMs); if (totalCostMs > EXECUTE_REROUTE_PLAN_SLOW_THRESHOLD_MS) { log.warn("executeReroutePlanWithTaskLock slow, sceneType={}, taskNo={}, stationId={}, totalCostMs={}ms, resetCostMs={}ms, registerDispatchCostMs={}ms, offerDispatchCostMs={}ms", context.sceneType(), taskNo, stationId, totalCostMs, resetCostMs, registerDispatchCostMs, offerDispatchCostMs); } return RerouteExecutionResult.dispatched(plan.command(), clearedCommandCount); } private RerouteExecutionResult executeSharedReroute(RerouteContext context) { RerouteDecision decision = resolveSharedRerouteDecision(context); if (decision.skip()) { return RerouteExecutionResult.skip(decision.skipReason()); } RerouteCommandPlan plan = buildRerouteCommandPlan(context, decision); return executeReroutePlan(context, plan); } private void preRegisterDispatchSession(RerouteContext context, RerouteCommandPlan plan) { if (context == null || plan == null || plan.command() == null || context.wrkMast() == null || context.stationProtocol() == null) { return; } if (stationMoveCoordinator == null) { return; } OutOrderDispatchDecision dispatchDecision = plan.decision() == null ? null : plan.decision().dispatchDecision(); stationMoveCoordinator.recordDispatch( context.wrkMast().getWrkNo(), context.stationProtocol().getStationId(), plan.dispatchScene(), plan.command(), dispatchDecision != null && dispatchDecision.isCircle() ); } private void applyRerouteDispatchEffects(RerouteContext context, RerouteCommandPlan plan, int clearedCommandCount) { if (context == null || plan == null || plan.command() == null || context.wrkMast() == null || context.stationProtocol() == null) { return; } WrkMast wrkMast = context.wrkMast(); StationProtocol stationProtocol = context.stationProtocol(); OutOrderDispatchDecision dispatchDecision = plan.decision() == null ? null : plan.decision().dispatchDecision(); stationOutboundDecisionSupport.syncOutOrderWatchState( wrkMast, stationProtocol.getStationId(), context.outOrderStationIds(), dispatchDecision, plan.command() ); if (context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE) { News.info("输送站点堵塞后重新计算路径命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(plan.command())); return; } if (context.sceneType() == RerouteSceneType.OUT_ORDER) { News.info(dispatchDecision != null && dispatchDecision.isCircle() ? "{}任务进行绕圈" : "{}任务直接去目标点", wrkMast.getWrkNo()); } } private void executeRunBlockDirectReassign(BasDevp basDevp, StationThread stationThread, StationProtocol stationProtocol, WrkMast wrkMast) { if (basDevp == null || stationThread == null || stationProtocol == null || wrkMast == null) { return; } int currentTaskBufferCommandCount = countCurrentTaskBufferCommands( stationProtocol.getTaskBufferItems(), stationProtocol.getTaskNo() ); if (currentTaskBufferCommandCount > 0) { News.info("输送站点运行堵塞重分配检测到旧分段命令残留,将先重置本地分段状态后继续重发。站点号={},工作号={},当前任务命令数={}", stationProtocol.getStationId(), stationProtocol.getTaskNo(), currentTaskBufferCommandCount); } if (stationDispatchRuntimeStateSupport.hasRunBlockDirectReassignLimit( wrkMast.getWrkNo(), stationProtocol.getStationId())) { News.info("输送站点运行堵塞重分配已跳过,8分钟内不允许重复申请。站点号={},工作号={}", stationProtocol.getStationId(), wrkMast.getWrkNo()); return; } String response = wmsOperateUtils.applyReassignTaskLocNo(wrkMast.getWrkNo(), stationProtocol.getStationId()); if (Cools.isEmpty(response)) { appendStationSystemWarning(stationProtocol, RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING); News.taskError(wrkMast.getWrkNo(), "请求WMS重新分配库位接口失败,接口未响应!!!response:{}", response); return; } JSONObject jsonObject; try { jsonObject = JSON.parseObject(response); } catch (Exception e) { appendStationSystemWarning(stationProtocol, RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING); News.taskError(wrkMast.getWrkNo(), "请求WMS重新分配库位接口响应解析异常!!!response:{}", response, e); return; } if (jsonObject == null || !Integer.valueOf(200).equals(jsonObject.getInteger("code"))) { appendStationSystemWarning(stationProtocol, RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING); News.taskError(wrkMast.getWrkNo(), "请求WMS接口失败!!!response:{}", response); return; } StartupDto dto = jsonObject.getObject("data", StartupDto.class); if (dto == null || Cools.isEmpty(dto.getLocNo())) { appendStationSystemWarning(stationProtocol, RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING); News.taskError(wrkMast.getWrkNo(), "请求WMS重新分配库位接口失败,WMS未返回目标库位!!!response:{}", response); return; } clearStationSystemWarningByPrefix(stationProtocol, RUN_BLOCK_DIRECT_REASSIGN_WMS_WARNING_PREFIX); String sourceLocNo = wrkMast.getLocNo(); String locNo = dto.getLocNo(); LocMast sourceLocMast = locMastService.queryByLoc(sourceLocNo); if (sourceLocMast == null) { News.taskInfo(wrkMast.getWrkNo(), "库位号:{} 源库位信息不存在", sourceLocNo); return; } if (!sourceLocMast.getLocSts().equals("S")) { News.taskInfo(wrkMast.getWrkNo(), "库位号:{} 源库位状态不处于入库预约", sourceLocNo); return; } LocMast locMast = locMastService.queryByLoc(locNo); if (locMast == null) { News.taskInfo(wrkMast.getWrkNo(), "库位号:{} 目标库位信息不存在", locNo); return; } if (!locMast.getLocSts().equals("O")) { News.taskInfo(wrkMast.getWrkNo(), "库位号:{} 目标库位状态不处于空库位", locNo); return; } FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo); if (findCrnNoResult == null) { News.taskInfo(wrkMast.getWrkNo(), "{}工作,未匹配到堆垛机", wrkMast.getWrkNo()); return; } Integer crnNo = findCrnNoResult.getCrnNo(); Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationProtocol.getStationId()); if (targetStationId == null) { News.taskInfo(wrkMast.getWrkNo(), "{}站点,搜索入库站点失败", stationProtocol.getStationId()); return; } StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), targetStationId, 0); if (command == null) { News.taskInfo(wrkMast.getWrkNo(), "{}工作,获取输送线命令失败", wrkMast.getWrkNo()); return; } sourceLocMast.setLocSts("O"); sourceLocMast.setModiTime(new Date()); locMastService.updateById(sourceLocMast); locMast.setLocSts("S"); locMast.setModiTime(new Date()); locMastService.updateById(locMast); wrkMast.setLocNo(locNo); wrkMast.setStaNo(targetStationId); if (findCrnNoResult.getCrnType().equals(SlaveType.Crn)) { wrkMast.setCrnNo(crnNo); } else if (findCrnNoResult.getCrnType().equals(SlaveType.DualCrn)) { wrkMast.setDualCrnNo(crnNo); } else { throw new CoolException("未知设备类型"); } if (!wrkMastService.updateById(wrkMast)) { return; } stationDispatchRuntimeStateSupport.recordRunBlockDirectReassignLimit( wrkMast.getWrkNo(), stationProtocol.getStationId(), RUN_BLOCK_DIRECT_REASSIGN_LIMIT_SECONDS); stationDispatchRuntimeStateSupport.signalSegmentReset(wrkMast.getWrkNo(), STATION_MOVE_RESET_WAIT_MS); if (stationMoveCoordinator != null) { stationMoveCoordinator.markCancelPending(wrkMast.getWrkNo(), "reroute_pending"); stationMoveCoordinator.cancelSession(wrkMast.getWrkNo()); stationMoveCoordinator.recordDispatch( wrkMast.getWrkNo(), stationProtocol.getStationId(), "checkStationRunBlock_direct", command, false ); } boolean offered = offerDevpCommandWithDedup(basDevp.getDevpNo(), command, "checkStationRunBlock_direct"); if (!offered) { News.warn("输送站点堵塞直派命令入队被拒绝(可能重复),站点号={},工作号={}", stationProtocol.getStationId(), wrkMast.getWrkNo()); } } private void appendStationSystemWarning(StationProtocol stationProtocol, String warning) { if (stationProtocol == null || Cools.isEmpty(warning)) { return; } String currentWarning = stationProtocol.getSystemWarning(); if (Cools.isEmpty(currentWarning)) { stationProtocol.setSystemWarning(warning); return; } if (currentWarning.contains(warning)) { return; } stationProtocol.setSystemWarning(currentWarning + ";" + warning); } private void clearStationSystemWarningByPrefix(StationProtocol stationProtocol, String warningPrefix) { if (stationProtocol == null || Cools.isEmpty(warningPrefix) || Cools.isEmpty(stationProtocol.getSystemWarning())) { return; } String[] warningParts = stationProtocol.getSystemWarning().split(";"); List keepWarningList = new ArrayList<>(); for (String warningPart : warningParts) { if (Cools.isEmpty(warningPart) || warningPart.startsWith(warningPrefix)) { continue; } keepWarningList.add(warningPart); } stationProtocol.setSystemWarning(String.join(";", keepWarningList)); } private int countCurrentTaskBufferCommands(List taskBufferItems, Integer currentTaskNo) { if (taskBufferItems == null || taskBufferItems.isEmpty() || currentTaskNo == null || currentTaskNo <= 0) { return 0; } int count = 0; for (StationTaskBufferItem item : taskBufferItems) { if (item == null || item.getTaskNo() == null) { continue; } if (currentTaskNo.equals(item.getTaskNo())) { count++; } } return count; } private boolean offerDevpCommandWithDedup(Integer deviceNo, StationCommand command, String scene) { StationCommandDispatchResult dispatchResult = stationCommandDispatcher .dispatch(deviceNo, command, "station-operate-process", scene); return dispatchResult.isAccepted(); } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } }