From aed8dfd97e8979b138cd858f35c15bec898542b3 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期四, 26 三月 2026 13:17:36 +0800
Subject: [PATCH] #
---
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java | 2368 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
1 files changed, 2,114 insertions(+), 254 deletions(-)
diff --git a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
index 1224d5d..9ab571c 100644
--- a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
@@ -7,6 +7,7 @@
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.*;
@@ -18,6 +19,9 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.NavigateUtils;
import com.zy.common.utils.RedisUtil;
+import com.zy.core.move.StationMoveCoordinator;
+import com.zy.core.move.StationMoveDispatchMode;
+import com.zy.core.move.StationMoveSession;
import com.zy.core.News;
import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
@@ -26,6 +30,8 @@
import com.zy.core.model.Task;
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.service.StationTaskLoopService;
import com.zy.core.thread.StationThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -35,7 +41,13 @@
@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 OUT_ORDER_DISPATCH_LIMIT_SECONDS = 2;
+ private static final int STATION_COMMAND_DISPATCH_DEDUP_SECONDS = 10;
+ private static final int STATION_IDLE_RECOVER_SECONDS = 10;
+ private static final int STATION_IDLE_RECOVER_LIMIT_SECONDS = 30;
+ private static final int STATION_IDLE_TRACK_EXPIRE_SECONDS = 60 * 60;
+ private static final long STATION_MOVE_RESET_WAIT_MS = 1000L;
+ private static final String IDLE_RECOVER_CLEARED_MEMO = "idleRecoverRerouteCleared";
@Autowired
private BasDevpService basDevpService;
@@ -57,13 +69,23 @@
private BasStationService basStationService;
@Autowired
private StationCycleCapacityService stationCycleCapacityService;
+ @Autowired
+ private StationPathPolicyService stationPathPolicyService;
+ @Autowired
+ private BasStationOptService basStationOptService;
+ @Autowired
+ private StationTaskLoopService stationTaskLoopService;
+ @Autowired
+ private WrkAnalysisService wrkAnalysisService;
+ @Autowired
+ private StationMoveCoordinator stationMoveCoordinator;
//鎵ц杈撻�佺珯鐐瑰叆搴撲换鍔�
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) {
@@ -102,7 +124,7 @@
continue;
}
- if (wrkMast.getWrkSts() == WrkStsType.INBOUND_DEVICE_RUN.sts) {
+ if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_INBOUND.sts)) {
continue;
}
@@ -119,6 +141,7 @@
continue;
}
+ DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), targetStationId);
LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), targetStationId, loadGuardState);
if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
@@ -131,13 +154,26 @@
continue;
}
- wrkMast.setWrkSts(WrkStsType.INBOUND_DEVICE_RUN.sts);
+ Date now = new Date();
+ wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
wrkMast.setSourceStaNo(stationProtocol.getStationId());
wrkMast.setStaNo(targetStationId);
wrkMast.setSystemMsg("");
- wrkMast.setIoTime(new Date());
+ wrkMast.setIoTime(now);
+ wrkMast.setModiTime(now);
if (wrkMastService.updateById(wrkMast)) {
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ wrkAnalysisService.markInboundStationStart(wrkMast, now);
+ boolean offered = offerDevpCommandWithDedup(basDevp.getDevpNo(), command, "stationInExecute");
+ if (offered && stationMoveCoordinator != null) {
+ // 鍒濆鍏ュ簱鍛戒护涔熺撼鍏� session 璺熻釜锛屽悗缁仠鐣欐仮澶�/缁曞湀/鍫靛閲嶇畻鎵嶈兘鍩轰簬鍚屼竴鏉¤矾绾跨姸鎬佸垽鏂��
+ stationMoveCoordinator.recordDispatch(
+ wrkMast.getWrkNo(),
+ stationProtocol.getStationId(),
+ "stationInExecute",
+ command,
+ false
+ );
+ }
News.info("杈撻�佺珯鐐瑰叆搴撳懡浠や笅鍙戞垚鍔燂紝绔欑偣鍙�={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}", stationId, wrkMast.getWrkNo(), JSON.toJSONString(command));
redisUtil.set(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId, "lock", 5);
loadGuardState.reserveLoopTask(loopHitResult.getLoopNo());
@@ -154,9 +190,9 @@
//鎵ц鍫嗗灈鏈鸿緭閫佺珯鐐瑰嚭搴撲换鍔�
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)
@@ -193,30 +229,56 @@
&& stationProtocol.isLoading()
&& stationProtocol.getTaskNo() == 0
) {
- Integer moveStaNo = resolveDispatchOutOrderTarget(
+ // 鍏堢畻褰撳墠浠诲姟鍦ㄦ壒娆″嚭搴撲腑鐨勮矾寰勫�惧悜绯绘暟锛屽啀甯︾潃杩欎釜绯绘暟鍘诲喅绛栫洰鏍囩珯锛�
+ // 杩欐牱鍚屼竴鎵规涓嶅悓搴忓彿浠诲姟鍦ㄦ帓搴忕偣銆佺粫鍦堢偣鍜屽牭濉為噸绠楁椂浼氬緱鍒颁竴鑷寸殑鐩爣瑁佸喅銆�
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision(
stationProtocol.getStationId(),
- wrkMast.getStaNo(),
+ wrkMast,
outOrderList,
- true
+ pathLenFactor
);
+ Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId();
+ if (moveStaNo == null) {
+ continue;
+ }
- LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState);
+ DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo);
+ LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState, wrkMast, pathLenFactor);
if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
return;
}
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0);
+ StationCommand command = buildOutboundMoveCommand(
+ stationThread,
+ wrkMast,
+ stationProtocol.getStationId(),
+ moveStaNo,
+ pathLenFactor
+ );
if (command == null) {
News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
continue;
}
+ Date now = new Date();
wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts);
wrkMast.setSystemMsg("");
- wrkMast.setIoTime(new Date());
+ wrkMast.setIoTime(now);
+ wrkMast.setModiTime(now);
if (wrkMastService.updateById(wrkMast)) {
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ wrkAnalysisService.markOutboundStationStart(wrkMast, now);
+ boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "crnStationOutExecute");
+ if (offered && stationMoveCoordinator != null) {
+ stationMoveCoordinator.recordDispatch(
+ wrkMast.getWrkNo(),
+ stationProtocol.getStationId(),
+ "crnStationOutExecute",
+ command,
+ false
+ );
+ }
News.info("杈撻�佺珯鐐瑰嚭搴撳懡浠や笅鍙戞垚鍔燂紝绔欑偣鍙�={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5);
redisUtil.del(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo());
@@ -267,7 +329,14 @@
&& stationProtocol.isLoading()
&& stationProtocol.getTaskNo() == 0
) {
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), wrkMast.getStaNo(), 0);
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ StationCommand command = buildOutboundMoveCommand(
+ stationThread,
+ wrkMast,
+ stationProtocol.getStationId(),
+ wrkMast.getStaNo(),
+ pathLenFactor
+ );
if (command == null) {
News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
continue;
@@ -277,7 +346,17 @@
wrkMast.setSystemMsg("");
wrkMast.setIoTime(new Date());
if (wrkMastService.updateById(wrkMast)) {
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "dualCrnStationOutExecute");
+ if (offered && stationMoveCoordinator != null) {
+ // 鍙屽伐浣嶅爢鍨涙満杞叆杈撻�佺嚎鍚庡悓鏍疯鐧昏 session锛屽惁鍒欏悗缁噸绠楀彧鑳界湅鍒� PLC 鍛戒护锛岀湅涓嶅埌璺嚎璇箟銆�
+ stationMoveCoordinator.recordDispatch(
+ wrkMast.getWrkNo(),
+ stationProtocol.getStationId(),
+ "dualCrnStationOutExecute",
+ command,
+ false
+ );
+ }
notifyUtils.notify(String.valueOf(SlaveType.Devp), stationObjModel.getDeviceNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN, null);
News.info("杈撻�佺珯鐐瑰嚭搴撳懡浠や笅鍙戞垚鍔燂紝绔欑偣鍙�={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5);
@@ -297,39 +376,51 @@
for (WrkMast wrkMast : wrkMasts) {
Integer wrkNo = wrkMast.getWrkNo();
Integer targetStaNo = wrkMast.getStaNo();
+ if (wrkNo == null || targetStaNo == null) {
+ continue;
+ }
boolean complete = false;
+ Integer targetDeviceNo = null;
BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", targetStaNo));
- if (basStation == null) {
- continue;
- }
-
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
- if (stationThread == null) {
- continue;
- }
-
- Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
- StationProtocol stationProtocol = statusMap.get(basStation.getStationId());
- if (stationProtocol == null) {
- continue;
- }
-
- if (stationProtocol.getTaskNo().equals(wrkNo)) {
- complete = true;
+ if (basStation != null) {
+ targetDeviceNo = basStation.getDeviceNo();
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
+ if (stationThread != null) {
+ Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
+ StationProtocol stationProtocol = statusMap.get(basStation.getStationId());
+ if (stationProtocol != null && wrkNo.equals(stationProtocol.getTaskNo())) {
+ complete = true;
+ }
+ }
}
if (complete) {
- wrkMast.setWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts);
- wrkMast.setIoTime(new Date());
- wrkMastService.updateById(wrkMast);
- notifyUtils.notify(String.valueOf(SlaveType.Devp), basStation.getDeviceNo(), String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN_COMPLETE, null);
- redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkMast.getWrkNo(), "lock", 60);
+ completeStationRunTask(wrkMast, targetDeviceNo);
}
}
} catch (Exception e) {
e.printStackTrace();
}
+ }
+
+ private void completeStationRunTask(WrkMast wrkMast, Integer deviceNo) {
+ if (wrkMast == null || wrkMast.getWrkNo() == null) {
+ return;
+ }
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.finishSession(wrkMast.getWrkNo());
+ }
+ Date now = new Date();
+ wrkMast.setWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts);
+ wrkMast.setIoTime(now);
+ wrkMast.setModiTime(now);
+ wrkMastService.updateById(wrkMast);
+ wrkAnalysisService.markOutboundStationComplete(wrkMast, now);
+ if (deviceNo != null) {
+ notifyUtils.notify(String.valueOf(SlaveType.Devp), deviceNo, String.valueOf(wrkMast.getWrkNo()), wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN_COMPLETE, null);
+ }
+ redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkMast.getWrkNo(), "lock", 60);
}
// 妫�娴嬩换鍔¤浆瀹屾垚
@@ -367,6 +458,9 @@
}
if (complete) {
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.finishSession(wrkNo);
+ }
wrkMast.setWrkSts(WrkStsType.COMPLETE_OUTBOUND.sts);
wrkMast.setIoTime(new Date());
wrkMastService.updateById(wrkMast);
@@ -391,6 +485,7 @@
for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) {
runBlockReassignLocStationList.add(stationObjModel.getStationId());
}
+ List<Integer> outOrderStationIds = basDevp.getOutOrderIntList();
List<StationProtocol> list = stationThread.getStatus();
for (StationProtocol stationProtocol : list) {
@@ -411,101 +506,54 @@
}
redisUtil.set(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo(), "lock", 15);
- if (wrkMast.getIoType() == WrkIoType.IN.id && runBlockReassignLocStationList.contains(stationProtocol.getStationId())) {
- //绔欑偣澶勪簬閲嶆柊鍒嗛厤搴撲綅鍖哄煙
- //杩愯鍫靛锛岄噸鏂扮敵璇蜂换鍔�
- String response = wmsOperateUtils.applyReassignTaskLocNo(wrkMast.getWrkNo(), stationProtocol.getStationId());
- if (Cools.isEmpty(response)) {
- News.taskError(wrkMast.getWrkNo(), "璇锋眰WMS閲嶆柊鍒嗛厤搴撲綅鎺ュ彛澶辫触锛屾帴鍙f湭鍝嶅簲锛侊紒锛乺esponse锛歿}", response);
- continue;
- }
- JSONObject jsonObject = JSON.parseObject(response);
- if (jsonObject.getInteger("code").equals(200)) {
- StartupDto dto = jsonObject.getObject("data", StartupDto.class);
-
- String sourceLocNo = wrkMast.getLocNo();
- String locNo = dto.getLocNo();
-
- LocMast sourceLocMast = locMastService.queryByLoc(sourceLocNo);
- if (sourceLocMast == null) {
- News.taskInfo(wrkMast.getWrkNo(), "搴撲綅鍙�:{} 婧愬簱浣嶄俊鎭笉瀛樺湪", sourceLocNo);
- continue;
- }
-
- if (!sourceLocMast.getLocSts().equals("S")) {
- News.taskInfo(wrkMast.getWrkNo(), "搴撲綅鍙�:{} 婧愬簱浣嶇姸鎬佷笉澶勪簬鍏ュ簱棰勭害", sourceLocNo);
- continue;
- }
-
- LocMast locMast = locMastService.queryByLoc(locNo);
- if (locMast == null) {
- News.taskInfo(wrkMast.getWrkNo(), "搴撲綅鍙�:{} 鐩爣搴撲綅淇℃伅涓嶅瓨鍦�", locNo);
- continue;
- }
-
- if (!locMast.getLocSts().equals("O")) {
- News.taskInfo(wrkMast.getWrkNo(), "搴撲綅鍙�:{} 鐩爣搴撲綅鐘舵�佷笉澶勪簬绌哄簱浣�", locNo);
- continue;
- }
-
- FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
- if (findCrnNoResult == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鏈尮閰嶅埌鍫嗗灈鏈�", wrkMast.getWrkNo());
- continue;
- }
- Integer crnNo = findCrnNoResult.getCrnNo();
-
- Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationProtocol.getStationId());
- if (targetStationId == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}绔欑偣,鎼滅储鍏ュ簱绔欑偣澶辫触", stationProtocol.getStationId());
- continue;
- }
-
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), targetStationId, 0);
- if (command == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鑾峰彇杈撻�佺嚎鍛戒护澶辫触", wrkMast.getWrkNo());
- continue;
- }
-
- //鏇存柊婧愬簱浣�
- sourceLocMast.setLocSts("O");
- sourceLocMast.setModiTime(new Date());
- locMastService.updateById(sourceLocMast);
-
- //鏇存柊鐩爣搴撲綅
- locMast.setLocSts("S");
- locMast.setModiTime(new Date());
- locMastService.updateById(locMast);
-
- //鏇存柊宸ヤ綔妗f暟鎹�
- 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)) {
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
- }
- } else {
- News.error("璇锋眰WMS鎺ュ彛澶辫触锛侊紒锛乺esponse锛歿}", response);
- }
- } else {
- //杩愯鍫靛锛岄噸鏂拌绠楄矾绾�
- 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, basDevp.getDevpNo(), new Task(2, command));
- News.info("杈撻�佺珯鐐瑰牭濉炲悗閲嶆柊璁$畻璺緞鍛戒护涓嬪彂鎴愬姛锛岀珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽懡浠ゆ暟鎹�={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
+ if (shouldUseRunBlockDirectReassign(wrkMast, stationProtocol.getStationId(), runBlockReassignLocStationList)) {
+ executeRunBlockDirectReassign(basDevp, stationThread, stationProtocol, wrkMast);
+ continue;
}
+
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ // 杩愯鍫靛涓嶅崟鐙喅瀹氫笟鍔$洰鏍囩珯锛屼粛鐒跺鐢ㄥ嚭搴撴帓搴�/缁曞湀鐨勭洰鏍囪鍐筹紝
+ // 杩欓噷鍙槸瑕佹眰鐢� run-block 涓撶敤绠楄矾锛屽苟鍦ㄩ噸鍙戝墠娓呮帀鏃� session/segment 鐘舵�併��
+ 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 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());
}
}
}
@@ -517,6 +565,19 @@
//鑾峰彇杈撻�佺嚎浠诲姟鏁伴噺
public synchronized int getCurrentStationTaskCount() {
return countCurrentStationTask();
+ }
+
+ public synchronized int getCurrentOutboundTaskCountByTargetStation(Integer stationId) {
+ if (stationId == null) {
+ return 0;
+ }
+ return (int) wrkMastService.count(new QueryWrapper<WrkMast>()
+ .eq("io_type", WrkIoType.OUT.id)
+ .eq("sta_no", stationId)
+ .in("wrk_sts",
+ WrkStsType.OUTBOUND_RUN.sts,
+ WrkStsType.OUTBOUND_RUN_COMPLETE.sts,
+ WrkStsType.STATION_RUN.sts));
}
// 妫�娴嬪嚭搴撴帓搴�
@@ -548,6 +609,11 @@
continue;
}
+ // 鎺掑簭鐐规湰韬凡缁忓牭濉炴椂锛屼笉鍦� out-order 閲屽仛浜屾鍐崇瓥锛岀粺涓�浜ょ粰 run-block 閲嶈鍒掑鐞嗐��
+ if (stationProtocol.isRunBlock()) {
+ continue;
+ }
+
if (!stationProtocol.getStationId().equals(stationProtocol.getTargetStaNo())) {
continue;
}
@@ -556,81 +622,32 @@
if (wrkMast == null) {
continue;
}
-
- if (isWatchingCircleArrival(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
+ if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)) {
+ continue;
+ }
+ if (Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) {
+ continue;
+ }
+ // 鍙湁娲诲姩涓殑鐜版湁璺嚎鎵嶄細鍘嬪埗 out-order锛汢LOCKED 璺嚎瑕佸厑璁告帓搴忕偣閲嶆柊鍚姩銆�
+ if (shouldSkipOutOrderDispatchForExistingRoute(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
continue;
}
- if (Cools.isEmpty(wrkMast.getBatch())) {
- continue;
- }
-
- if (Cools.isEmpty(wrkMast.getBatchSeq())) {
- 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_seq")
- );
- if (batchWrkList.isEmpty()) {
- 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";
- }
- }
-
- 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;
- }
- if (!tryAcquireOutOrderDispatchLock(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
- continue;
- }
- clearWatchCircleCommand(wrkMast.getWrkNo());
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
- News.info("{}浠诲姟鐩存帴鍘荤洰鏍囩偣", wrkMast.getWrkNo());
- } else if (commandType.equals("toCircle")) {
- Integer circleTarget = resolveNextCircleOrderTarget(stationProtocol.getStationId(), outOrderStationIds);
-
- if (circleTarget == null) {
- News.taskInfo(wrkMast.getWrkNo(), "鏈壘鍒板彲鎵ц鐨勪笅涓�鎺掑簭妫�娴嬬偣锛屽綋鍓嶇珯鐐�={}", stationProtocol.getStationId());
- continue;
- }
-
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), circleTarget, 0);
- if (command == null) {
- News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
- continue;
- }
- if (!tryAcquireOutOrderDispatchLock(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
- continue;
- }
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
- saveWatchCircleCommand(wrkMast.getWrkNo(), command);
- News.info("{}浠诲姟杩涜缁曞湀", wrkMast.getWrkNo());
- }
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.OUT_ORDER,
+ basDevp,
+ stationThread,
+ stationProtocol,
+ wrkMast,
+ outOrderStationIds,
+ pathLenFactor,
+ "checkStationOutOrder"
+ ).withDispatchDeviceNo(stationObjModel.getDeviceNo())
+ .withSuppressDispatchGuard()
+ .withOutOrderDispatchLock()
+ .withResetSegmentCommandsBeforeDispatch();
+ executeSharedReroute(context);
}
}
}
@@ -659,11 +676,8 @@
continue;
}
- StationCommand circleCommand = getWatchCircleCommand(stationProtocol.getTaskNo());
- if (circleCommand == null) {
- continue;
- }
- if (!stationProtocol.getStationId().equals(circleCommand.getTargetStaNo())) {
+ // 缁曞湀瑙﹀彂浼樺厛璇� session 鐨勪笅涓�鍐崇瓥绔欙紝legacy WATCH_CIRCLE key 鍙仛鍏煎鍥為��銆�
+ if (!isWatchingCircleArrival(stationProtocol.getTaskNo(), stationProtocol.getStationId())) {
continue;
}
@@ -671,30 +685,390 @@
if (wrkMast == null) {
continue;
}
-
- Integer moveStaNo = resolveDispatchOutOrderTarget(
- stationProtocol.getStationId(),
- wrkMast.getStaNo(),
+ if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)) {
+ continue;
+ }
+ if (Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) {
+ continue;
+ }
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.WATCH_CIRCLE,
+ basDevp,
+ stationThread,
+ stationProtocol,
+ wrkMast,
outOrderList,
- true
- );
-
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0);
- if (command == null) {
- News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
- continue;
- }
- if (!tryAcquireOutOrderDispatchLock(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
- continue;
- }
- if (Objects.equals(moveStaNo, wrkMast.getStaNo())) {
- clearWatchCircleCommand(wrkMast.getWrkNo());
- } else {
- saveWatchCircleCommand(wrkMast.getWrkNo(), command);
- }
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ pathLenFactor,
+ "watchCircleStation"
+ ).withSuppressDispatchGuard()
+ .withOutOrderDispatchLock()
+ .withResetSegmentCommandsBeforeDispatch();
+ executeSharedReroute(context);
}
}
+ }
+
+ private StationCommand buildOutboundMoveCommand(StationThread stationThread,
+ WrkMast wrkMast,
+ Integer stationId,
+ Integer targetStationId,
+ Double pathLenFactor) {
+ if (stationThread == null || wrkMast == null) {
+ return null;
+ }
+ return stationThread.getCommand(
+ StationCommandType.MOVE,
+ wrkMast.getWrkNo(),
+ stationId,
+ targetStationId,
+ 0,
+ normalizePathLenFactor(pathLenFactor)
+ );
+ }
+
+ 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()
+ )
+ : 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());
+ }
+
+ 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");
+ }
+ boolean runBlockReroute = context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE;
+ if (runBlockReroute) {
+ // 绔欑偣杩涘叆鍫靛鍚庯紝璁惧渚у彲鑳藉凡缁忔妸涔嬪墠棰勪笅鍙戠殑鍒嗘鍛戒护娓呮帀浜嗐��
+ // 鍏堜綔搴熸湰鍦� session/segment 鐘舵�侊紝鍐嶆寜鏂拌矾绾块噸鍙戯紝閬垮厤琚棫鐘舵�佸弽鍚戝崱浣忋��
+ if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
+ stationMoveCoordinator.cancelSession(taskNo);
+ }
+ if (context.resetSegmentCommandsBeforeDispatch()) {
+ resetSegmentMoveCommandsBeforeReroute(taskNo);
+ }
+ }
+ if (context.checkRecentDispatch()
+ && shouldSkipIdleRecoverForRecentDispatch(taskNo, stationId)) {
+ return RerouteExecutionResult.skip("recent-dispatch");
+ }
+ int currentTaskBufferCommandCount = countCurrentTaskBufferCommands(stationProtocol.getTaskBufferItems(), taskNo);
+ if (currentTaskBufferCommandCount > 0 && !runBlockReroute) {
+ if (context.sceneType() == RerouteSceneType.IDLE_RECOVER) {
+ News.info("杈撻�佺珯鐐逛换鍔″仠鐣欒秴鏃讹紝浣嗙紦瀛樺尯浠嶅瓨鍦ㄥ綋鍓嶄换鍔″懡浠わ紝宸茶烦杩囬噸绠椼�傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽綋鍓嶄换鍔″懡浠ゆ暟={}",
+ stationId,
+ taskNo,
+ currentTaskBufferCommandCount);
+ }
+ return RerouteExecutionResult.skip("buffer-has-current-task");
+ }
+ if (currentTaskBufferCommandCount > 0 && runBlockReroute) {
+ // 鍫靛閲嶈鍒掕鏇挎崲鐨勬鏄繖浜涙棫鍒嗘鍛戒护锛屼笉鑳藉啀鎶婃畫鐣� buffer 褰撴垚鏂扮殑鎷︽埅鏉′欢銆�
+ News.info("杈撻�佺珯鐐硅繍琛屽牭濉為噸瑙勫垝妫�娴嬪埌鏃у垎娈靛懡浠ゆ畫鐣欙紝宸插厛娓呯悊鏈湴鐘舵�佸悗缁х画閲嶅彂銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽綋鍓嶄换鍔″懡浠ゆ暟={}",
+ stationId,
+ taskNo,
+ currentTaskBufferCommandCount);
+ }
+ if (!runBlockReroute
+ && context.checkSuppressDispatch()
+ && stationMoveCoordinator != null
+ && stationMoveCoordinator.shouldSuppressDispatch(taskNo, stationId, plan.command())) {
+ return RerouteExecutionResult.skip("dispatch-suppressed");
+ }
+ // 杩涘叆鍫靛閲嶈鍒掑悗锛屾棫璺嚎宸茬粡琚樉寮忓彇娑堬紝鏈疆鍛戒护涓嶅啀鍙備笌 active-session suppress 鍒ゅ畾銆�
+ if (context.requireOutOrderDispatchLock()
+ && !tryAcquireOutOrderDispatchLock(taskNo, stationId)) {
+ return RerouteExecutionResult.skip("out-order-lock");
+ }
+
+ if (!runBlockReroute
+ && context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
+ stationMoveCoordinator.cancelSession(taskNo);
+ }
+ if (!isBlank(context.executionLockKey())) {
+ Object lock = redisUtil.get(context.executionLockKey());
+ if (lock != null) {
+ return RerouteExecutionResult.skip("scene-lock");
+ }
+ redisUtil.set(context.executionLockKey(), "lock", context.executionLockSeconds());
+ }
+ if (!runBlockReroute && context.resetSegmentCommandsBeforeDispatch()) {
+ resetSegmentMoveCommandsBeforeReroute(taskNo);
+ }
+
+ int clearedCommandCount = 0;
+ if (context.clearIdleIssuedCommands()) {
+ clearedCommandCount = clearIssuedMoveCommandsDuringIdleStay(context.idleTrack(), taskNo, stationId);
+ }
+
+ boolean offered = offerDevpCommandWithDedup(context.dispatchDeviceNo(), plan.command(), plan.dispatchScene());
+ if (!offered) {
+ return RerouteExecutionResult.skip("dispatch-dedup");
+ }
+
+ applyRerouteDispatchEffects(context, plan, clearedCommandCount);
+ return RerouteExecutionResult.dispatched(plan.command(), clearedCommandCount);
+ }
+
+ RerouteDecision resolveSharedRerouteDecision(RerouteContext context) {
+ 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();
+ return targetStationId == null || Objects.equals(targetStationId, currentStationId)
+ ? RerouteDecision.skip("same-station")
+ : RerouteDecision.proceed(targetStationId);
+ }
+
+ OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision(
+ currentStationId,
+ context.wrkMast(),
+ context.outOrderStationIds(),
+ context.pathLenFactor()
+ );
+ Integer targetStationId = dispatchDecision == null ? null : dispatchDecision.getTargetStationId();
+ if (targetStationId == null || Objects.equals(targetStationId, currentStationId)) {
+ return RerouteDecision.skip("same-station");
+ }
+ return RerouteDecision.proceed(targetStationId, dispatchDecision);
+ }
+
+ 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 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();
+
+ syncOutOrderWatchState(wrkMast, stationProtocol.getStationId(), context.outOrderStationIds(), dispatchDecision, plan.command());
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.recordDispatch(
+ wrkMast.getWrkNo(),
+ stationProtocol.getStationId(),
+ plan.dispatchScene(),
+ plan.command(),
+ dispatchDecision != null && dispatchDecision.isCircle()
+ );
+ }
+ if (context.sceneType() == RerouteSceneType.IDLE_RECOVER) {
+ saveStationTaskIdleTrack(new StationTaskIdleTrack(wrkMast.getWrkNo(), stationProtocol.getStationId(), System.currentTimeMillis()));
+ News.info("杈撻�佺珯鐐逛换鍔″仠鐣檣}绉掓湭杩愯锛屽凡閲嶆柊璁$畻璺緞骞堕噸鍚繍琛岋紝绔欑偣鍙�={}锛岀洰鏍囩珯={}锛屽伐浣滃彿={}锛屾竻鐞嗘棫鍒嗘鍛戒护鏁�={}锛屽懡浠ゆ暟鎹�={}",
+ STATION_IDLE_RECOVER_SECONDS,
+ stationProtocol.getStationId(),
+ plan.command().getTargetStaNo(),
+ wrkMast.getWrkNo(),
+ clearedCommandCount,
+ JSON.toJSONString(plan.command()));
+ return;
+ }
+ 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 List<NavigateNode> calcOutboundNavigatePath(WrkMast wrkMast,
+ Integer sourceStationId,
+ Integer targetStationId,
+ Double pathLenFactor) {
+ Double normalizedFactor = normalizePathLenFactor(pathLenFactor);
+ Integer currentTaskNo = wrkMast == null ? null : wrkMast.getWrkNo();
+ if (currentTaskNo == null) {
+ return navigateUtils.calcByStationId(sourceStationId, targetStationId, normalizedFactor);
+ }
+ return navigateUtils.calcByStationId(sourceStationId, targetStationId, currentTaskNo, normalizedFactor);
+ }
+
+ /**
+ * 璁$畻褰撳墠鍑哄簱浠诲姟鐨勮矾寰勫�惧悜绯绘暟銆�
+ *
+ * <p>杩欎釜绯绘暟涓嶆槸涓氬姟鐩爣绔欐湰韬紝鑰屾槸鈥滃湪澶氭潯鍙璺嚎涔嬮棿鏇村亸鍚戝摢涓�鏉♀�濈殑杈呭姪杈撳叆锛�
+ * 鐩殑鏄鍚屼竴鎵规銆佷笉鍚屽簭鍙风殑浠诲姟鍦ㄥ叡浜幆绾块噷灏介噺褰㈡垚绋冲畾銆佸彲閲嶅鐨勮矾寰勫垎甯冦��
+ *
+ * <p>杩斿洖鍊艰寖鍥村浐瀹氬湪 {@code [0, 1]}锛�
+ * 1. 闈炴壒娆″嚭搴撲换鍔★紝鐩存帴杩斿洖 {@code 0.0}锛岃〃绀轰笉寮曞叆棰濆璺緞鍋忕疆銆�
+ * 2. 褰撳墠鎵规鍙湁 1 涓湁鏁堟椿鍔ㄤ换鍔★紝杩斿洖 {@code 0.0}锛屽洜涓烘病鏈夆�滃墠鍚庨『搴忊�濆彲姣旇緝銆�
+ * 3. 鍚﹀垯鎸夆�滃綋鍓嶄换鍔″墠闈㈣繕鏈夊灏戜釜鏈夋晥鍓嶅簭浠诲姟鈥濆崰鈥滄湁鏁堟椿鍔ㄤ换鍔℃�绘暟鈥濈殑姣斾緥鏉ョ畻銆�
+ *
+ * <p>杩欓噷鐨勨�滄湁鏁堜换鍔♀�濆彧缁熻锛�
+ * 1. ioType=OUT 鐨勫嚭搴撲换鍔★紱
+ * 2. 浠嶅浜庢椿鍔ㄧ姸鎬侊紝鏈畬鎴�/鏈粨绠楋紱
+ * 3. 鏈� batchSeq锛�
+ * 4. mk != taskCancel銆�
+ *
+ * <p>缁撴灉鍚箟鍙互鐩磋鐞嗚В涓猴細
+ * 浠诲姟鎵规搴忓彿瓒婇潬鍚庯紝鍓嶉潰宸茬粡瀛樺湪鐨勬湁鏁堜换鍔¤秺澶氾紝寰楀埌鐨勭郴鏁拌秺澶э紱
+ * 鍚庣画绠楄矾鏃跺氨鏇村鏄撳拰鍓嶅簭浠诲姟褰㈡垚绋冲畾鐨勮矾寰勫垎娴侊紝鑰屼笉鏄墍鏈変换鍔¢兘璧板悓涓�鏉¢粯璁ょ煭璺��
+ *
+ * <p>娉ㄦ剰锛�
+ * 杩欎釜鏂规硶涓嶇洿鎺ュ喅瀹氱洰鏍囩珯锛屼笉璐熻矗鎺掑簭鏀捐锛屽彧鎻愪緵鈥滆矾寰勫亸濂解�濊緭鍏ャ��
+ * 鐪熸鐨勭洰鏍囩珯浠嶇敱 {@link #resolveOutboundDispatchDecision(Integer, WrkMast, List, Double)} 鍐冲畾銆�
+ */
+ private Double resolveOutboundPathLenFactor(WrkMast wrkMast) {
+ if (!isBatchOutboundTaskWithSeq(wrkMast)) {
+ return 0.0d;
+ }
+ List<WrkMast> activeBatchTaskList = loadActiveBatchTaskList(wrkMast.getBatch());
+ if (activeBatchTaskList.size() <= 1) {
+ return 0.0d;
+ }
+
+ int activeTaskCount = 0;
+ int predecessorCount = 0;
+ for (WrkMast item : activeBatchTaskList) {
+ if (!isFactorCandidateTask(item)) {
+ continue;
+ }
+ activeTaskCount++;
+ if (item.getBatchSeq() < wrkMast.getBatchSeq()) {
+ predecessorCount++;
+ }
+ }
+ if (activeTaskCount <= 1 || predecessorCount <= 0) {
+ return 0.0d;
+ }
+ return normalizePathLenFactor((double) predecessorCount / (double) (activeTaskCount - 1));
+ }
+
+ /**
+ * 鍒ゆ柇褰撳墠浠诲姟鏄惁鍏峰鈥滄寜鎵规鍑哄簱瑙勫垯鍙備笌鎺掑簭/璺緞鍋忓ソ璁$畻鈥濈殑鍩虹鏉′欢銆�
+ *
+ * <p>杩欓噷鍙仛鏈�鍩虹鐨勮祫鏍艰繃婊わ紝涓嶅叧蹇冨綋鍓嶆槸鍚︾湡鐨勯渶瑕佹帓搴忕偣浠嬪叆銆�
+ * 鍙涓嶆槸鎵规鍑哄簱浠诲姟锛屽悗闈㈢殑璺緞鍋忓ソ绯绘暟涓庢帓搴忕洰鏍囧喅绛栭兘搴旇鐩存帴閫�鍖栦负榛樿琛屼负銆�
+ */
+ private boolean isBatchOutboundTaskWithSeq(WrkMast wrkMast) {
+ return wrkMast != null
+ && Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)
+ && !Cools.isEmpty(wrkMast.getBatch())
+ && wrkMast.getBatchSeq() != null
+ && wrkMast.getWrkNo() != null;
+ }
+
+ /**
+ * 鍔犺浇鍚屼竴鎵规涓嬩粛澶勪簬娲诲姩涓殑鍑哄簱浠诲姟銆�
+ *
+ * <p>杩欓噷鐢ㄤ簬涓ょ被璁$畻锛�
+ * 1. 璁$畻璺緞鍋忓ソ绯绘暟鏃讹紝缁熻褰撳墠浠诲姟鍓嶉潰杩樻湁澶氬皯涓湁鏁堝墠搴忎换鍔°��
+ * 2. 褰撳墠鎺掑簭鐐归噸鏂板喅绛栨椂锛屾壘鍑鸿繖涓�鎵光�滈涓湭瀹屾垚浠诲姟鈥濈殑瀹為檯鎵规搴忓彿銆�
+ *
+ * <p>宸茬粡瀹屾垚/缁撶畻鐨勪换鍔′笉鍐嶅弬涓庡綋鍓嶆壒娆$殑鎺掑簭涓庡亸濂借绠椼��
+ */
+ private List<WrkMast> loadActiveBatchTaskList(String batch) {
+ if (Cools.isEmpty(batch)) {
+ return Collections.emptyList();
+ }
+ return wrkMastService.list(new QueryWrapper<WrkMast>()
+ .eq("io_type", WrkIoType.OUT.id)
+ .eq("batch", batch)
+ .notIn("wrk_sts",
+ WrkStsType.STATION_RUN_COMPLETE.sts,
+ WrkStsType.COMPLETE_OUTBOUND.sts,
+ WrkStsType.SETTLE_OUTBOUND.sts));
+ }
+
+ /**
+ * 鍒ゆ柇鏌愭潯鎵规浠诲姟鏄惁搴旇璁″叆璺緞鍋忓ソ绯绘暟鐨勫垎姣�/鍒嗗瓙缁熻銆�
+ *
+ * <p>杩欓噷鎺掗櫎娌℃湁 batchSeq 鐨勪换鍔′互鍙婅鏄惧紡鏍囪涓� taskCancel 鐨勪换鍔★紝
+ * 閬垮厤鏃犳晥浠诲姟鎶婂悓鎵规鐨勮矾寰勫亸濂借绠楁媺鍋忋��
+ */
+ private boolean isFactorCandidateTask(WrkMast wrkMast) {
+ return wrkMast != null
+ && Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)
+ && wrkMast.getBatchSeq() != null
+ && !"taskCancel".equals(wrkMast.getMk());
}
public List<Integer> getAllOutOrderList() {
@@ -707,25 +1081,312 @@
return list;
}
- private Integer resolveDispatchOutOrderTarget(Integer currentStationId,
+ /**
+ * 缁熶竴璁$畻褰撳墠浠诲姟鈥滄鍒诲簲璇ユ湞鍝釜鐩爣绔欑户缁繍琛屸�濄��
+ *
+ * <p>杩欐槸鍑哄簱鎺掑簭銆佺粫鍦堛�佸牭濉為噸瑙勫垝鍏辩敤鐨勭洰鏍囪鍐冲叆鍙c��
+ * 涓嶇瑙﹀彂鏉ユ簮鏄� OUT_ORDER銆乄ATCH_CIRCLE 杩樻槸 RUN_BLOCK_REROUTE锛�
+ * 鍙涓氬姟璇箟杩樻槸鈥滃綋鍓嶈繖绁ㄥ嚭搴撲换鍔′笅涓�姝ヨ寰�鍝噷璧扳�濓紝閮戒粠杩欓噷寰楀嚭鐩爣绔欍��
+ *
+ * <p>瀹冨仛涓夊眰鍒ゆ柇锛�
+ * 1. 濡傛灉褰撳墠浠诲姟鏍规湰涓嶉�傜敤鍑哄簱鎺掑簭锛岀洿鎺ヨ繑鍥炰换鍔′笟鍔$洰鏍囩珯 {@code wrkMast.staNo}銆�
+ * 2. 濡傛灉閫傜敤鍑哄簱鎺掑簭锛屽厛绠楀嚭鈥滃綋鍓嶆壒娆¤鍒欎笅锛屾鍒诲厑璁稿墠寰�鐨� dispatchStationId鈥濄��
+ * 3. 濡傛灉褰撳墠绔欑偣姝eソ灏辨槸鎺掑簭鍐崇瓥鐐癸紝鍐嶈繘涓�姝ュ垽鏂槸锛�
+ * 鐩存帴鍘荤洰鏍囩偣锛岃繕鏄厛杩涘叆缁曞湀鐩爣鐐癸紝鎴栬�呭洜涓轰弗鏍肩獥鍙i檺鍒惰�屾殏涓嶆斁琛屻��
+ *
+ * <p>杩斿洖鐨� {@link OutOrderDispatchDecision} 涓嶅彧鏄竴涓洰鏍囩珯锛�
+ * 杩樻惡甯︿簡杩欐鍐崇瓥鏄惁灞炰簬缁曞湀銆佹槸鍚︽潵鑷綋鍓嶆帓搴忕偣閲嶆柊瑁佸喅绛夎涔変俊鎭紝
+ * 渚涘悗缁棩蹇椼�乻ession 璁板綍鍜� watch-circle 鍒ゅ畾浣跨敤銆�
+ *
+ * <p>鍙傛暟鍚箟锛�
+ * 1. {@code currentStationId}锛氫换鍔″綋鍓嶆墍鍦ㄧ珯鐐广�傜敤浜庡垽鏂綋鍓嶆槸涓嶆槸鎺掑簭鍐崇瓥鐐广��
+ * 褰撳墠鏄笉鏄凡缁忓埌杈� watch-circle 鐨勪笅涓�鍐崇瓥绔欍��
+ * 2. {@code wrkMast}锛氬綋鍓嶄换鍔′富鐘舵�侊紝鑷冲皯瑕佹彁渚涗笟鍔$洰鏍囩珯銆佹潵婧愮珯銆佹壒娆°�佸簭鍙风瓑淇℃伅銆�
+ * 3. {@code outOrderStationIds}锛氬綋鍓嶈澶囬厤缃殑鎵�鏈夊嚭搴撴帓搴忕珯鐐瑰垪琛ㄣ��
+ * 4. {@code pathLenFactor}锛氱敱 {@link #resolveOutboundPathLenFactor(WrkMast)} 寰楀埌鐨勮矾寰勫亸濂界郴鏁帮紝
+ * 鐢ㄦ潵璁╁悓涓�鎵规浠诲姟鍦ㄩ�夋嫨 dispatch target 鏃朵繚鎸佺ǔ瀹氱殑璺緞鍊惧悜銆�
+ *
+ * <p>杩斿洖鍊艰涔夛細
+ * 1. 杩斿洖 {@code null}锛氬綋鍓嶆棤娉曞緱鍒板悎娉曠洰鏍囩珯锛岃皟鐢ㄦ柟搴旇烦杩囨湰娆℃淳鍙戙��
+ * 2. 杩斿洖 {@code targetStationId=wrkMast.staNo, circle=false}锛�
+ * 褰撳墠涓嶉渶瑕佸嚭搴撴帓搴忓共棰勶紝鎴栧凡鍏佽鐩存帴鍘讳笟鍔$洰鏍囩珯銆�
+ * 3. 杩斿洖 {@code targetStationId!=wrkMast.staNo}锛�
+ * 褰撳墠搴旇鍏堝幓涓�涓腑闂� dispatch 鐩爣绔欙紝鍚庣画鍐嶇敱鎺掑簭鐐�/缁曞湀鐐圭户缁喅绛栥��
+ * 4. 杩斿洖 {@code circle=true}锛�
+ * 褰撳墠灞炰簬缁曞湀鍐崇瓥缁撴灉锛屽悗缁� watch-circle 閫昏緫浼氭嵁姝ゆ帴绠°��
+ *
+ * <p>娉ㄦ剰锛�
+ * 杩欎釜鏂规硶鍙喅瀹氣�滅洰鏍囩珯鈥濓紝涓嶇洿鎺ョ敓鎴愯緭閫佸懡浠ゃ��
+ * 鐪熸鐨勮矾寰勭敱鏅�氱畻璺垨 run-block 涓撶敤绠楄矾鍦ㄥ悗缁楠ょ敓鎴愩��
+ */
+ private OutOrderDispatchDecision resolveOutboundDispatchDecision(Integer currentStationId,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor) {
+ if (wrkMast == null || wrkMast.getStaNo() == null) {
+ return null;
+ }
+ if (!shouldApplyOutOrder(wrkMast, outOrderStationIds)) {
+ return new OutOrderDispatchDecision(wrkMast.getStaNo(), false);
+ }
+ Integer dispatchStationId = resolveDispatchOutOrderTarget(
+ wrkMast,
+ wrkMast.getSourceStaNo(),
+ wrkMast.getStaNo(),
+ outOrderStationIds,
+ pathLenFactor
+ );
+ if (dispatchStationId == null) {
+ return null;
+ }
+ if (isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor)) {
+ return resolveCurrentOutOrderDispatchDecision(currentStationId, wrkMast, outOrderStationIds, pathLenFactor);
+ }
+ if (!Objects.equals(dispatchStationId, wrkMast.getStaNo())
+ && isCurrentOutOrderStation(currentStationId, outOrderStationIds)
+ && isWatchingCircleArrival(wrkMast.getWrkNo(), currentStationId)) {
+ return new OutOrderDispatchDecision(dispatchStationId, true, null, false);
+ }
+ return new OutOrderDispatchDecision(dispatchStationId, false);
+ }
+
+ /**
+ * 鍦ㄢ�滃綋鍓嶇珯鐐瑰氨鏄湰娆℃帓搴忓喅绛栫偣鈥濇椂锛岃绠楄繖閲屽埌搴曡鐩存帴鏀捐杩樻槸杩涘叆缁曞湀銆�
+ *
+ * <p>杩欐槸鍑哄簱鎺掑簭閲屾渶鏍稿績鐨勫眬閮ㄥ喅绛栨柟娉曘�傝繘鍏ヨ繖閲屼箣鍓嶏紝宸茬粡婊¤冻锛�
+ * 1. 褰撳墠浠诲姟閫傜敤 out-order锛�
+ * 2. 褰撳墠绔欑偣灏辨槸杩欑エ浠诲姟姝ゅ埢瀵瑰簲鐨� dispatch 鎺掑簭鐐广��
+ *
+ * <p>鍐呴儴鍐崇瓥椤哄簭锛�
+ * 1. 鍏堝彇鍑哄悓鎵规浠嶆湭瀹屾垚鐨勪换鍔★紝鎵惧嚭杩欐壒褰撳墠鈥滃簲褰撹浼樺厛鏀捐鈥濈殑搴忓彿浣嶇疆銆�
+ * 2. 鍐嶇粨鍚堝綋鍓嶄换鍔″湪鍒濆璺緞涓婄殑鎺掑簭绐楀彛浣嶇疆锛屽垽鏂嚜宸辨鍒昏兘鍚︾洿鎺ュ幓涓氬姟鐩爣绔欍��
+ * 3. 濡傛灉鐞嗚涓婅鑷繁鏀捐锛岃繕瑕侀澶栨鏌ョ洰鏍囨柟鍚戜笂鏄惁瀛樺湪鍙繘鍏ョ殑 release slot銆�
+ * 4. 濡傛灉涓嶈兘鐩磋揪锛屾垨鑰呯洿杈炬柟鍚戝綋鍓嶅叏閮ㄩ樆濉烇紝灏辫浆鎴� circle 鍐崇瓥锛屽鎵句笅涓�鎺掑簭妫�娴嬬偣銆�
+ *
+ * <p>杩斿洖鍊硷細
+ * 1. {@code circle=false} 琛ㄧず褰撳墠鎺掑簭鐐瑰凡缁忓厑璁哥洿鎺ュ幓涓氬姟鐩爣绔欍��
+ * 2. {@code circle=true} 琛ㄧず褰撳墠鍙兘鍏堝幓涓嬩竴缁曞湀鐩爣绔欙紝鍚庣画鐢� watch-circle/鎺掑簭鐐圭户缁帴鍔涘喅绛栥��
+ * 3. {@code null} 琛ㄧず褰撳墠鏃笉鑳界洿杈撅紝涔熸病鎵惧埌鍚堟硶鐨勪笅涓�缁曞湀鐐癸紝璋冪敤鏂瑰簲璺宠繃鏈娲惧彂銆�
+ */
+ private OutOrderDispatchDecision resolveCurrentOutOrderDispatchDecision(Integer currentStationId,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor) {
+ if (!isCurrentOutOrderDispatchStation(currentStationId, wrkMast, outOrderStationIds, pathLenFactor)) {
+ 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 = calcOutboundNavigatePath(wrkMast, wrkMast.getSourceStaNo(), wrkMast.getStaNo(), pathLenFactor);
+ } 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());
+ }
+ if (toTarget) {
+ if (hasReachableOutReleaseSlot(wrkMast, currentStationId, wrkMast.getStaNo(), pathLenFactor)) {
+ return new OutOrderDispatchDecision(wrkMast.getStaNo(), false);
+ }
+ StationTaskLoopService.LoopEvaluation loopEvaluation = evaluateOutOrderLoop(
+ wrkMast.getWrkNo(),
+ currentStationId,
+ outOrderStationIds
+ );
+ Integer circleTarget = resolveNextCircleOrderTarget(
+ wrkMast,
+ currentStationId,
+ outOrderStationIds,
+ loopEvaluation.getExpectedLoopIssueCount(),
+ pathLenFactor
+ );
+ if (circleTarget == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "鐩爣绔欏綋鍓嶄笉鍙繘锛屼笖鏈壘鍒板彲鎵ц鐨勪笅涓�鎺掑簭妫�娴嬬偣锛屽綋鍓嶇珯鐐�={}", currentStationId);
+ return null;
+ }
+ return new OutOrderDispatchDecision(circleTarget, true, loopEvaluation, true);
+ }
+
+ StationTaskLoopService.LoopEvaluation loopEvaluation = evaluateOutOrderLoop(
+ wrkMast.getWrkNo(),
+ currentStationId,
+ outOrderStationIds
+ );
+ Integer circleTarget = resolveNextCircleOrderTarget(
+ wrkMast,
+ currentStationId,
+ outOrderStationIds,
+ loopEvaluation.getExpectedLoopIssueCount(),
+ pathLenFactor
+ );
+ if (circleTarget == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "鏈壘鍒板彲鎵ц鐨勪笅涓�鎺掑簭妫�娴嬬偣锛屽綋鍓嶇珯鐐�={}", currentStationId);
+ return null;
+ }
+ return new OutOrderDispatchDecision(circleTarget, true, loopEvaluation, true);
+ }
+
+ /**
+ * 鍒ゆ柇杩欑エ浠诲姟鍦ㄥ綋鍓嶈澶囦笂鏄惁搴旇鍚敤鍑哄簱鎺掑簭閫昏緫銆�
+ *
+ * <p>鍙缂哄皯浠讳綍涓�涓帓搴忓墠鎻愶紝渚嬪涓嶆槸鍑哄簱浠诲姟銆佹病鏈夋壒娆°�佹病鏈夊簭鍙枫��
+ * 褰撳墠璁惧涔熸病鏈夐厤缃帓搴忕偣锛屽氨搴旇鐩存帴閫�鍥炩�滄櫘閫氱洰鏍囩珯鍐崇瓥鈥濄��
+ */
+ 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();
+ }
+
+ /**
+ * 鍒ゆ柇褰撳墠鎵�鍦ㄧ珯鐐规槸鍚﹀氨鏄�滆繖绁ㄤ换鍔℃鍒诲簲璇ヨЕ鍙戞帓搴忓喅绛栫殑 dispatch 绔欑偣鈥濄��
+ *
+ * <p>娉ㄦ剰瀹冧笉鏄畝鍗曞垽鏂�滃綋鍓嶇珯鐐规槸鍚﹀睘浜庢帓搴忕偣鍒楄〃鈥濓紝
+ * 鑰屾槸鍏堟牴鎹畬鏁磋矾寰勫弽鎺ㄥ嚭褰撳墠浠诲姟瀵瑰簲鐨� dispatch 鎺掑簭鐐癸紝
+ * 鍐嶅垽鏂綋鍓嶄綅缃槸鍚︽濂界瓑浜庤繖涓� dispatch 鐐广��
+ */
+ private boolean isCurrentOutOrderDispatchStation(Integer currentStationId,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor) {
+ if (!shouldApplyOutOrder(wrkMast, outOrderStationIds) || currentStationId == null) {
+ return false;
+ }
+ Integer dispatchStationId = resolveDispatchOutOrderTarget(
+ wrkMast,
+ wrkMast.getSourceStaNo(),
+ wrkMast.getStaNo(),
+ outOrderStationIds,
+ pathLenFactor
+ );
+ return dispatchStationId != null
+ && !Objects.equals(dispatchStationId, wrkMast.getStaNo())
+ && Objects.equals(currentStationId, dispatchStationId);
+ }
+
+ /**
+ * 鍒ゆ柇褰撳墠浣嶇疆鏄惁灞炰簬璁惧閰嶇疆閲岀殑浠绘剰涓�涓帓搴忕偣銆�
+ *
+ * <p>杩欎釜鍒ゆ柇姣� {@link #isCurrentOutOrderDispatchStation(Integer, WrkMast, List, Double)} 鏇村锛�
+ * 鍙洖绛斺�滃綋鍓嶄綅缃槸涓嶆槸鎺掑簭鐐光�濓紝涓嶅洖绛斺�滄槸涓嶆槸杩欑エ浠诲姟褰撳墠搴旇鍛戒腑鐨勬帓搴忕偣鈥濄��
+ */
+ private boolean isCurrentOutOrderStation(Integer currentStationId,
+ List<Integer> outOrderStationIds) {
+ return currentStationId != null
+ && outOrderStationIds != null
+ && outOrderStationIds.contains(currentStationId);
+ }
+
+ private void syncOutOrderWatchState(WrkMast wrkMast,
+ Integer currentStationId,
+ List<Integer> outOrderStationIds,
+ OutOrderDispatchDecision dispatchDecision,
+ StationCommand command) {
+ if (dispatchDecision == null || command == null || !shouldApplyOutOrder(wrkMast, outOrderStationIds)) {
+ return;
+ }
+ if (dispatchDecision.isCircle()) {
+ saveWatchCircleCommand(wrkMast.getWrkNo(), command);
+ if (dispatchDecision.shouldCountLoopIssue()
+ && stationTaskLoopService != null
+ && dispatchDecision.getLoopEvaluation() != null) {
+ stationTaskLoopService.recordLoopIssue(dispatchDecision.getLoopEvaluation(), "OUT_ORDER_CIRCLE");
+ }
+ } else {
+ clearWatchCircleCommand(wrkMast.getWrkNo());
+ }
+ }
+
+ /**
+ * 涓哄綋鍓嶆帓搴忕偣鍐崇瓥棰勫厛鍋氫竴娆$幆绾胯瘎浼般��
+ *
+ * <p>褰撴湰娆″喅绛栨渶缁堣繘鍏ョ粫鍦堟椂锛岃瘎浼扮粨鏋滀細琚甫杩� {@link OutOrderDispatchDecision}锛�
+ * 鍚庣画鐢ㄤ簬璁板綍 loop issue 缁熻锛岃�屼笉鏄湪鐪熸涓嬪彂鍚庡啀閲嶅璇勪及涓�娆°��
+ */
+ private StationTaskLoopService.LoopEvaluation evaluateOutOrderLoop(Integer taskNo,
+ Integer currentStationId,
+ List<Integer> outOrderStationIds) {
+ if (stationTaskLoopService == null) {
+ return new StationTaskLoopService.LoopEvaluation(
+ taskNo,
+ currentStationId,
+ StationTaskLoopService.LoopIdentitySnapshot.empty(),
+ 0,
+ 0,
+ false
+ );
+ }
+ return stationTaskLoopService.evaluateLoop(
+ taskNo,
+ currentStationId,
+ true,
+ outOrderStationIds,
+ "outOrderCircle"
+ );
+ }
+
+ /**
+ * 浠庘�滄簮绔欏埌涓氬姟鐩爣绔欌�濈殑瀹屾暣璺緞閲岋紝鍙嶆帹鍑哄綋鍓嶄换鍔″簲褰撳厛鍛戒腑鐨� dispatch 鎺掑簭鐐广��
+ *
+ * <p>鍋氭硶鏄細
+ * 1. 鍏堣绠椾粠 sourceStationId 鍒� finalTargetStationId 鐨勫畬鏁村鑸矾寰勶紱
+ * 2. 鍐嶄粠璺緞灏鹃儴鍚戝墠鎵弿锛�
+ * 3. 鎵惧埌绂绘渶缁堢洰鏍囨渶杩戠殑閭d釜鎺掑簭鐐癸紝浣滀负褰撳墠 dispatch 鐩爣銆�
+ *
+ * <p>濡傛灉璺緞涓婃牴鏈病鏈夋帓搴忕偣锛屾垨鑰呯己灏戞簮绔�/鎺掑簭鐐归厤缃紝
+ * 灏辩洿鎺ユ妸涓氬姟鐩爣绔欐湰韬綋鎴� dispatch 鐩爣銆�
+ */
+ private Integer resolveDispatchOutOrderTarget(WrkMast wrkMast,
+ Integer sourceStationId,
Integer finalTargetStationId,
List<Integer> outOrderList,
- boolean skipCurrentStation) {
+ Double pathLenFactor) {
if (finalTargetStationId == null) {
return null;
}
- if (currentStationId == null || outOrderList == null || outOrderList.isEmpty()) {
+ if (sourceStationId == null || outOrderList == null || outOrderList.isEmpty()) {
return finalTargetStationId;
}
try {
- List<NavigateNode> nodes = navigateUtils.calcByStationId(currentStationId, finalTargetStationId);
+ List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, sourceStationId, finalTargetStationId, pathLenFactor);
for (int i = nodes.size() - 1; i >= 0; i--) {
Integer stationId = getStationIdFromNode(nodes.get(i));
if (stationId == null) {
continue;
}
- if (skipCurrentStation && currentStationId.equals(stationId)) {
+ if (Objects.equals(stationId, finalTargetStationId)) {
continue;
}
if (outOrderList.contains(stationId)) {
@@ -736,13 +1397,102 @@
return finalTargetStationId;
}
- private Integer resolveNextCircleOrderTarget(Integer currentStationId, List<Integer> orderedOutStationList) {
+ /**
+ * 鍒ゆ柇浠庡綋鍓嶆帓搴忕偣缁х画鍓嶅線鏈�缁堜笟鍔$洰鏍囩珯鏃讹紝璺緞涓婃槸鍚﹁嚦灏戝瓨鍦ㄤ竴涓彲杩涘叆鐨勫悗缁珯鐐广��
+ *
+ * <p>杩欓噷涓嶆槸瑕佹眰鏁存潯璺緞瀹屽叏鐣呴�氾紝鑰屾槸鍒ゆ柇鈥滃綋鍓嶆槸鍚︽湁閲婃斁鍙e彲璧扳�濄��
+ * 鍙鍚庣画璺緞涓婂瓨鍦ㄤ竴涓湭闃诲绔欑偣锛屽氨璁や负褰撳墠浠嶅彲灏濊瘯鐩磋揪锛�
+ * 鍙湁鏁存鍚庣画璺緞鐪嬭捣鏉ラ兘琚樆濉炴椂锛屾墠浼氬己鍒惰浆鍏ョ粫鍦堛��
+ */
+ private boolean hasReachableOutReleaseSlot(WrkMast wrkMast,
+ Integer currentStationId,
+ Integer finalTargetStationId,
+ Double pathLenFactor) {
+ if (currentStationId == null || finalTargetStationId == null) {
+ return true;
+ }
+
+ try {
+ List<NavigateNode> nodes = calcOutboundNavigatePath(wrkMast, currentStationId, finalTargetStationId, pathLenFactor);
+ if (nodes == null || nodes.isEmpty()) {
+ return true;
+ }
+
+ for (NavigateNode node : nodes) {
+ Integer stationId = getStationIdFromNode(node);
+ if (stationId == null || Objects.equals(stationId, currentStationId)) {
+ continue;
+ }
+
+ if (!isPathStationBlocked(stationId)) {
+ return true;
+ }
+ }
+ return false;
+ } catch (Exception ignore) {
+ return true;
+ }
+ }
+
+ private boolean isPathStationBlocked(Integer stationId) {
+ if (stationId == null) {
+ return true;
+ }
+
+ BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", stationId));
+ if (basStation == null) {
+ return true;
+ }
+
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
+ if (stationThread == null) {
+ return true;
+ }
+
+ StationProtocol stationProtocol = stationThread.getStatusMap().get(stationId);
+ if (stationProtocol == null) {
+ return true;
+ }
+
+ return !stationProtocol.isAutoing()
+ || stationProtocol.isLoading()
+ || (stationProtocol.getTaskNo() != null && stationProtocol.getTaskNo() > 0);
+ }
+
+ /**
+ * 涓衡�滄帓搴忕偣闇�瑕佺粫鍦堚�濈殑鍦烘櫙锛屼粠鍑哄簱鎺掑簭绔欑偣鍒楄〃閲屾寫鍑轰笅涓�璺崇粫鍦堢洰鏍囥��
+ * <p>
+ * 杩欓噷鐨勮緭鍏ヤ笉鏄暣寮犲湴鍥撅紝鑰屾槸宸茬粡鎸変笟鍔¢『搴忔帓濂界殑鍑哄簱绔欑偣搴忓垪銆傛柟娉曚細浠庡綋鍓嶇珯鐐瑰湪璇ュ簭鍒椾腑鐨勫悗缁у紑濮嬶紝
+ * 渚濇灏濊瘯姣忎釜鍊欓�夌珯鐐癸紝骞惰绠椻�滃綋鍓嶇珯鐐� -> 鍊欓�夌珯鐐光�濈殑鍙璺緞锛�
+ * <p>
+ * 1. 鍙鑳界畻鍑鸿矾寰勶紝灏辨妸鍊欓�夌珯鐐硅褰曚负涓�涓彲閫夌粫鍦堢洰鏍囷紱
+ * 2. 璁板綍涓ょ被鎺掑簭淇℃伅锛�
+ * - pathLength锛氬埌璇ュ�欓�夌偣鐨勮矾寰勯暱搴︼紝瓒婄煭璇存槑瓒婇�傚悎鍏堟嬁鏉ュ仛涓存椂缂撳啿锛�
+ * - offset锛氳绔欑偣鍦ㄦ帓搴忓簭鍒椾腑璺濈褰撳墠绔欑偣鏈夊杩滐紝鐢ㄦ潵鍦ㄨ矾寰勯暱搴︾浉鍚岀殑鏃跺�欎繚鐣欐棦鏈夐『搴忔劅锛�
+ * 3. 鏈�鍚庢妸鍊欓�夊垪琛ㄤ氦缁� {@link #resolveGradualCircleTargetByPathLength(Integer, List, Double)}锛�
+ * 鍐嶆牴鎹�滃綋鍓嶅凡缁忕粫鍦堝灏戞鈥濅笌鈥滆矾寰勯暱搴﹀亸濂界郴鏁扳�濅粠涓嶅悓闀垮害灞傜骇閲屾寫鏈�缁堢洰鏍囥��
+ * <p>
+ * 杩欎釜鏂规硶鏈韩涓嶅垽鏂�滄槸鍚﹀簲璇ョ粫鍦堚�濓紝鍙礋璐e湪宸茬粡鍐冲畾缁曞湀鍚庯紝浠庢帓搴忕珯鐐归摼璺噷鎵句竴涓笅涓�璺崇紦鍐茬偣銆�
+ *
+ * @param wrkMast 褰撳墠浠诲姟锛屼富瑕佺敤浜庣畻璺�
+ * @param currentStationId 褰撳墠绔欑偣锛屽嵆鏈鍑嗗浠庡摢閲屽彂鍑虹粫鍦堝懡浠�
+ * @param orderedOutStationList 宸叉寜涓氬姟椤哄簭鎺掑ソ鐨勫嚭搴撶珯鐐瑰垪琛�
+ * @param expectedLoopIssueCount 棰勮宸插彂鐢熺殑缁曞湀/鍫靛杞锛岀敤浜庡喅瀹氭槸鍚﹂�愭鏀惧ぇ缁曞湀鍗婂緞
+ * @param pathLenFactor 褰撳墠浠诲姟瀵瑰簲鐨勮矾寰勫亸濂界郴鏁帮紝褰卞搷 calcOutboundNavigatePath 鐨勯�夎矾缁撴灉
+ * @return 涓嬩竴璺崇粫鍦堢洰鏍囩珯锛涘鏋滄病鏈変换浣曞彲鍒拌揪鍊欓�夊垯杩斿洖 null
+ */
+ private Integer resolveNextCircleOrderTarget(WrkMast wrkMast,
+ Integer currentStationId,
+ List<Integer> orderedOutStationList,
+ Integer expectedLoopIssueCount,
+ Double pathLenFactor) {
if (currentStationId == null || orderedOutStationList == null || orderedOutStationList.size() <= 1) {
return null;
}
int startIndex = orderedOutStationList.indexOf(currentStationId);
int total = orderedOutStationList.size();
+ List<CircleTargetCandidate> candidateList = new ArrayList<>();
for (int offset = 1; offset < total; offset++) {
int candidateIndex = (startIndex + offset + total) % total;
Integer candidateStationId = orderedOutStationList.get(candidateIndex);
@@ -750,13 +1500,88 @@
continue;
}
try {
- List<NavigateNode> path = navigateUtils.calcByStationId(currentStationId, candidateStationId);
+ List<NavigateNode> path = calcOutboundNavigatePath(wrkMast, currentStationId, candidateStationId, pathLenFactor);
if (path != null && !path.isEmpty()) {
- return candidateStationId;
+ candidateList.add(new CircleTargetCandidate(candidateStationId, path.size(), offset));
}
} catch (Exception ignore) {}
}
- return null;
+ if (candidateList.isEmpty()) {
+ return null;
+ }
+ candidateList.sort(new Comparator<CircleTargetCandidate>() {
+ @Override
+ public int compare(CircleTargetCandidate left, CircleTargetCandidate right) {
+ if (left == right) {
+ return 0;
+ }
+ if (left == null) {
+ return 1;
+ }
+ if (right == null) {
+ return -1;
+ }
+ int pathCompare = Integer.compare(left.getPathLength(), right.getPathLength());
+ if (pathCompare != 0) {
+ return pathCompare;
+ }
+ return Integer.compare(left.getOffset(), right.getOffset());
+ }
+ });
+ return resolveGradualCircleTargetByPathLength(expectedLoopIssueCount, candidateList, pathLenFactor);
+ }
+
+ /**
+ * 鍦ㄢ�滃凡鎸夎矾寰勯暱搴﹀崌搴忔帓濂解�濈殑缁曞湀鍊欓�夊垪琛ㄤ腑锛屾寜灞傜骇娓愯繘鍦版寫閫夌洰鏍囩珯銆�
+ * <p>
+ * candidateList 閲屽彲鑳藉瓨鍦ㄥ涓�欓�夌偣鎷ユ湁鐩稿悓鐨� pathLength銆傚缁曞湀鍐崇瓥鏉ヨ锛�
+ * 鍚屼竴闀垮害灞傞噷鐨勫�欓�夌偣閮藉睘浜庘�滃悓涓�缁曞湀鍗婂緞鈥濓紝鐪熸闇�瑕佹帶鍒剁殑鏄細
+ * <p>
+ * 1. 鍒濇/鍓嶅嚑娆″牭濉炴椂锛屼紭鍏堥�夋嫨鏈�鐭彲杈惧眰锛屽敖閲忕敤鏈�灏忕粫琛岃窛绂绘仮澶嶆祦杞紱
+ * 2. 濡傛灉浠诲姟宸茬粡杩炵画澶氭鍦ㄥ悓涓�鍖哄煙缁曞湀锛岃鏄庣煭鍗婂緞鍊欓�夊ぇ姒傜巼宸茬粡璇曡繃鎴栨仮澶嶆晥鏋滃樊锛�
+ * 灏遍渶瑕侀�愭鏀惧ぇ鍒版洿杩滀竴灞傦紱
+ * 3. pathLenFactor 浠h〃褰撳墠浠诲姟瀵光�滅煭璺緞/闀胯矾寰勨�濈殑鍋忓ソ锛屽厑璁稿湪鐩稿悓鍫靛杞涓嬮�傚害寰�鏇磋繙灞傚亸绉汇��
+ * <p>
+ * 鍥犳杩欓噷鍏堟妸 candidateList 鍘嬬缉鎴愨�滄寜 pathLength 鍘婚噸鍚庣殑 tierList鈥濓紝姣忎釜 tier 鍙繚鐣欒闀垮害灞傜殑棣栦釜鍊欓�夈��
+ * 鐒跺悗鍚屾椂璁$畻涓や釜灞傜骇绱㈠紩锛�
+ * <p>
+ * - defaultTierIndex锛氬熀浜� expectedLoopIssueCount 鐨勯粯璁ゆ斁澶у眰绾э紱
+ * - factorTierIndex锛氬熀浜� pathLenFactor 鐨勫亸濂藉眰绾э紱
+ * <p>
+ * 鏈�缁堝彇涓よ�呰緝澶у�硷紝鍚箟鏄�滆嚦灏戞弧瓒冲綋鍓嶅牭濉炶疆娆¢渶瑕佺殑鏀惧ぇ鍗婂緞锛屽悓鏃跺厑璁歌矾寰勫亸濂芥妸鐩爣鎺ㄥ悜鏇磋繙灞傜骇鈥濄��
+ *
+ * @param expectedLoopIssueCount 棰勮宸插彂鐢熺殑缁曞湀/鍫靛杞
+ * @param candidateList 宸叉寜 pathLength銆乷ffset 鎺掑簭鐨勫�欓�夊垪琛�
+ * @param pathLenFactor 褰撳墠浠诲姟鐨勮矾寰勫亸濂界郴鏁�
+ * @return 鏈�缁堥�変腑鐨勭粫鍦堢洰鏍囩珯锛涜嫢娌℃湁鍊欓�夊垯杩斿洖 null
+ */
+ private Integer resolveGradualCircleTargetByPathLength(Integer expectedLoopIssueCount,
+ List<CircleTargetCandidate> candidateList,
+ Double pathLenFactor) {
+ if (candidateList == null || candidateList.isEmpty()) {
+ return null;
+ }
+
+ List<CircleTargetCandidate> tierList = new ArrayList<>();
+ Integer lastPathLength = null;
+ for (CircleTargetCandidate candidate : candidateList) {
+ if (candidate == null) {
+ continue;
+ }
+ if (lastPathLength == null || !Objects.equals(lastPathLength, candidate.getPathLength())) {
+ tierList.add(candidate);
+ lastPathLength = candidate.getPathLength();
+ }
+ }
+ if (tierList.isEmpty()) {
+ return candidateList.get(0).getStationId();
+ }
+ int defaultTierIndex = expectedLoopIssueCount == null || expectedLoopIssueCount <= 2
+ ? 0
+ : Math.min(expectedLoopIssueCount - 2, tierList.size() - 1);
+ int factorTierIndex = (int) Math.round(normalizePathLenFactor(pathLenFactor) * (tierList.size() - 1));
+ int tierIndex = Math.max(defaultTierIndex, factorTierIndex);
+ return tierList.get(tierIndex).getStationId();
}
private boolean tryAcquireOutOrderDispatchLock(Integer wrkNo, Integer stationId) {
@@ -772,9 +1597,61 @@
return true;
}
+ private boolean shouldSkipOutOrderDispatchForExistingRoute(Integer wrkNo, Integer stationId) {
+ if (stationMoveCoordinator == null || wrkNo == null || wrkNo <= 0 || stationId == null) {
+ return false;
+ }
+ StationMoveSession session = stationMoveCoordinator.loadSession(wrkNo);
+ if (session == null) {
+ return false;
+ }
+ if (!session.isActive() || !session.containsStation(stationId)) {
+ return false;
+ }
+ // 缁曞湀璺嚎鍦ㄥ綋鍓嶇珯鐐瑰皻鏈蛋瀹屾椂锛屾帓搴忕偣涓嶅簲鍐嶆鎻掓墜銆�
+ if (StationMoveDispatchMode.CIRCLE == session.getDispatchMode()) {
+ return true;
+ }
+ // 鐩存帴璺嚎鍙湪鈥滃綋鍓嶇珯鐐瑰凡缁忚鍒殑娲诲姩璺嚎鍗犱綇涓旂洰鏍囦笉鍚屸�濇椂鎵嶆嫤鎴��
+ return !Objects.equals(stationId, session.getCurrentRouteTargetStationId());
+ }
+
private boolean isWatchingCircleArrival(Integer wrkNo, Integer stationId) {
+ if (stationMoveCoordinator != null) {
+ StationMoveSession session = stationMoveCoordinator.loadSession(wrkNo);
+ if (session != null && session.isActive() && stationId != null) {
+ // nextDecisionStationId 琛ㄧず杩欐潯璺嚎鐪熸绛夊緟閲嶆柊鍐崇瓥鐨勭珯鐐癸紝鍒扮珯鎵嶈Е鍙� watch-circle銆�
+ if (stationId.equals(session.getNextDecisionStationId())) {
+ return true;
+ }
+ // 杩樺湪 session 璺緞涓棿绔欒繍琛屾椂涓嶅簲璇Е鍙戙��
+ if (session.containsStation(stationId)) {
+ return false;
+ }
+ }
+ }
StationCommand command = getWatchCircleCommand(wrkNo);
return command != null && stationId != null && stationId.equals(command.getTargetStaNo());
+ }
+
+ private boolean isWatchingCircleTransit(Integer wrkNo, Integer stationId) {
+ if (stationMoveCoordinator != null) {
+ StationMoveSession session = stationMoveCoordinator.loadSession(wrkNo);
+ if (session != null && session.isActive() && stationId != null) {
+ if (stationId.equals(session.getNextDecisionStationId())) {
+ return false;
+ }
+ if (session.containsStation(stationId)) {
+ return true;
+ }
+ }
+ }
+ StationCommand command = getWatchCircleCommand(wrkNo);
+ if (command == null || stationId == null || Objects.equals(stationId, command.getTargetStaNo())) {
+ return false;
+ }
+ List<Integer> navigatePath = command.getNavigatePath();
+ return navigatePath != null && navigatePath.contains(stationId);
}
private StationCommand getWatchCircleCommand(Integer wrkNo) {
@@ -807,7 +1684,448 @@
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 (shouldSkipIdleRecoverForRecentDispatch(stationProtocol.getTaskNo(), stationProtocol.getStationId())) {
+ return;
+ }
+ 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;
+ }
+ Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.IDLE_RECOVER,
+ basDevp,
+ stationThread,
+ stationProtocol,
+ wrkMast,
+ outOrderList,
+ pathLenFactor,
+ "checkStationIdleRecover"
+ ).withCancelSessionBeforeDispatch()
+ .withExecutionLock(RedisKeyType.CHECK_STATION_IDLE_RECOVER_LIMIT_.key + stationProtocol.getTaskNo(), STATION_IDLE_RECOVER_LIMIT_SECONDS)
+ .withResetSegmentCommandsBeforeDispatch()
+ .clearIdleIssuedCommands(idleTrack);
+ executeSharedReroute(context);
+ }
+
+ boolean shouldUseRunBlockDirectReassign(WrkMast wrkMast,
+ Integer stationId,
+ List<Integer> runBlockReassignLocStationList) {
+ return wrkMast != null
+ && Objects.equals(wrkMast.getIoType(), WrkIoType.IN.id)
+ && stationId != null
+ && runBlockReassignLocStationList != null
+ && runBlockReassignLocStationList.contains(stationId);
+ }
+
+ 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);
+ return;
+ }
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.cancelSession(wrkMast.getWrkNo());
+ }
+ String response = wmsOperateUtils.applyReassignTaskLocNo(wrkMast.getWrkNo(), stationProtocol.getStationId());
+ if (Cools.isEmpty(response)) {
+ News.taskError(wrkMast.getWrkNo(), "璇锋眰WMS閲嶆柊鍒嗛厤搴撲綅鎺ュ彛澶辫触锛屾帴鍙f湭鍝嶅簲锛侊紒锛乺esponse锛歿}", response);
+ return;
+ }
+ JSONObject jsonObject = JSON.parseObject(response);
+ if (!jsonObject.getInteger("code").equals(200)) {
+ News.error("璇锋眰WMS鎺ュ彛澶辫触锛侊紒锛乺esponse锛歿}", response);
+ return;
+ }
+
+ StartupDto dto = jsonObject.getObject("data", StartupDto.class);
+ 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;
+ }
+ boolean offered = offerDevpCommandWithDedup(basDevp.getDevpNo(), command, "checkStationRunBlock_direct");
+ if (!offered) {
+ return;
+ }
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.recordDispatch(
+ wrkMast.getWrkNo(),
+ stationProtocol.getStationId(),
+ "checkStationRunBlock_direct",
+ command,
+ false
+ );
+ }
+ }
+
+ 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_STATION_RUN.sts)
+ || Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts);
+ }
+
+ private boolean shouldSkipIdleRecoverForRecentDispatch(Integer taskNo, Integer stationId) {
+ if (taskNo == null || taskNo <= 0 || stationId == null) {
+ return false;
+ }
+ long thresholdMs = STATION_IDLE_RECOVER_SECONDS * 1000L;
+ StationMoveSession session = stationMoveCoordinator == null ? null : stationMoveCoordinator.loadSession(taskNo);
+ if (session != null && session.isActive() && session.getLastIssuedAt() != null) {
+ // 鍒嗘鎵ц杩囩▼涓紝鍒氫笅鍙戜笅涓�娈靛懡浠ゆ椂锛宻ession 鐨� currentStationId/dispatchStationId
+ // 鍙兘杩樻病鏉ュ緱鍙婂拰褰撳墠瑙傚療绔欑偣瀹屽叏瀵归綈锛涘彧瑕佸綋鍓嶇珯鐐逛粛鍦ㄨ繖鏉℃椿鍔ㄨ矾绾块噷锛�
+ // 灏辫鏄庤繖娆� recent dispatch 浠嶇劧鍜屽畠鐩稿叧锛宨dle recover 涓嶅簲鍦� 10 绉掔獥鍙e唴鍐嶆浠嬪叆銆�
+ if (Objects.equals(stationId, session.getCurrentStationId())
+ || Objects.equals(stationId, session.getDispatchStationId())
+ || session.containsStation(stationId)) {
+ long elapsedMs = System.currentTimeMillis() - session.getLastIssuedAt();
+ if (elapsedMs < thresholdMs) {
+ saveStationTaskIdleTrack(new StationTaskIdleTrack(taskNo, stationId, System.currentTimeMillis()));
+ News.info("杈撻�佺珯鐐逛换鍔″垰瀹屾垚鍛戒护涓嬪彂锛屽凡璺宠繃鍋滅暀閲嶇畻銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛岃窛涓婃涓嬪彂={}ms锛宺outeVersion={}",
+ stationId, taskNo, elapsedMs, session.getRouteVersion());
+ return true;
+ }
+ }
+ }
+ if (!hasRecentIssuedMoveCommand(taskNo, stationId, thresholdMs)) {
+ return false;
+ }
+ saveStationTaskIdleTrack(new StationTaskIdleTrack(taskNo, stationId, System.currentTimeMillis()));
+ News.info("杈撻�佺珯鐐逛换鍔″垰瀹屾垚鍛戒护涓嬪彂锛屽凡璺宠繃鍋滅暀閲嶇畻銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛岃窛鏈�杩戝懡浠や笅鍙�<{}ms锛宺outeVersion={}",
+ stationId, taskNo, thresholdMs, session == null ? null : session.getRouteVersion());
+ return true;
+ }
+
+ private boolean hasRecentIssuedMoveCommand(Integer taskNo, Integer stationId, long thresholdMs) {
+ if (taskNo == null || taskNo <= 0 || stationId == null || thresholdMs <= 0L || basStationOptService == null) {
+ return false;
+ }
+ Date thresholdTime = new Date(System.currentTimeMillis() - thresholdMs);
+ List<BasStationOpt> optList = basStationOptService.list(new QueryWrapper<BasStationOpt>()
+ .select("id")
+ .eq("task_no", taskNo)
+ .eq("station_id", stationId)
+ .eq("mode", String.valueOf(StationCommandType.MOVE))
+ .eq("send", 1)
+ .ge("send_time", thresholdTime)
+ .orderByDesc("send_time")
+ .last("limit 1"));
+ return optList != null && !optList.isEmpty();
+ }
+
+ private void resetSegmentMoveCommandsBeforeReroute(Integer taskNo) {
+ if (redisUtil == null || taskNo == null || taskNo <= 0) {
+ return;
+ }
+ String key = RedisKeyType.DEVICE_STATION_MOVE_RESET.key + taskNo;
+ redisUtil.set(key, "cancel", 3);
+ try {
+ Thread.sleep(STATION_MOVE_RESET_WAIT_MS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (Exception ignore) {
+ }
+ redisUtil.del(key);
+ }
+
+ 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) {
+ if (deviceNo == null || command == null) {
+ return false;
+ }
+ String dedupKey = buildStationCommandDispatchDedupKey(deviceNo, command);
+ if (redisUtil != null) {
+ Object lock = redisUtil.get(dedupKey);
+ if (lock != null) {
+ News.info("杈撻�佺珯鐐瑰懡浠ょ煭鏃堕噸澶嶆淳鍙戯紝宸茶烦杩囥�俿cene={}锛宒eviceNo={}锛宼askNo={}锛宻tationId={}锛宼argetStaNo={}锛宑ommandType={}",
+ scene,
+ deviceNo,
+ command.getTaskNo(),
+ command.getStationId(),
+ command.getTargetStaNo(),
+ command.getCommandType());
+ return false;
+ }
+ redisUtil.set(dedupKey, "lock", STATION_COMMAND_DISPATCH_DEDUP_SECONDS);
+ }
+ boolean offered = MessageQueue.offer(SlaveType.Devp, deviceNo, new Task(2, command));
+ if (!offered && redisUtil != null) {
+ redisUtil.del(dedupKey);
+ }
+ return offered;
+ }
+
+ private String buildStationCommandDispatchDedupKey(Integer deviceNo, StationCommand command) {
+ return RedisKeyType.STATION_COMMAND_DISPATCH_DEDUP_.key
+ + deviceNo + "_"
+ + command.getTaskNo() + "_"
+ + command.getStationId() + "_"
+ + (stationMoveCoordinator == null ? Integer.toHexString(buildFallbackPathSignature(command).hashCode())
+ : stationMoveCoordinator.buildPathSignatureHash(command));
+ }
+
+ private String buildFallbackPathSignature(StationCommand command) {
+ if (command == null) {
+ return "";
+ }
+ return String.valueOf(command.getCommandType())
+ + "_" + command.getStationId()
+ + "_" + command.getTargetStaNo()
+ + "_" + command.getNavigatePath()
+ + "_" + command.getLiftTransferPath()
+ + "_" + command.getOriginalNavigatePath();
+ }
+
+ private int clearIssuedMoveCommandsDuringIdleStay(StationTaskIdleTrack idleTrack,
+ Integer taskNo,
+ Integer stationId) {
+ if (basStationOptService == null) {
+ return 0;
+ }
+ List<BasStationOpt> optList;
+ try {
+ optList = listIssuedMoveCommandsDuringIdleStay(idleTrack, taskNo);
+ } catch (Exception e) {
+ return 0;
+ }
+ if (optList == null || optList.isEmpty()) {
+ return 0;
+ }
+
+ Date now = new Date();
+ String cleanupMemo = buildIdleRecoverClearedMemo(stationId);
+ int clearedCount = 0;
+ for (BasStationOpt opt : optList) {
+ if (opt == null || opt.getId() == null) {
+ continue;
+ }
+ opt.setSend(0);
+ opt.setUpdateTime(now);
+ opt.setMemo(appendCleanupMemo(opt.getMemo(), cleanupMemo));
+ clearedCount++;
+ }
+ if (clearedCount > 0) {
+ basStationOptService.updateBatchById(optList);
+ }
+ return clearedCount;
+ }
+
+ private List<BasStationOpt> listIssuedMoveCommandsDuringIdleStay(StationTaskIdleTrack idleTrack,
+ Integer taskNo) {
+ if (idleTrack == null || taskNo == null || taskNo <= 0 || idleTrack.firstSeenTime == null || basStationOptService == null) {
+ return Collections.emptyList();
+ }
+ List<BasStationOpt> optList = basStationOptService.list(new QueryWrapper<BasStationOpt>()
+ .select("id", "task_no", "send_time", "target_station_id", "memo", "send")
+ .eq("task_no", taskNo)
+ .eq("mode", String.valueOf(StationCommandType.MOVE))
+ .eq("send", 1)
+ .ge("send_time", new Date(idleTrack.firstSeenTime))
+ .orderByAsc("send_time"));
+ if (optList == null || optList.isEmpty()) {
+ return Collections.emptyList();
+ }
+ return optList;
+ }
+
+ private String buildIdleRecoverClearedMemo(Integer stationId) {
+ if (stationId == null) {
+ return IDLE_RECOVER_CLEARED_MEMO;
+ }
+ return IDLE_RECOVER_CLEARED_MEMO + "(stationId=" + stationId + ")";
+ }
+
+ private String appendCleanupMemo(String memo, String cleanupMemo) {
+ if (Cools.isEmpty(cleanupMemo)) {
+ return memo;
+ }
+ if (Cools.isEmpty(memo)) {
+ return cleanupMemo;
+ }
+ if (memo.contains(cleanupMemo)) {
+ return memo;
+ }
+ return memo + " | " + cleanupMemo;
+ }
+
+ 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
+ );
+ }
+
+ /**
+ * 娌库�滄簮绔� -> 鐩爣绔欌�濈殑鐞嗚璺緞锛屼粠褰撳墠绔欑偣寰�涓嬫父鍥炵湅锛屾壘鍑哄悓鎵规浠诲姟鍦ㄥ悗缁珯鐐逛笂鐨勫凡鐭ュ簭鍙枫��
+ * <p>
+ * 涓ユ牸绐楀彛鎺у埗瑕佸洖绛旂殑闂涓嶆槸鈥滃綋鍓嶄换鍔¤嚜宸辩殑 batchSeq 鏄灏戔�濓紝鑰屾槸锛�
+ * 鈥滃湪褰撳墠绔欑偣鍚庨潰锛屾部杩欐潯鍑哄簱閾捐矾宸茬粡鎺掔潃鐨勫悓鎵规浠诲姟锛屾渶闈犺繎鐩爣绔殑搴忓彿鏄灏戯紵鈥�
+ * 鍙湁鎷垮埌杩欎釜搴忓彿锛屾帓搴忕偣鎵嶈兘鍒ゆ柇褰撳墠浠诲姟鏄惁搴旇绱ц窡鍏跺悗鏀捐銆�
+ * <p>
+ * 鍏蜂綋鍋氭硶锛�
+ * <p>
+ * 1. 鎸� pathList 浠庡熬鍒板ご鍥炴壂锛屾埅鍑� searchStationId 涔嬪悗鐨勫叏閮ㄤ笅娓哥珯鐐癸紱
+ * 2. 璇诲彇杩欎簺绔欑偣褰撳墠姝e湪鎵ц鐨勪换鍔″彿锛�
+ * 3. 濡傛灉鏌愮珯鐐逛笂鐨勪换鍔″睘浜� searchBatch锛屽氨璁板綍瀹冪殑 batchSeq锛�
+ * 4. 杩斿洖璇ユ壒娆″湪涓嬫父宸茬煡鐨勫簭鍙枫��
+ * <p>
+ * 杩欓噷杩斿洖鐨勬槸鈥滆矾寰勪笅娓哥幇鍦哄凡鍑虹幇鐨勬壒娆″簭鍙封�濓紝涓嶆槸鎵规鐞嗚鏈�灏�/鏈�澶у�笺��
+ * 濡傛灉涓嬫父娌℃湁鍚屾壒娆′换鍔★紝杩斿洖 null锛岃皟鐢ㄦ柟闇�瑕侀��鍥炲埌鈥滃綋鍓嶆壒娆¢涓湭瀹屾垚浠诲姟鏄惁灏辨槸鑷繁鈥濈殑鍒ゅ畾銆�
+ *
+ * @param pathList 浠庝换鍔℃簮绔欏埌鐩爣绔欑殑鐞嗚鍑哄簱璺緞
+ * @param searchStationId 褰撳墠姝e湪鍋氭帓搴忓喅绛栫殑绔欑偣
+ * @param searchBatch 褰撳墠浠诲姟鎵�灞炴壒娆�
+ * @return 涓嬫父鍚屾壒娆′换鍔$殑宸茬煡搴忓彿锛涜嫢璺緞涓嬫父灏氭湭鍑虹幇璇ユ壒娆″垯杩斿洖 null
+ */
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);
@@ -822,6 +2140,7 @@
}
}
+ // 涓嬫父绔欑偣鍙兘鍚屾椂鎸傜潃澶氫釜涓嶅悓鎵规浠诲姟锛涜繖閲屽彧鎶藉彇涓庡綋鍓嶆壒娆$浉鍏崇殑鐜板満搴忓彿蹇収銆�
HashMap<String, Integer> batchMap = new HashMap<>();
for (Integer station : checkList) {
BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", station));
@@ -932,6 +2251,15 @@
Integer sourceStationId,
Integer targetStationId,
LoadGuardState loadGuardState) {
+ return findPathLoopHit(config, sourceStationId, targetStationId, loadGuardState, null, null);
+ }
+
+ private LoopHitResult findPathLoopHit(DispatchLimitConfig config,
+ Integer sourceStationId,
+ Integer targetStationId,
+ LoadGuardState loadGuardState,
+ WrkMast wrkMast,
+ Double pathLenFactor) {
if (!config.loopModeEnable) {
return LoopHitResult.NO_HIT;
}
@@ -943,7 +2271,9 @@
}
try {
- List<NavigateNode> nodes = navigateUtils.calcByStationId(sourceStationId, targetStationId);
+ List<NavigateNode> nodes = wrkMast == null
+ ? navigateUtils.calcByStationId(sourceStationId, targetStationId)
+ : calcOutboundNavigatePath(wrkMast, sourceStationId, targetStationId, pathLenFactor);
if (nodes == null || nodes.isEmpty()) {
return LoopHitResult.NO_HIT;
}
@@ -987,6 +2317,483 @@
return value;
}
+ private Double normalizePathLenFactor(Double pathLenFactor) {
+ if (pathLenFactor == null || pathLenFactor < 0.0d) {
+ return 0.0d;
+ }
+ if (pathLenFactor > 1.0d) {
+ return 1.0d;
+ }
+ return pathLenFactor;
+ }
+
+ enum RerouteSceneType {
+ RUN_BLOCK_REROUTE,
+ IDLE_RECOVER,
+ OUT_ORDER,
+ WATCH_CIRCLE
+ }
+
+ static final class RerouteDecision {
+ private final boolean skip;
+ private final String skipReason;
+ private final Integer targetStationId;
+ private final OutOrderDispatchDecision dispatchDecision;
+
+ private RerouteDecision(boolean skip,
+ String skipReason,
+ Integer targetStationId,
+ OutOrderDispatchDecision dispatchDecision) {
+ this.skip = skip;
+ this.skipReason = skipReason;
+ this.targetStationId = targetStationId;
+ this.dispatchDecision = dispatchDecision;
+ }
+
+ static RerouteDecision skip(String reason) {
+ return new RerouteDecision(true, reason, null, null);
+ }
+
+ static RerouteDecision proceed(Integer targetStationId) {
+ return new RerouteDecision(false, null, targetStationId, null);
+ }
+
+ static RerouteDecision proceed(Integer targetStationId,
+ OutOrderDispatchDecision dispatchDecision) {
+ return new RerouteDecision(false, null, targetStationId, dispatchDecision);
+ }
+
+ boolean skip() {
+ return skip;
+ }
+
+ String skipReason() {
+ return skipReason;
+ }
+
+ Integer targetStationId() {
+ return targetStationId;
+ }
+
+ OutOrderDispatchDecision dispatchDecision() {
+ return dispatchDecision;
+ }
+ }
+
+ /**
+ * 閲嶇畻/閲嶅彂閾捐矾鐨勪竴娆℃�ф墽琛屼笂涓嬫枃銆�
+ *
+ * <p>杩欎釜瀵硅薄鍙湪涓�娆� reroute 鎵ц杩囩▼涓瓨鍦紝涓嶄細钀藉簱锛屼篃涓嶄細闀挎湡缂撳瓨銆�
+ * 瀹冪殑鑱岃矗涓嶆槸琛ㄨ揪涓氬姟鐘舵�侊紝鑰屾槸鎶娾�滆繖娆′负浠�涔堣繘鏉ャ�佸綋鍓嶇敤鍝杩愯鏃跺璞°��
+ * 涓嬪彂鍓嶅悗瑕佷笉瑕佸惎鐢ㄩ澶栨帶鍒堕�昏緫鈥濋泦涓墦鍖咃紝渚涚粺涓�鎵ц閾捐矾浣跨敤銆�
+ *
+ * <p>缁熶竴鎵ц閾捐矾澶ц嚧鍒嗕笁娈碉細
+ * 1. {@code resolveSharedRerouteDecision} 鏍规嵁褰撳墠浠诲姟鍜屽満鏅厛鍐崇瓥鐩爣绔欍��
+ * 2. {@code buildRerouteCommandPlan} 鍐冲畾鐢ㄦ櫘閫氬嚭搴撶畻璺繕鏄� run-block 涓撶敤绠楄矾銆�
+ * 3. {@code executeReroutePlan} 鎸変笂涓嬫枃閲岀殑寮�鍏冲喅瀹氭槸鍚﹀仛 suppress銆佹槸鍚﹀姞閿併��
+ * 鏄惁鍏� cancel 鏃� session銆佹槸鍚﹀厛娓呮棫鍒嗘鍛戒护锛岀劧鍚庣湡姝d笅鍙戙��
+ *
+ * <p>瀛楁鍙互鍒嗘垚鍥涚粍鐞嗚В锛�
+ * 1. 鍦烘櫙涓庤繍琛屾椂瀵硅薄锛�
+ * {@code sceneType} / {@code basDevp} / {@code stationThread} /
+ * {@code stationProtocol} / {@code wrkMast}
+ * 琛ㄧず鈥滆皝鍦ㄤ粈涔堝満鏅笅瑙﹀彂浜嗚繖娆¢噸绠椻�濄��
+ * 2. 鐩爣鍐崇瓥杈撳叆锛�
+ * {@code outOrderStationIds} / {@code pathLenFactor}
+ * 琛ㄧず鈥滅洰鏍囩珯濡備綍绠椼�佽矾寰勫�惧悜绯绘暟鏄灏戔�濄��
+ * 3. 涓嬪彂鐩爣淇℃伅锛�
+ * {@code dispatchScene} / {@code dispatchDeviceNo}
+ * 琛ㄧず鈥滆繖娆″懡浠ゆ渶缁堝線鍝釜杈撻�佽澶囬槦鍒楀彂锛屼互鍙婃棩蹇�/鍘婚噸鍦烘櫙鍚嶆槸浠�涔堚�濄��
+ * 4. 鎵ц鎺у埗寮�鍏筹細
+ * {@code useRunBlockCommand} / {@code checkSuppressDispatch} /
+ * {@code requireOutOrderDispatchLock} / {@code cancelSessionBeforeDispatch} /
+ * {@code resetSegmentCommandsBeforeDispatch} / {@code clearIdleIssuedCommands} /
+ * {@code checkRecentDispatch} / {@code executionLockKey} / {@code executionLockSeconds}
+ * 琛ㄧず鈥滅湡姝f墽琛屽墠鍚庤鎵撳紑鍝簺淇濇姢鍔ㄤ綔鈥濄��
+ *
+ * <p>瀹冩湰璐ㄤ笂鏄竴涓弬鏁板璞″姞甯冨皵寮�鍏抽泦鍚堬細
+ * {@code create(...)} 鍏堟妸杩欐 reroute 鐨勫熀纭�鐜板満濉繘鏉ワ紝
+ * 鍐嶉�氳繃 {@code withXxx(...)} 閫愰」澹版槑杩欐鎵ц闇�瑕侀檮鍔犲摢浜涙帶鍒惰涔夈��
+ *
+ * <p>渚嬪锛�
+ * RUN_BLOCK_REROUTE 浼氭墦寮� {@code useRunBlockCommand}锛�
+ * 骞惰姹傚湪涓嬪彂鍓嶅厛 {@code cancelSession}銆佸厛娓呮棫鍒嗘鍛戒护锛�
+ * OUT_ORDER 浼氭墦寮� suppress guard 鍜� out-order lock锛�
+ * IDLE_RECOVER 鍒欎細鎵撳紑 recent dispatch guard锛屽苟璁板綍/娓呯悊鍋滅暀鏈熼棿宸蹭笅鍙戝懡浠ゃ��
+ */
+ static final class RerouteContext {
+ // 鏈 reroute 鐨勮Е鍙戞潵婧愩�傚喅瀹氬悗闈㈣蛋鍝被鐩爣瑁佸喅銆佹棩蹇楁枃妗堝拰鐗规畩淇濇姢鍒嗘敮銆�
+ private final RerouteSceneType sceneType;
+ // 褰撳墠杈撻�佽澶囬厤缃紝涓昏鐢ㄤ簬鎷块粯璁や笅鍙戣澶囧彿鍜岀浉鍏崇珯鐐归厤缃��
+ private final BasDevp basDevp;
+ // 褰撳墠绔欑偣绾跨▼锛屽悗闈㈡瀯閫犺緭閫佸懡浠ゆ椂鐩存帴渚濊禆瀹冨彇 command銆�
+ private final StationThread stationThread;
+ // 瑙﹀彂杩欐 reroute 鐨勭幇鍦虹珯鐐圭姸鎬佸揩鐓э紝鍖呭惈 stationId/taskNo/runBlock/targetStaNo 绛夎繍琛屾椂淇℃伅銆�
+ private final StationProtocol stationProtocol;
+ // 褰撳墠宸ヤ綔涓昏〃璁板綍锛岃〃绀鸿繖娆¢噸绠楀搴旂殑浠诲姟涓荤姸鎬併��
+ private final WrkMast wrkMast;
+ // 褰撳墠璁惧鐨勫嚭搴撴帓搴忕偣鍒楄〃銆傜洰鏍囩珯鍐崇瓥鏃堕渶瑕佺煡閬撳摢浜涚珯鐐瑰睘浜� out-order 鑺傜偣銆�
+ private final List<Integer> outOrderStationIds;
+ // 璺緞闀垮害鍊惧悜绯绘暟銆傛壒娆″嚭搴撴椂鐢ㄤ簬璁╀笉鍚屼换鍔″璺緞閫夋嫨鏈夌ǔ瀹氬亸濂姐��
+ private final Double pathLenFactor;
+ // 杩欐娲惧彂鍦ㄦ棩蹇椼�佸幓閲嶃�乻ession 璁板綍涓娇鐢ㄧ殑鍦烘櫙鍚嶏紝渚嬪 checkStationRunBlock_reroute銆�
+ private final String dispatchScene;
+ // 瀹為檯鎶曢�掑懡浠ょ殑璁惧鍙枫�傞粯璁ゅ彇 basDevp.getDevpNo()锛屾煇浜涘満鏅彲鏄惧紡瑕嗙洊銆�
+ private Integer dispatchDeviceNo;
+ // true 琛ㄧず鍛戒护鏋勯�犻樁娈垫敼璧� stationThread.getRunBlockRerouteCommand锛岃�屼笉鏄櫘閫氬嚭搴撶畻璺��
+ private boolean useRunBlockCommand;
+ // true 琛ㄧず鎵ц鍓嶈鍏堝仛 active-session suppress锛岄伩鍏嶆棫娲诲姩璺嚎琚噸澶嶆彃鍏ユ柊鍛戒护銆�
+ private boolean checkSuppressDispatch;
+ // true 琛ㄧず鎵ц鍓嶈鍔� out-order 涓撶敤鐭攣锛岄槻姝㈠悓涓�鎺掑簭鐐圭煭鏃堕棿閲嶅璁$畻/涓嬪彂銆�
+ private boolean requireOutOrderDispatchLock;
+ // true 琛ㄧず鐪熸涓嬪彂鍓嶅厛鍙栨秷鏃� session銆傞�氬父鐢ㄤ簬 reroute 鏇挎崲鏃ц矾绾裤��
+ private boolean cancelSessionBeforeDispatch;
+ // true 琛ㄧず鐪熸涓嬪彂鍓嶅厛娓呯悊鏃у垎娈佃緭閫佸懡浠わ紝閬垮厤 segment executor 杩樻寔鏈夋棫璺嚎銆�
+ private boolean resetSegmentCommandsBeforeDispatch;
+ // true 琛ㄧず瑕佹竻鐞� idle recover 鏈熼棿宸茬粡涓嬪彂杩囦絾鏈疄闄呯敓鏁堢殑鏃у懡浠ょ棔杩广��
+ private boolean clearIdleIssuedCommands;
+ // true 琛ㄧず鎵ц鍓嶈妫�鏌モ�滄渶杩戝垰涓嬪彂杩団�濓紝鐢ㄤ簬 idle recover 閬垮厤鍒氬彂瀹屽氨閲嶇畻銆�
+ private boolean checkRecentDispatch;
+ // 鍙�夋墽琛岄攣 key銆傜敤浜庣粰鏌愪釜 reroute 鍦烘櫙鍔犵煭鏃堕棿浜掓枼銆�
+ private String executionLockKey;
+ // executionLockKey 瀵瑰簲鐨勯攣绉掓暟銆�
+ private int executionLockSeconds;
+ // 浠� idle recover 闇�瑕侊紝璁板綍鍋滅暀璺熻釜涓婁笅鏂囷紝渚涙竻鐞嗘棫鍛戒护涓庢洿鏂版椂闂翠娇鐢ㄣ��
+ private StationTaskIdleTrack idleTrack;
+
+ private RerouteContext(RerouteSceneType sceneType,
+ BasDevp basDevp,
+ StationThread stationThread,
+ StationProtocol stationProtocol,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor,
+ String dispatchScene) {
+ this.sceneType = sceneType;
+ this.basDevp = basDevp;
+ this.stationThread = stationThread;
+ this.stationProtocol = stationProtocol;
+ this.wrkMast = wrkMast;
+ this.outOrderStationIds = outOrderStationIds == null ? Collections.emptyList() : outOrderStationIds;
+ this.pathLenFactor = pathLenFactor;
+ this.dispatchScene = dispatchScene;
+ this.dispatchDeviceNo = basDevp == null ? null : basDevp.getDevpNo();
+ }
+
+ static RerouteContext create(RerouteSceneType sceneType,
+ BasDevp basDevp,
+ StationThread stationThread,
+ StationProtocol stationProtocol,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor,
+ String dispatchScene) {
+ // create 鍙礋璐e~鍩虹鐜板満锛屼笉榛樿鎵撳紑浠讳綍鎵ц寮�鍏炽��
+ // 姣忎釜鍦烘櫙鍚庨潰閫氳繃 withXxx 鏄庣‘澹版槑鑷繁闇�瑕佸摢浜涢檮鍔犳帶鍒躲��
+ return new RerouteContext(sceneType, basDevp, stationThread, stationProtocol, wrkMast, outOrderStationIds, pathLenFactor, dispatchScene);
+ }
+
+ RerouteContext withDispatchDeviceNo(Integer dispatchDeviceNo) {
+ // 瑕嗙洊榛樿涓嬪彂璁惧鍙枫�傚吀鍨嬪満鏅槸 out-order 绔欑偣閰嶇疆鐨� deviceNo 涓� basDevp 榛樿鍊间笉鍚屻��
+ this.dispatchDeviceNo = dispatchDeviceNo;
+ return this;
+ }
+
+ RerouteContext withRunBlockCommand() {
+ // 鍛戒护鏋勯�犻樁娈靛垏鎹㈠埌 run-block 涓撶敤绠楄矾鍣ㄣ��
+ // 鐩爣绔欎粛鐢辩粺涓�鍐崇瓥閫昏緫鍐冲畾锛屽彧鏄�滃幓鐩爣绔欑殑璺緞鈥濇敼涓哄牭濉為噸瑙勫垝绠楁硶鐢熸垚銆�
+ this.useRunBlockCommand = true;
+ return this;
+ }
+
+ RerouteContext withSuppressDispatchGuard() {
+ // 鎵ц鍓嶅惎鐢� session suppress锛�
+ // 濡傛灉褰撳墠 task 鍦ㄥ綋鍓嶄綅缃凡缁忔湁涓�鏉℃椿鍔ㄤ腑鐨勫悓璺緞/鍚岃鐩栬寖鍥磋矾绾匡紝鍒欐湰娆′笉鍐嶉噸澶嶆淳鍙戙��
+ this.checkSuppressDispatch = true;
+ return this;
+ }
+
+ RerouteContext withOutOrderDispatchLock() {
+ // 鎵ц鍓嶅惎鐢ㄦ帓搴忕偣鐭攣銆�
+ // 涓昏闃叉鍚屼竴涓� out-order/watch-circle 瑙﹀彂鐐瑰湪鏋佺煭鏃堕棿鍐呰骞跺彂閲嶅閲嶇畻銆�
+ this.requireOutOrderDispatchLock = true;
+ return this;
+ }
+
+ RerouteContext withCancelSessionBeforeDispatch() {
+ // 鎵ц鍓嶆樉寮忓彇娑堟棫 session銆�
+ // 璇箟鏄�滄湰娆″懡浠ゅ噯澶囨浛鎹㈡棫璺嚎鈥濓紝鏃� routeVersion 涔嬪悗涓嶅簲鍐嶇户缁帹杩涖��
+ this.cancelSessionBeforeDispatch = true;
+ return this;
+ }
+
+ RerouteContext withResetSegmentCommandsBeforeDispatch() {
+ // 鎵ц鍓嶆竻鎺� segment executor 渚ф棫鍒嗘鍛戒护銆�
+ // 杩欏 run-block/idle recover 寰堝叧閿紝鍚﹀垯绯荤粺鍙兘杩樻嬁鐫�鏃� segment 鐘舵�侀樆鏂柊璺嚎銆�
+ this.resetSegmentCommandsBeforeDispatch = true;
+ return this;
+ }
+
+ RerouteContext clearIdleIssuedCommands(StationTaskIdleTrack idleTrack) {
+ // 浠� idle recover 浣跨敤锛�
+ // 琛ㄧず閲嶅惎鍓嶈鎶娾�滃仠鐣欐湡闂村凡缁忓彂杩囦絾鍙兘鏈湡姝f墽琛岀殑鍛戒护鐥曡抗鈥濇竻鐞嗘帀銆�
+ this.clearIdleIssuedCommands = true;
+ this.idleTrack = idleTrack;
+ return this;
+ }
+
+ RerouteContext withRecentDispatchGuard() {
+ // 鎵ц鍓嶆鏌ユ渶杩戞槸鍚﹀垰涓嬪彂杩囥��
+ // 閬垮厤 idle recover 鍦ㄢ�滃垰閲嶅彂瀹屸�濈殑绐楀彛鍐呭張椹笂瑙﹀彂涓�娆°��
+ this.checkRecentDispatch = true;
+ return this;
+ }
+
+ RerouteContext withExecutionLock(String executionLockKey, int executionLockSeconds) {
+ // 涓烘煇涓満鏅寕涓�涓嫭绔嬫墽琛岄攣銆�
+ // 鍜� out-order lock 涓嶅悓锛岃繖閲屾槸娉涘寲閿侊紝璋佷紶 key 璋佽礋璐e畾涔夐攣璇箟銆�
+ this.executionLockKey = executionLockKey;
+ this.executionLockSeconds = executionLockSeconds;
+ return this;
+ }
+
+ RerouteSceneType sceneType() {
+ return sceneType;
+ }
+
+ BasDevp basDevp() {
+ return basDevp;
+ }
+
+ StationThread stationThread() {
+ return stationThread;
+ }
+
+ StationProtocol stationProtocol() {
+ return stationProtocol;
+ }
+
+ WrkMast wrkMast() {
+ return wrkMast;
+ }
+
+ List<Integer> outOrderStationIds() {
+ return outOrderStationIds;
+ }
+
+ Double pathLenFactor() {
+ return pathLenFactor;
+ }
+
+ String dispatchScene() {
+ return dispatchScene;
+ }
+
+ Integer dispatchDeviceNo() {
+ return dispatchDeviceNo;
+ }
+
+ boolean useRunBlockCommand() {
+ return useRunBlockCommand;
+ }
+
+ boolean checkSuppressDispatch() {
+ return checkSuppressDispatch;
+ }
+
+ boolean requireOutOrderDispatchLock() {
+ return requireOutOrderDispatchLock;
+ }
+
+ boolean cancelSessionBeforeDispatch() {
+ return cancelSessionBeforeDispatch;
+ }
+
+ boolean resetSegmentCommandsBeforeDispatch() {
+ return resetSegmentCommandsBeforeDispatch;
+ }
+
+ boolean clearIdleIssuedCommands() {
+ return clearIdleIssuedCommands;
+ }
+
+ boolean checkRecentDispatch() {
+ return checkRecentDispatch;
+ }
+
+ String executionLockKey() {
+ return executionLockKey;
+ }
+
+ int executionLockSeconds() {
+ return executionLockSeconds;
+ }
+
+ StationTaskIdleTrack idleTrack() {
+ return idleTrack;
+ }
+ }
+
+ static final class RerouteCommandPlan {
+ private final boolean skip;
+ private final String skipReason;
+ private final StationCommand command;
+ private final RerouteDecision decision;
+ private final String dispatchScene;
+
+ private RerouteCommandPlan(boolean skip,
+ String skipReason,
+ StationCommand command,
+ RerouteDecision decision,
+ String dispatchScene) {
+ this.skip = skip;
+ this.skipReason = skipReason;
+ this.command = command;
+ this.decision = decision;
+ this.dispatchScene = dispatchScene;
+ }
+
+ static RerouteCommandPlan skip(String reason) {
+ return new RerouteCommandPlan(true, reason, null, null, null);
+ }
+
+ static RerouteCommandPlan dispatch(StationCommand command,
+ RerouteDecision decision,
+ String dispatchScene) {
+ return new RerouteCommandPlan(false, null, command, decision, dispatchScene);
+ }
+
+ boolean skip() {
+ return skip;
+ }
+
+ String skipReason() {
+ return skipReason;
+ }
+
+ StationCommand command() {
+ return command;
+ }
+
+ RerouteDecision decision() {
+ return decision;
+ }
+
+ String dispatchScene() {
+ return dispatchScene;
+ }
+ }
+
+ static final class RerouteExecutionResult {
+ private final boolean skipped;
+ private final String skipReason;
+ private final boolean dispatched;
+ private final StationCommand command;
+ private final int clearedCommandCount;
+
+ private RerouteExecutionResult(boolean skipped,
+ String skipReason,
+ boolean dispatched,
+ StationCommand command,
+ int clearedCommandCount) {
+ this.skipped = skipped;
+ this.skipReason = skipReason;
+ this.dispatched = dispatched;
+ this.command = command;
+ this.clearedCommandCount = clearedCommandCount;
+ }
+
+ static RerouteExecutionResult skip(String reason) {
+ return new RerouteExecutionResult(true, reason, false, null, 0);
+ }
+
+ static RerouteExecutionResult dispatched(StationCommand command,
+ int clearedCommandCount) {
+ return new RerouteExecutionResult(false, null, true, command, clearedCommandCount);
+ }
+
+ boolean skipped() {
+ return skipped;
+ }
+
+ String skipReason() {
+ return skipReason;
+ }
+
+ boolean dispatched() {
+ return dispatched;
+ }
+
+ StationCommand command() {
+ return command;
+ }
+
+ int clearedCommandCount() {
+ return clearedCommandCount;
+ }
+ }
+
+ private static class OutOrderDispatchDecision {
+ private final Integer targetStationId;
+ private final boolean circle;
+ private final StationTaskLoopService.LoopEvaluation loopEvaluation;
+ private final boolean countLoopIssue;
+
+ private OutOrderDispatchDecision(Integer targetStationId, boolean circle) {
+ this(targetStationId, circle, null, false);
+ }
+
+ private OutOrderDispatchDecision(Integer targetStationId,
+ boolean circle,
+ StationTaskLoopService.LoopEvaluation loopEvaluation,
+ boolean countLoopIssue) {
+ this.targetStationId = targetStationId;
+ this.circle = circle;
+ this.loopEvaluation = loopEvaluation;
+ this.countLoopIssue = countLoopIssue;
+ }
+
+ private Integer getTargetStationId() {
+ return targetStationId;
+ }
+
+ private boolean isCircle() {
+ return circle;
+ }
+
+ private StationTaskLoopService.LoopEvaluation getLoopEvaluation() {
+ return loopEvaluation;
+ }
+
+ private boolean shouldCountLoopIssue() {
+ return countLoopIssue;
+ }
+ }
+
+ private static class CircleTargetCandidate {
+ private final Integer stationId;
+ private final Integer pathLength;
+ private final Integer offset;
+
+ private CircleTargetCandidate(Integer stationId, Integer pathLength, Integer offset) {
+ this.stationId = stationId;
+ this.pathLength = pathLength == null ? 0 : pathLength;
+ this.offset = offset == null ? 0 : offset;
+ }
+
+ private Integer getStationId() {
+ return stationId;
+ }
+
+ private Integer getPathLength() {
+ return pathLength;
+ }
+
+ private Integer getOffset() {
+ return offset;
+ }
+ }
+
private void saveLoopLoadReserve(Integer wrkNo, LoopHitResult loopHitResult) {
if (wrkNo == null || wrkNo <= 0 || loopHitResult == null || !loopHitResult.isThroughLoop()) {
return;
@@ -1000,23 +2807,31 @@
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;
}
@@ -1157,4 +2972,49 @@
}
}
+ 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;
+ }
+ }
+
}
--
Gitblit v1.9.1