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<Integer, StationProtocol> 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<Integer> runBlockReassignLocStationList = new ArrayList<>();
|
for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) {
|
runBlockReassignLocStationList.add(stationObjModel.getStationId());
|
}
|
List<Integer> 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<Integer, StationProtocol> 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<Integer> 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<Integer> 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<Integer> 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<NavigateNode> 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<Integer> 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<Integer> 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<Integer> runBlockReassignLocStationList) {
|
if (targetStationId == null || runBlockReassignLocStationList == null || runBlockReassignLocStationList.isEmpty()) {
|
return null;
|
}
|
List<Integer> 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<NavigateNode> 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<StationObjModel> 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<String> 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<StationTaskBufferItem> 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();
|
}
|
}
|