From 90402710d962aa357062ecb94649e0f277c1dfb3 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期日, 29 三月 2026 21:03:48 +0800
Subject: [PATCH] #
---
src/test/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupportTest.java | 139
src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java | 615 +++
src/main/java/com/zy/core/utils/station/model/RerouteExecutionResult.java | 52
src/test/java/com/zy/asrs/controller/StationControllerTest.java | 48
src/test/java/com/zy/core/utils/station/StationOutboundDispatchProcessorTest.java | 202 +
src/main/java/com/zy/core/utils/station/model/DispatchLimitConfig.java | 11
src/main/java/com/zy/core/thread/impl/ZyStationV5Thread.java | 971 -----
src/main/java/com/zy/core/plugin/XiaosongProcess.java | 9
src/main/java/com/zy/core/utils/station/StationDispatchLoadSupport.java | 300 +
src/test/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlannerTest.java | 110
src/test/java/com/zy/core/utils/station/model/StationModelTypePlacementTest.java | 49
src/test/java/com/zy/core/utils/StationRerouteProcessorTest.java | 133
src/main/java/com/zy/core/utils/station/model/LoadGuardState.java | 52
src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java | 127
src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java | 249 +
src/main/java/com/zy/core/utils/station/model/RerouteDecision.java | 47
src/main/java/com/zy/core/plugin/FakeProcess.java | 19
src/test/java/com/zy/core/thread/impl/ZyStationV5ThreadTest.java | 34
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java | 2998 -------------------
src/main/java/com/zy/core/utils/station/model/CircleTargetCandidate.java | 25
src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java | 734 ++++
src/main/java/com/zy/core/cache/MessageQueue.java | 88
src/main/java/com/zy/core/utils/station/model/OutOrderDispatchDecision.java | 46
src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java | 5
src/main/java/com/zy/core/dispatch/StationCommandDispatcher.java | 121
src/main/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupport.java | 231 +
/dev/null | 96
src/main/java/com/zy/asrs/controller/StationController.java | 19
src/main/java/com/zy/core/plugin/NormalProcess.java | 7
src/test/java/com/zy/core/dispatch/StationCommandDispatcherTest.java | 100
src/main/java/com/zy/core/utils/station/model/RerouteSceneType.java | 8
src/main/java/com/zy/core/dispatch/StationCommandDispatchResult.java | 27
src/test/java/com/zy/core/utils/station/StationRegularDispatchProcessorTest.java | 240 +
src/main/java/com/zy/core/utils/station/StationTaskIdleTrack.java | 47
src/main/java/com/zy/core/utils/station/model/RerouteCommandPlan.java | 53
src/main/java/com/zy/core/utils/station/model/RerouteContext.java | 185 +
src/test/java/com/zy/core/utils/StationOperateProcessUtilsReroutePipelineTest.java | 382 +
src/main/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlanner.java | 375 ++
src/main/java/com/zy/core/utils/station/model/LoopHitResult.java | 20
src/main/java/com/zy/core/plugin/GslProcess.java | 9
src/main/java/com/zy/core/utils/station/StationRegularDispatchProcessor.java | 284 +
41 files changed, 5,078 insertions(+), 4,189 deletions(-)
diff --git a/src/main/java/com/zy/asrs/controller/StationController.java b/src/main/java/com/zy/asrs/controller/StationController.java
index 1d3b63d..f2fe531 100644
--- a/src/main/java/com/zy/asrs/controller/StationController.java
+++ b/src/main/java/com/zy/asrs/controller/StationController.java
@@ -7,6 +7,8 @@
import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.DeviceConfigService;
import com.zy.common.utils.RedisUtil;
+import com.zy.core.dispatch.StationCommandDispatchResult;
+import com.zy.core.dispatch.StationCommandDispatcher;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.StationObjModel;
@@ -22,10 +24,8 @@
import com.core.common.Cools;
import com.core.common.R;
import com.zy.asrs.domain.param.StationCommandMoveParam;
-import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.SlaveType;
-import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
import com.zy.core.thread.StationThread;
@@ -45,6 +45,8 @@
private ConfigService configService;
@Autowired
private DeviceConfigService deviceConfigService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
@PostMapping("/command/move")
public R commandMove(@RequestBody StationCommandMoveParam param) {
@@ -73,7 +75,11 @@
if (command == null) {
return R.error("鐢熸垚杈撻�佸懡浠ゅけ璐ワ紝璺緞涓虹┖鎴栦笉鍙揪");
}
- MessageQueue.offer(SlaveType.Devp, devpNo, new Task(2, command));
+ StationCommandDispatchResult dispatchResult = stationCommandDispatcher
+ .dispatch(devpNo, command, "station-controller", "manual-move");
+ if (!dispatchResult.isAccepted()) {
+ return R.error("杈撻�佸懡浠や笅鍙戝け璐�:" + dispatchResult.getReason());
+ }
return R.ok();
}
@@ -118,7 +124,11 @@
StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9997, stationId, stationId, 0);
command.setBarcode(barcode.trim());
- MessageQueue.offer(SlaveType.Devp, devpNo, new Task(2, command));
+ StationCommandDispatchResult dispatchResult = stationCommandDispatcher
+ .dispatch(devpNo, command, "station-controller", "manual-barcode");
+ if (!dispatchResult.isAccepted()) {
+ return R.error("鏉$爜鍛戒护涓嬪彂澶辫触:" + dispatchResult.getReason());
+ }
return R.ok();
}
@@ -172,5 +182,4 @@
}
return null;
}
-
}
diff --git a/src/main/java/com/zy/core/cache/MessageQueue.java b/src/main/java/com/zy/core/cache/MessageQueue.java
index 48d96cd..668e31e 100644
--- a/src/main/java/com/zy/core/cache/MessageQueue.java
+++ b/src/main/java/com/zy/core/cache/MessageQueue.java
@@ -4,6 +4,7 @@
import com.zy.core.model.Task;
import java.util.Map;
+import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
@@ -65,21 +66,19 @@
* 濡傛灉鍙戠幇闃熷垪宸叉弧鏃犳硶娣诲姞鐨勮瘽锛屼細鐩存帴杩斿洖false銆�
*/
public static boolean offer(SlaveType type, Integer id, Task task) {
+ Queue<Task> queue = resolveQueue(type, id);
+ if (queue == null) {
+ return false;
+ }
switch (type) {
case Crn:
- return CRN_EXCHANGE.get(id).offer(task);
case DualCrn:
- return DUAL_CRN_EXCHANGE.get(id).offer(task);
case Rgv:
- return RGV_EXCHANGE.get(id).offer(task);
case Devp:
- return DEVP_EXCHANGE.get(id).offer(task);
case Barcode:
- return BARCODE_EXCHANGE.get(id).offer(task);
case Led:
- return LED_EXCHANGE.get(id).offer(task);
case Scale:
- return SCALE_EXCHANGE.get(id).offer(task);
+ return queue.offer(task);
default:
return false;
}
@@ -90,21 +89,19 @@
* 鑻ラ槦鍒椾负绌猴紝杩斿洖null銆�
*/
public static Task poll(SlaveType type, Integer id) {
+ Queue<Task> queue = resolveQueue(type, id);
+ if (queue == null) {
+ return null;
+ }
switch (type) {
case Crn:
- return CRN_EXCHANGE.get(id).poll();
case DualCrn:
- return DUAL_CRN_EXCHANGE.get(id).poll();
case Rgv:
- return RGV_EXCHANGE.get(id).poll();
case Devp:
- return DEVP_EXCHANGE.get(id).poll();
case Barcode:
- return BARCODE_EXCHANGE.get(id).poll();
case Led:
- return LED_EXCHANGE.get(id).poll();
case Scale:
- return SCALE_EXCHANGE.get(id).poll();
+ return queue.poll();
default:
return null;
}
@@ -114,52 +111,75 @@
* 鍙栧嚭鍏冪礌锛屽苟涓嶅垹闄�.
*/
public static Task peek(SlaveType type, Integer id) {
+ Queue<Task> queue = resolveQueue(type, id);
+ if (queue == null) {
+ return null;
+ }
switch (type) {
case Crn:
- return CRN_EXCHANGE.get(id).peek();
case DualCrn:
- return DUAL_CRN_EXCHANGE.get(id).peek();
case Rgv:
- return RGV_EXCHANGE.get(id).peek();
case Devp:
- return DEVP_EXCHANGE.get(id).peek();
case Barcode:
- return BARCODE_EXCHANGE.get(id).peek();
case Led:
- return LED_EXCHANGE.get(id).peek();
case Scale:
- return SCALE_EXCHANGE.get(id).peek();
+ return queue.peek();
default:
return null;
}
}
public static void clear(SlaveType type, Integer id){
+ Queue<Task> queue = resolveQueue(type, id);
+ if (queue == null) {
+ return;
+ }
switch (type) {
case Crn:
- CRN_EXCHANGE.get(id).clear();
- break;
case DualCrn:
- DUAL_CRN_EXCHANGE.get(id).clear();
- break;
case Rgv:
- RGV_EXCHANGE.get(id).clear();
- break;
case Devp:
- DEVP_EXCHANGE.get(id).clear();
- break;
case Barcode:
- BARCODE_EXCHANGE.get(id).clear();
- break;
case Led:
- LED_EXCHANGE.get(id).clear();
- break;
case Scale:
- SCALE_EXCHANGE.get(id).clear();
+ queue.clear();
break;
default:
break;
}
}
+ public static boolean hasExchange(SlaveType type, Integer id) {
+ return resolveQueue(type, id) != null;
+ }
+
+ public static int size(SlaveType type, Integer id) {
+ Queue<Task> queue = resolveQueue(type, id);
+ return queue == null ? 0 : queue.size();
+ }
+
+ private static Queue<Task> resolveQueue(SlaveType type, Integer id) {
+ if (type == null || id == null) {
+ return null;
+ }
+ switch (type) {
+ case Crn:
+ return CRN_EXCHANGE.get(id);
+ case DualCrn:
+ return DUAL_CRN_EXCHANGE.get(id);
+ case Rgv:
+ return RGV_EXCHANGE.get(id);
+ case Devp:
+ return DEVP_EXCHANGE.get(id);
+ case Barcode:
+ return BARCODE_EXCHANGE.get(id);
+ case Led:
+ return LED_EXCHANGE.get(id);
+ case Scale:
+ return SCALE_EXCHANGE.get(id);
+ default:
+ return null;
+ }
+ }
+
}
diff --git a/src/main/java/com/zy/core/dispatch/StationCommandDispatchResult.java b/src/main/java/com/zy/core/dispatch/StationCommandDispatchResult.java
new file mode 100644
index 0000000..7dc095e
--- /dev/null
+++ b/src/main/java/com/zy/core/dispatch/StationCommandDispatchResult.java
@@ -0,0 +1,27 @@
+package com.zy.core.dispatch;
+
+import lombok.Data;
+
+@Data
+public class StationCommandDispatchResult {
+
+ private final boolean accepted;
+ private final String reason;
+ private final int queueDepth;
+ private final String source;
+ private final String scene;
+
+ public static StationCommandDispatchResult accepted(String reason,
+ int queueDepth,
+ String source,
+ String scene) {
+ return new StationCommandDispatchResult(true, reason, queueDepth, source, scene);
+ }
+
+ public static StationCommandDispatchResult rejected(String reason,
+ int queueDepth,
+ String source,
+ String scene) {
+ return new StationCommandDispatchResult(false, reason, queueDepth, source, scene);
+ }
+}
diff --git a/src/main/java/com/zy/core/dispatch/StationCommandDispatcher.java b/src/main/java/com/zy/core/dispatch/StationCommandDispatcher.java
new file mode 100644
index 0000000..8255146
--- /dev/null
+++ b/src/main/java/com/zy/core/dispatch/StationCommandDispatcher.java
@@ -0,0 +1,121 @@
+package com.zy.core.dispatch;
+
+import com.alibaba.fastjson.JSON;
+import com.core.common.Cools;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.News;
+import com.zy.core.cache.MessageQueue;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.model.Task;
+import com.zy.core.model.command.StationCommand;
+import com.zy.core.move.StationMoveCoordinator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class StationCommandDispatcher {
+
+ private static final int STATION_COMMAND_DISPATCH_DEDUP_SECONDS = 10;
+
+ @Autowired(required = false)
+ private RedisUtil redisUtil;
+ @Autowired(required = false)
+ private StationMoveCoordinator stationMoveCoordinator;
+
+ public StationCommandDispatcher() {
+ }
+
+ public StationCommandDispatcher(RedisUtil redisUtil, StationMoveCoordinator stationMoveCoordinator) {
+ this.redisUtil = redisUtil;
+ this.stationMoveCoordinator = stationMoveCoordinator;
+ }
+
+ public StationCommandDispatchResult dispatch(Integer deviceNo,
+ StationCommand command,
+ String source,
+ String scene) {
+ String normalizedSource = Cools.isEmpty(source) ? "unknown" : source;
+ String normalizedScene = Cools.isEmpty(scene) ? "default" : scene;
+ if (deviceNo == null || command == null) {
+ return reject("invalid-argument", 0, normalizedSource, normalizedScene, null);
+ }
+ if (!MessageQueue.hasExchange(SlaveType.Devp, deviceNo)) {
+ return reject("queue-not-initialized", 0, normalizedSource, normalizedScene, command);
+ }
+
+ String dedupKey = buildDedupKey(deviceNo, command);
+ if (!Cools.isEmpty(dedupKey) && redisUtil != null && redisUtil.get(dedupKey) != null) {
+ return reject("dedup-suppressed",
+ MessageQueue.size(SlaveType.Devp, deviceNo),
+ normalizedSource,
+ normalizedScene,
+ command);
+ }
+ if (!Cools.isEmpty(dedupKey) && redisUtil != null) {
+ redisUtil.set(dedupKey, "lock", STATION_COMMAND_DISPATCH_DEDUP_SECONDS);
+ }
+
+ boolean offered = MessageQueue.offer(SlaveType.Devp, deviceNo, new Task(2, command));
+ int queueDepth = MessageQueue.size(SlaveType.Devp, deviceNo);
+ if (!offered) {
+ if (!Cools.isEmpty(dedupKey) && redisUtil != null) {
+ redisUtil.del(dedupKey);
+ }
+ return reject("queue-offer-failed", queueDepth, normalizedSource, normalizedScene, command);
+ }
+
+ News.info("杈撻�佺珯鐐瑰懡浠ゅ叆闃熸垚鍔熴�俿ource={}锛宻cene={}锛宒eviceNo={}锛宼askNo={}锛宻tationId={}锛宼argetStaNo={}锛宑ommandType={}锛宷ueueDepth={}",
+ normalizedSource,
+ normalizedScene,
+ deviceNo,
+ command.getTaskNo(),
+ command.getStationId(),
+ command.getTargetStaNo(),
+ command.getCommandType(),
+ queueDepth);
+ return StationCommandDispatchResult.accepted("accepted", queueDepth, normalizedSource, normalizedScene);
+ }
+
+ private StationCommandDispatchResult reject(String reason,
+ int queueDepth,
+ String source,
+ String scene,
+ StationCommand command) {
+ News.warn("杈撻�佺珯鐐瑰懡浠ゅ叆闃熷け璐ャ�俽eason={}锛宻ource={}锛宻cene={}锛宒eviceNo?=N/A锛宼askNo={}锛宻tationId={}锛宼argetStaNo={}锛宑ommandType={}锛宷ueueDepth={}",
+ reason,
+ source,
+ scene,
+ command == null ? null : command.getTaskNo(),
+ command == null ? null : command.getStationId(),
+ command == null ? null : command.getTargetStaNo(),
+ command == null ? null : command.getCommandType(),
+ queueDepth);
+ return StationCommandDispatchResult.rejected(reason, queueDepth, source, scene);
+ }
+
+ private String buildDedupKey(Integer deviceNo, StationCommand command) {
+ if (deviceNo == null || command == null) {
+ return "";
+ }
+ return RedisKeyType.STATION_COMMAND_DISPATCH_DEDUP_.key
+ + deviceNo + "_"
+ + command.getTaskNo() + "_"
+ + command.getStationId() + "_"
+ + buildCommandSignatureHash(command);
+ }
+
+ private String buildCommandSignatureHash(StationCommand command) {
+ if (command == null) {
+ return "";
+ }
+ if (stationMoveCoordinator != null && command.getCommandType() != null
+ && command.getCommandType().name().startsWith("MOVE")) {
+ String pathSignatureHash = stationMoveCoordinator.buildPathSignatureHash(command);
+ if (!Cools.isEmpty(pathSignatureHash)) {
+ return pathSignatureHash;
+ }
+ }
+ return Integer.toHexString(JSON.toJSONString(command).hashCode());
+ }
+}
diff --git a/src/main/java/com/zy/core/plugin/FakeProcess.java b/src/main/java/com/zy/core/plugin/FakeProcess.java
index a136dd6..9f3e374 100644
--- a/src/main/java/com/zy/core/plugin/FakeProcess.java
+++ b/src/main/java/com/zy/core/plugin/FakeProcess.java
@@ -16,6 +16,7 @@
import com.zy.core.News;
import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
+import com.zy.core.dispatch.StationCommandDispatcher;
import com.zy.core.enums.*;
import com.zy.core.model.StationObjModel;
import com.zy.core.model.Task;
@@ -93,6 +94,8 @@
private DualCrnOperateProcessUtils dualCrnOperateProcessUtils;
@Autowired
private StoreInTaskGenerationService storeInTaskGenerationService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
/**
* 甯﹁秴鏃朵繚鎶ゆ墽琛屾柟娉�
@@ -254,7 +257,7 @@
StationCommand command = stationThread.getCommand(StationCommandType.MOVE,
commonService.getWorkNo(WrkIoType.FAKE_TASK_NO.id), stationId,
entity.getBarcodeStation().getStationId(), 0);
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-enable-in");
redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 5);
}
}
@@ -349,7 +352,7 @@
News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
continue;
}
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "fake-process", "fake-in-task");
redisUtil.set(RedisKeyType.GENERATE_FAKE_IN_TASK_LIMIT.key + stationId, "lock", 5);
}
}
@@ -459,7 +462,7 @@
News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
return;
}
- MessageQueue.offer(SlaveType.Devp, context.getBasDevp().getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(context.getBasDevp().getDevpNo(), command, "fake-process", "write-info");
}
// 璁$畻鎵�鏈夌珯鐐瑰仠鐣欐椂闂�
@@ -521,7 +524,7 @@
continue;
}
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "out-station-reset");
redisUtil.set(
RedisKeyType.CHECK_OUT_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(),
"lock", 10);
@@ -584,7 +587,7 @@
WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
if (wrkMast == null) {
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-task-over");
redisUtil.set(
RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key + stationObjModel.getStationId(),
"lock", 10);
@@ -605,7 +608,7 @@
continue;
}
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-crn-fetch");
redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key
+ stationObjModel.getStationId(), "lock", 10);
News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(crn_fetch)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(),
@@ -632,7 +635,7 @@
continue;
}
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "in-station-reset-dual-crn-fetch");
redisUtil.set(RedisKeyType.CHECK_IN_STATION_STAY_TIME_OUT_LIMIT.key
+ stationObjModel.getStationId(), "lock", 10);
News.info("杈撻�佺珯鐐归噸缃懡浠や笅鍙戞垚鍔�(crn_fetch)锛岀珯鐐瑰彿={}锛屽懡浠ゆ暟鎹�={}", stationObjModel.getStationId(),
@@ -699,7 +702,7 @@
// 鐢熸垚浠跨湡绔欑偣鏁版嵁
StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998,
wrkMast.getSourceStaNo(), 0, 0);
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "fake-process", "crn-out-complete-write-info");
redisUtil.set(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo(), JSON.toJSONString(stationObjModel, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24);
}
} else if (wrkMast.getWrkSts() == WrkStsType.LOC_MOVE_RUN.sts) {
diff --git a/src/main/java/com/zy/core/plugin/GslProcess.java b/src/main/java/com/zy/core/plugin/GslProcess.java
index 5d5d1a7..ea52095 100644
--- a/src/main/java/com/zy/core/plugin/GslProcess.java
+++ b/src/main/java/com/zy/core/plugin/GslProcess.java
@@ -8,14 +8,13 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
-import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
+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.model.StationObjModel;
-import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.plugin.api.MainProcessPluginApi;
@@ -49,6 +48,8 @@
private RedisUtil redisUtil;
@Autowired
private StoreInTaskGenerationService storeInTaskGenerationService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
@Override
public void run() {
@@ -115,7 +116,7 @@
News.taskInfo(stationProtocol.getTaskNo(), "{}宸ヤ綔,鑾峰彇杈撻�佺嚎鍛戒护澶辫触", stationProtocol.getTaskNo());
return false;
}
- MessageQueue.offer(SlaveType.Devp, context.getBasDevp().getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(context.getBasDevp().getDevpNo(), command, "gsl-process", "station-back");
News.taskInfo(stationProtocol.getTaskNo(), "{}鎵爜寮傚父锛屽凡閫�鍥炶嚦{}", backStation.getStationId());
redisUtil.set(RedisKeyType.GENERATE_STATION_BACK_LIMIT.key + stationProtocol.getStationId(), "lock", 10);
return true;
@@ -163,7 +164,7 @@
&& stationProtocol.isEnableIn()
) {
StationCommand command = stationThread.getCommand(StationCommandType.MOVE, commonService.getWorkNo(WrkIoType.ENABLE_IN.id), stationId, entity.getBarcodeStation().getStationId(), 0);
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "gsl-process", "enable-in");
redisUtil.set(RedisKeyType.GENERATE_ENABLE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 15);
News.info("{}绔欑偣鍚姩鍏ュ簱鎴愬姛锛屾暟鎹寘:{}", stationId, JSON.toJSONString(command));
}
diff --git a/src/main/java/com/zy/core/plugin/NormalProcess.java b/src/main/java/com/zy/core/plugin/NormalProcess.java
index 21221e3..32e3481 100644
--- a/src/main/java/com/zy/core/plugin/NormalProcess.java
+++ b/src/main/java/com/zy/core/plugin/NormalProcess.java
@@ -13,14 +13,13 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
-import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
+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.model.StationObjModel;
-import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.plugin.api.MainProcessPluginApi;
@@ -59,6 +58,8 @@
private WmsOperateUtils wmsOperateUtils;
@Autowired
private StoreInTaskGenerationService storeInTaskGenerationService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
@Override
public void run() {
@@ -135,7 +136,7 @@
&& stationProtocol.isEnableIn()
) {
StationCommand command = stationThread.getCommand(StationCommandType.MOVE, commonService.getWorkNo(WrkIoType.ENABLE_IN.id), stationId, entity.getBarcodeStation().getStationId(), 0);
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "normal-process", "enable-in");
redisUtil.set(RedisKeyType.GENERATE_ENABLE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 15);
News.info("{}绔欑偣鍚姩鍏ュ簱鎴愬姛锛屾暟鎹寘:{}", stationId, JSON.toJSONString(command));
}
diff --git a/src/main/java/com/zy/core/plugin/XiaosongProcess.java b/src/main/java/com/zy/core/plugin/XiaosongProcess.java
index 5fcf9d6..cf5670f 100644
--- a/src/main/java/com/zy/core/plugin/XiaosongProcess.java
+++ b/src/main/java/com/zy/core/plugin/XiaosongProcess.java
@@ -13,14 +13,13 @@
import com.zy.common.service.CommonService;
import com.zy.common.utils.RedisUtil;
import com.zy.core.News;
-import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
+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.model.StationObjModel;
-import com.zy.core.model.Task;
import com.zy.core.model.command.StationCommand;
import com.zy.core.model.protocol.StationProtocol;
import com.zy.core.plugin.api.MainProcessPluginApi;
@@ -62,6 +61,8 @@
private DualCrnOperateProcessUtils dualCrnOperateProcessUtils;
@Autowired
private StoreInTaskGenerationService storeInTaskGenerationService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
@Override
public void run() {
@@ -130,7 +131,7 @@
News.taskInfo(stationProtocol.getTaskNo(), "{}宸ヤ綔,鑾峰彇杈撻�佺嚎鍛戒护澶辫触", stationProtocol.getTaskNo());
return false;
}
- MessageQueue.offer(SlaveType.Devp, context.getBasDevp().getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(context.getBasDevp().getDevpNo(), command, "xiaosong-process", "station-back");
News.taskInfo(stationProtocol.getTaskNo(), "{}鎵爜寮傚父锛屽凡閫�鍥炶嚦{}", backStation.getStationId());
redisUtil.set(RedisKeyType.GENERATE_STATION_BACK_LIMIT.key + stationProtocol.getTaskNo(), "lock", 10);
return true;
@@ -170,7 +171,7 @@
&& stationProtocol.isEnableIn()
) {
StationCommand command = stationThread.getCommand(StationCommandType.MOVE, commonService.getWorkNo(WrkIoType.ENABLE_IN.id), stationId, entity.getBarcodeStation().getStationId(), 0);
- MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(basDevp.getDevpNo(), command, "xiaosong-process", "enable-in");
redisUtil.set(RedisKeyType.GENERATE_ENABLE_IN_STATION_DATA_LIMIT.key + stationId, "lock", 15);
News.info("{}绔欑偣鍚姩鍏ュ簱鎴愬姛锛屾暟鎹寘:{}", stationId, JSON.toJSONString(command));
}
diff --git a/src/main/java/com/zy/core/thread/impl/ZyStationV5Thread.java b/src/main/java/com/zy/core/thread/impl/ZyStationV5Thread.java
index 1e8f4c4..17bd7f8 100644
--- a/src/main/java/com/zy/core/thread/impl/ZyStationV5Thread.java
+++ b/src/main/java/com/zy/core/thread/impl/ZyStationV5Thread.java
@@ -2,26 +2,14 @@
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
-import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-import com.core.common.Cools;
-import com.core.common.DateUtils;
import com.core.common.SpringUtils;
-import com.zy.asrs.domain.vo.StationCycleCapacityVo;
-import com.zy.asrs.domain.vo.StationCycleLoopVo;
-import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStationOpt;
import com.zy.asrs.entity.DeviceConfig;
-import com.zy.asrs.entity.DeviceDataLog;
-import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.BasStationOptService;
-import com.zy.asrs.service.StationCycleCapacityService;
-import com.zy.asrs.utils.Utils;
import com.zy.common.model.NavigateNode;
import com.zy.common.utils.NavigateUtils;
import com.zy.common.utils.RedisUtil;
import com.zy.core.cache.MessageQueue;
-import com.zy.core.cache.OutputQueue;
-import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.enums.StationCommandType;
import com.zy.core.model.CommandResponse;
@@ -33,26 +21,19 @@
import com.zy.core.network.ZyStationConnectDriver;
import com.zy.core.network.entity.ZyStationStatusEntity;
import com.zy.core.service.StationTaskLoopService;
+import com.zy.core.thread.impl.v5.StationV5RunBlockReroutePlanner;
import com.zy.core.thread.impl.v5.StationV5SegmentExecutor;
+import com.zy.core.thread.impl.v5.StationV5StatusReader;
import com.zy.core.thread.support.RecentStationArrivalTracker;
-import com.zy.core.trace.StationTaskTraceRegistry;
-import com.zy.core.utils.DeviceLogRedisKeyBuilder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
-import java.text.MessageFormat;
import java.util.ArrayList;
-import java.util.ArrayDeque;
-import java.util.Collections;
import java.util.Date;
-import java.util.Deque;
import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -60,42 +41,35 @@
@Slf4j
public class ZyStationV5Thread implements Runnable, com.zy.core.thread.StationThread {
- private static final int RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS = 60 * 60 * 24;
- private static final int SHORT_PATH_REPEAT_AVOID_THRESHOLD = 2;
- private static final int LOOP_REPEAT_TRIGGER_COUNT = 3;
- private static final int LOCAL_LOOP_NEIGHBOR_HOP = 3;
+ private static final int SEGMENT_EXECUTOR_POOL_SIZE = 64;
- private List<StationProtocol> statusList = new ArrayList<>();
private DeviceConfig deviceConfig;
private RedisUtil redisUtil;
private ZyStationConnectDriver zyStationConnectDriver;
- private int deviceLogCollectTime = 200;
- private boolean initStatus = false;
- private long deviceDataLogTime = System.currentTimeMillis();
- private ExecutorService executor = Executors.newFixedThreadPool(9999);
+ private final ExecutorService executor = Executors.newFixedThreadPool(SEGMENT_EXECUTOR_POOL_SIZE);
private StationV5SegmentExecutor segmentExecutor;
private final RecentStationArrivalTracker recentArrivalTracker;
+ private final StationV5StatusReader statusReader;
+ private final StationV5RunBlockReroutePlanner runBlockReroutePlanner;
public ZyStationV5Thread(DeviceConfig deviceConfig, RedisUtil redisUtil) {
this.deviceConfig = deviceConfig;
this.redisUtil = redisUtil;
this.recentArrivalTracker = new RecentStationArrivalTracker(redisUtil);
this.segmentExecutor = new StationV5SegmentExecutor(deviceConfig, redisUtil, this::sendCommand);
+ this.statusReader = new StationV5StatusReader(deviceConfig, redisUtil, recentArrivalTracker);
+ this.runBlockReroutePlanner = new StationV5RunBlockReroutePlanner(redisUtil);
}
@Override
@SuppressWarnings("InfiniteLoopStatement")
public void run() {
this.connect();
- deviceLogCollectTime = Utils.getDeviceLogCollectTime();
Thread readThread = new Thread(() -> {
while (true) {
try {
- if (initStatus) {
- deviceLogCollectTime = Utils.getDeviceLogCollectTime();
- }
- readStatus();
+ statusReader.readStatus(zyStationConnectDriver);
Thread.sleep(100);
} catch (Exception e) {
log.error("StationV5Thread Fail", e);
@@ -107,15 +81,7 @@
Thread processThread = new Thread(() -> {
while (true) {
try {
- int step = 1;
- Task task = MessageQueue.poll(SlaveType.Devp, deviceConfig.getDeviceNo());
- if (task != null) {
- step = task.getStep();
- }
- if (step == 2) {
- StationCommand cmd = (StationCommand) task.getData();
- executor.submit(() -> segmentExecutor.execute(cmd));
- }
+ pollAndDispatchQueuedCommand();
Thread.sleep(100);
} catch (Exception e) {
log.error("StationV5Process Fail", e);
@@ -123,84 +89,6 @@
}
}, "DevpProcess-" + deviceConfig.getDeviceNo());
processThread.start();
- }
-
- private void readStatus() {
- if (zyStationConnectDriver == null) {
- return;
- }
-
- if (statusList.isEmpty()) {
- BasDevpService basDevpService = null;
- try {
- basDevpService = SpringUtils.getBean(BasDevpService.class);
- } catch (Exception ignore) {
- }
- if (basDevpService == null) {
- return;
- }
-
- BasDevp basDevp = basDevpService
- .getOne(new QueryWrapper<BasDevp>().eq("devp_no", deviceConfig.getDeviceNo()));
- if (basDevp == null) {
- return;
- }
-
- List<ZyStationStatusEntity> list = JSONObject.parseArray(basDevp.getStationList(), ZyStationStatusEntity.class);
- for (ZyStationStatusEntity entity : list) {
- StationProtocol stationProtocol = new StationProtocol();
- stationProtocol.setStationId(entity.getStationId());
- statusList.add(stationProtocol);
- }
- initStatus = true;
- }
-
- List<ZyStationStatusEntity> zyStationStatusEntities = zyStationConnectDriver.getStatus();
- for (ZyStationStatusEntity statusEntity : zyStationStatusEntities) {
- for (StationProtocol stationProtocol : statusList) {
- if (stationProtocol.getStationId().equals(statusEntity.getStationId())) {
- stationProtocol.setTaskNo(statusEntity.getTaskNo());
- stationProtocol.setTargetStaNo(statusEntity.getTargetStaNo());
- stationProtocol.setAutoing(statusEntity.isAutoing());
- stationProtocol.setLoading(statusEntity.isLoading());
- stationProtocol.setInEnable(statusEntity.isInEnable());
- stationProtocol.setOutEnable(statusEntity.isOutEnable());
- stationProtocol.setEmptyMk(statusEntity.isEmptyMk());
- stationProtocol.setFullPlt(statusEntity.isFullPlt());
- stationProtocol.setPalletHeight(statusEntity.getPalletHeight());
- stationProtocol.setError(statusEntity.getError());
- stationProtocol.setErrorMsg(statusEntity.getErrorMsg());
- stationProtocol.setBarcode(statusEntity.getBarcode());
- stationProtocol.setRunBlock(statusEntity.isRunBlock());
- stationProtocol.setEnableIn(statusEntity.isEnableIn());
- stationProtocol.setWeight(statusEntity.getWeight());
- stationProtocol.setTaskWriteIdx(statusEntity.getTaskWriteIdx());
- stationProtocol.setTaskBufferItems(statusEntity.getTaskBufferItems());
- recentArrivalTracker.observe(statusEntity.getStationId(), statusEntity.getTaskNo(), statusEntity.isLoading());
- }
-
- if (!Cools.isEmpty(stationProtocol.getSystemWarning())) {
- if (stationProtocol.isAutoing() && !stationProtocol.isLoading()) {
- stationProtocol.setSystemWarning("");
- }
- }
- }
- }
-
- OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛",
- DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
-
- if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
- DeviceDataLog deviceDataLog = new DeviceDataLog();
- deviceDataLog.setOriginData(JSON.toJSONString(zyStationStatusEntities));
- deviceDataLog.setWcsData(JSON.toJSONString(statusList));
- deviceDataLog.setType(String.valueOf(SlaveType.Devp));
- deviceDataLog.setDeviceNo(deviceConfig.getDeviceNo());
- deviceDataLog.setCreateTime(new Date());
-
- redisUtil.set(DeviceLogRedisKeyBuilder.build(deviceDataLog), deviceDataLog, 60 * 60 * 24);
- deviceDataLogTime = System.currentTimeMillis();
- }
}
@Override
@@ -226,16 +114,31 @@
@Override
public List<StationProtocol> getStatus() {
- return statusList;
+ return statusReader.getStatusList();
}
@Override
public Map<Integer, StationProtocol> getStatusMap() {
Map<Integer, StationProtocol> map = new HashMap<>();
- for (StationProtocol stationProtocol : statusList) {
+ for (StationProtocol stationProtocol : statusReader.getStatusList()) {
map.put(stationProtocol.getStationId(), stationProtocol);
}
return map;
+ }
+
+ private void pollAndDispatchQueuedCommand() {
+ Task task = MessageQueue.poll(SlaveType.Devp, deviceConfig.getDeviceNo());
+ if (task == null || task.getStep() == null || task.getStep() != 2) {
+ return;
+ }
+ submitSegmentCommand((StationCommand) task.getData());
+ }
+
+ private void submitSegmentCommand(StationCommand command) {
+ if (command == null || executor == null || segmentExecutor == null) {
+ return;
+ }
+ executor.submit(() -> segmentExecutor.execute(command));
}
@Override
@@ -294,7 +197,6 @@
return getCommand(StationCommandType.MOVE, taskNo, stationId, targetStationId, palletSize, pathLenFactor);
}
- RunBlockRerouteState rerouteState = loadRunBlockRerouteState(taskNo, stationId);
StationTaskLoopService taskLoopService = loadStationTaskLoopService();
StationTaskLoopService.LoopEvaluation loopEvaluation = taskLoopService == null
? new StationTaskLoopService.LoopEvaluation(taskNo, stationId, StationTaskLoopService.LoopIdentitySnapshot.empty(), 0, 0, false)
@@ -305,61 +207,44 @@
loopEvaluation.getLoopIdentity().getScopeType(),
loopEvaluation.getLoopIdentity().getLocalStationCount(),
loopEvaluation.getLoopIdentity().getSourceLoopStationCount());
- rerouteState.setTaskNo(taskNo);
- rerouteState.setBlockStationId(stationId);
- rerouteState.setLastTargetStationId(targetStationId);
- rerouteState.setPlanCount((rerouteState.getPlanCount() == null ? 0 : rerouteState.getPlanCount()) + 1);
- rerouteState.setLastPlanTime(System.currentTimeMillis());
-
List<List<NavigateNode>> candidatePathList = calcCandidatePathNavigateNodes(taskNo, stationId, targetStationId, pathLenFactor);
- if (candidatePathList.isEmpty()) {
- saveRunBlockRerouteState(rerouteState);
+ List<StationCommand> candidateCommandList = new ArrayList<>();
+ for (List<NavigateNode> candidatePath : candidatePathList) {
+ StationCommand rerouteCommand = buildMoveCommand(taskNo, stationId, targetStationId, palletSize, candidatePath);
+ if (rerouteCommand == null || rerouteCommand.getNavigatePath() == null || rerouteCommand.getNavigatePath().isEmpty()) {
+ continue;
+ }
+ candidateCommandList.add(rerouteCommand);
+ }
+
+ StationV5RunBlockReroutePlanner.PlanResult planResult = runBlockReroutePlanner.plan(
+ taskNo,
+ stationId,
+ loopEvaluation,
+ candidateCommandList
+ );
+ if (candidateCommandList.isEmpty()) {
log.warn("杈撻�佺嚎鍫靛閲嶈鍒掑け璐ワ紝鍊欓�夎矾寰勪负绌猴紝taskNo={}, planCount={}, stationId={}, targetStationId={}",
- taskNo, rerouteState.getPlanCount(), stationId, targetStationId);
+ taskNo, planResult.getPlanCount(), stationId, targetStationId);
return null;
}
- StationCommand rerouteCommand = selectAvailableRerouteCommand(
- rerouteState,
- loopEvaluation,
- candidatePathList,
- taskNo,
- stationId,
- targetStationId,
- palletSize
- );
- if (rerouteCommand == null) {
- log.info("杈撻�佺嚎鍫靛閲嶈鍒掑�欓�夎矾绾垮凡鍏ㄩ儴璇曡繃锛岄噸缃矾绾垮巻鍙插悗閲嶆柊寮�濮嬶紝taskNo={}, planCount={}, stationId={}, targetStationId={}",
- taskNo, rerouteState.getPlanCount(), stationId, targetStationId);
- rerouteState.resetIssuedRoutes();
- rerouteCommand = selectAvailableRerouteCommand(
- rerouteState,
- loopEvaluation,
- candidatePathList,
- taskNo,
- stationId,
- targetStationId,
- palletSize
- );
- }
-
+ StationCommand rerouteCommand = planResult.getCommand();
if (rerouteCommand != null) {
- saveRunBlockRerouteState(rerouteState);
if (taskLoopService != null) {
taskLoopService.recordLoopIssue(loopEvaluation, "RUN_BLOCK_REROUTE");
}
log.info("杈撻�佺嚎鍫靛閲嶈鍒掗�変腑鍊欓�夎矾绾匡紝taskNo={}, planCount={}, stationId={}, targetStationId={}, route={}",
- taskNo, rerouteState.getPlanCount(), stationId, targetStationId, JSON.toJSONString(rerouteCommand.getNavigatePath()));
+ taskNo, planResult.getPlanCount(), stationId, targetStationId, JSON.toJSONString(rerouteCommand.getNavigatePath()));
return rerouteCommand;
}
- saveRunBlockRerouteState(rerouteState);
log.warn("杈撻�佺嚎鍫靛閲嶈鍒掓湭鎵惧埌鍙笅鍙戣矾绾匡紝taskNo={}, planCount={}, stationId={}, targetStationId={}, triedRoutes={}",
taskNo,
- rerouteState.getPlanCount(),
+ planResult.getPlanCount(),
stationId,
targetStationId,
- JSON.toJSONString(rerouteState.getIssuedRoutePathList()));
+ JSON.toJSONString(planResult.getIssuedRoutePathList()));
return null;
}
@@ -395,6 +280,8 @@
stationId, item.getSlotIdx(), item.getTaskNo());
continue;
}else {
+ item.setTaskNo(0);
+ item.setTargetStaNo(0);
success = true;
log.warn("杈撻�佺珯缂撳瓨鍖烘畫鐣欒矾寰勬竻鐞嗘垚鍔熴�俿tationId={}, slotIdx={}, taskNo={}",
stationId, item.getSlotIdx(), item.getTaskNo());
@@ -529,769 +416,11 @@
return stationCommand;
}
- private StationCommand selectAvailableRerouteCommand(RunBlockRerouteState rerouteState,
- StationTaskLoopService.LoopEvaluation loopEvaluation,
- List<List<NavigateNode>> candidatePathList,
- Integer taskNo,
- Integer stationId,
- Integer targetStationId,
- Integer palletSize) {
- if (rerouteState == null || candidatePathList == null || candidatePathList.isEmpty()) {
- return null;
- }
-
- Set<String> issuedRouteSignatureSet = rerouteState.getIssuedRouteSignatureSet();
- List<RerouteCandidateCommand> candidateCommandList = new ArrayList<>();
- for (List<NavigateNode> candidatePath : candidatePathList) {
- StationCommand rerouteCommand = buildMoveCommand(taskNo, stationId, targetStationId, palletSize, candidatePath);
- if (rerouteCommand == null || rerouteCommand.getNavigatePath() == null || rerouteCommand.getNavigatePath().isEmpty()) {
- continue;
- }
- String routeSignature = buildPathSignature(rerouteCommand.getNavigatePath());
- if (Cools.isEmpty(routeSignature)) {
- continue;
- }
- RerouteCandidateCommand candidateCommand = new RerouteCandidateCommand();
- candidateCommand.setCommand(rerouteCommand);
- candidateCommand.setRouteSignature(routeSignature);
- candidateCommand.setPathLength(rerouteCommand.getNavigatePath().size());
- candidateCommand.setIssuedCount(rerouteState.getRouteIssueCountMap().getOrDefault(routeSignature, 0));
- candidateCommand.setLoopFingerprint(loopEvaluation.getLoopIdentity().getLoopFingerprint());
- candidateCommand.setLoopIssuedCount(loopEvaluation.getExpectedLoopIssueCount());
- candidateCommand.setLoopTriggered(loopEvaluation.isLargeLoopTriggered());
- candidateCommand.setCurrentLoopHitCount(countCurrentLoopStationHit(
- rerouteCommand.getNavigatePath(),
- loopEvaluation.getLoopIdentity().getStationIdSet()
- ));
- candidateCommandList.add(candidateCommand);
- }
- if (candidateCommandList.isEmpty()) {
- return null;
- }
-
- List<RerouteCandidateCommand> orderedCandidateCommandList = reorderCandidateCommandsForLoopRelease(candidateCommandList);
- for (RerouteCandidateCommand candidateCommand : orderedCandidateCommandList) {
- if (candidateCommand == null || candidateCommand.getCommand() == null) {
- continue;
- }
- if (issuedRouteSignatureSet.contains(candidateCommand.getRouteSignature())) {
- continue;
- }
-
- StationCommand rerouteCommand = candidateCommand.getCommand();
- issuedRouteSignatureSet.add(candidateCommand.getRouteSignature());
- rerouteState.getIssuedRoutePathList().add(new ArrayList<>(rerouteCommand.getNavigatePath()));
- rerouteState.setLastSelectedRoute(new ArrayList<>(rerouteCommand.getNavigatePath()));
- rerouteState.getRouteIssueCountMap().put(
- candidateCommand.getRouteSignature(),
- rerouteState.getRouteIssueCountMap().getOrDefault(candidateCommand.getRouteSignature(), 0) + 1
- );
- return rerouteCommand;
- }
- return null;
- }
-
- private List<RerouteCandidateCommand> reorderCandidateCommandsForLoopRelease(List<RerouteCandidateCommand> candidateCommandList) {
- if (candidateCommandList == null || candidateCommandList.isEmpty()) {
- return new ArrayList<>();
- }
-
- int shortestPathLength = Integer.MAX_VALUE;
- int shortestPathLoopHitCount = Integer.MAX_VALUE;
- boolean shortestPathOverused = false;
- boolean currentLoopOverused = false;
- boolean hasLongerCandidate = false;
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) {
- continue;
- }
- shortestPathLength = Math.min(shortestPathLength, candidateCommand.getPathLength());
- }
- if (shortestPathLength == Integer.MAX_VALUE) {
- return candidateCommandList;
- }
-
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) {
- continue;
- }
- if (candidateCommand.getPathLength() == shortestPathLength) {
- shortestPathLoopHitCount = Math.min(shortestPathLoopHitCount, safeInt(candidateCommand.getCurrentLoopHitCount()));
- }
- if (candidateCommand.getPathLength() > shortestPathLength) {
- hasLongerCandidate = true;
- }
- if (candidateCommand.getPathLength() == shortestPathLength
- && candidateCommand.getIssuedCount() != null
- && candidateCommand.getIssuedCount() >= SHORT_PATH_REPEAT_AVOID_THRESHOLD) {
- shortestPathOverused = true;
- }
- if (!Cools.isEmpty(candidateCommand.getLoopFingerprint())
- && Boolean.TRUE.equals(candidateCommand.getLoopTriggered())) {
- currentLoopOverused = true;
- }
- }
- if (!shortestPathOverused && !currentLoopOverused) {
- return candidateCommandList;
- }
- if (shortestPathLoopHitCount == Integer.MAX_VALUE) {
- shortestPathLoopHitCount = 0;
- }
-
- boolean hasLoopExitCandidate = false;
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand == null) {
- continue;
- }
- if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) {
- hasLoopExitCandidate = true;
- break;
- }
- }
- if (!hasLongerCandidate && !hasLoopExitCandidate) {
- return candidateCommandList;
- }
-
- List<RerouteCandidateCommand> reorderedList = new ArrayList<>();
- if (currentLoopOverused && hasLoopExitCandidate) {
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand == null) {
- continue;
- }
- if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) {
- appendCandidateIfAbsent(reorderedList, candidateCommand);
- }
- }
- }
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand != null
- && candidateCommand.getPathLength() != null
- && candidateCommand.getPathLength() > shortestPathLength) {
- appendCandidateIfAbsent(reorderedList, candidateCommand);
- }
- }
- for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
- if (candidateCommand == null || candidateCommand.getPathLength() == null) {
- continue;
- }
- appendCandidateIfAbsent(reorderedList, candidateCommand);
- }
- return reorderedList;
- }
-
- private void appendCandidateIfAbsent(List<RerouteCandidateCommand> reorderedList,
- RerouteCandidateCommand candidateCommand) {
- if (reorderedList == null || candidateCommand == null) {
- return;
- }
- if (!reorderedList.contains(candidateCommand)) {
- reorderedList.add(candidateCommand);
- }
- }
-
- private RunBlockRerouteState loadRunBlockRerouteState(Integer taskNo, Integer blockStationId) {
- if (redisUtil == null || taskNo == null || taskNo <= 0 || blockStationId == null || blockStationId <= 0) {
- return new RunBlockRerouteState();
- }
- Object stateObj = redisUtil.get(buildRunBlockRerouteStateKey(taskNo, blockStationId));
- if (stateObj == null) {
- return new RunBlockRerouteState();
- }
- try {
- RunBlockRerouteState state = JSON.parseObject(String.valueOf(stateObj), RunBlockRerouteState.class);
- return state == null ? new RunBlockRerouteState() : state.normalize();
- } catch (Exception ignore) {
- return new RunBlockRerouteState();
- }
- }
-
- private void saveRunBlockRerouteState(RunBlockRerouteState rerouteState) {
- if (redisUtil == null
- || rerouteState == null
- || rerouteState.getTaskNo() == null
- || rerouteState.getTaskNo() <= 0
- || rerouteState.getBlockStationId() == null
- || rerouteState.getBlockStationId() <= 0) {
- return;
- }
- rerouteState.normalize();
- redisUtil.set(
- buildRunBlockRerouteStateKey(rerouteState.getTaskNo(), rerouteState.getBlockStationId()),
- JSON.toJSONString(rerouteState),
- RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS
- );
- }
-
- private TaskLoopRerouteState loadTaskLoopRerouteState(Integer taskNo) {
- if (redisUtil == null || taskNo == null || taskNo <= 0) {
- return new TaskLoopRerouteState();
- }
- Object stateObj = redisUtil.get(RedisKeyType.STATION_RUN_BLOCK_TASK_LOOP_STATE_.key + taskNo);
- if (stateObj == null) {
- return new TaskLoopRerouteState();
- }
- try {
- TaskLoopRerouteState state = JSON.parseObject(String.valueOf(stateObj), TaskLoopRerouteState.class);
- return state == null ? new TaskLoopRerouteState() : state.normalize();
- } catch (Exception ignore) {
- return new TaskLoopRerouteState();
- }
- }
-
- private void saveTaskLoopRerouteState(TaskLoopRerouteState taskLoopRerouteState) {
- if (redisUtil == null
- || taskLoopRerouteState == null
- || taskLoopRerouteState.getTaskNo() == null
- || taskLoopRerouteState.getTaskNo() <= 0) {
- return;
- }
- taskLoopRerouteState.normalize();
- redisUtil.set(
- RedisKeyType.STATION_RUN_BLOCK_TASK_LOOP_STATE_.key + taskLoopRerouteState.getTaskNo(),
- JSON.toJSONString(taskLoopRerouteState),
- RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS
- );
- }
-
- private void touchTaskLoopRerouteState(TaskLoopRerouteState taskLoopRerouteState,
- LoopIdentity currentLoopIdentity) {
- if (taskLoopRerouteState == null || currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) {
- return;
- }
- taskLoopRerouteState.getLoopIssueCountMap().put(
- currentLoopIdentity.getLoopFingerprint(),
- taskLoopRerouteState.getLoopIssueCountMap().getOrDefault(currentLoopIdentity.getLoopFingerprint(), 0) + 1
- );
- taskLoopRerouteState.setLastLoopFingerprint(currentLoopIdentity.getLoopFingerprint());
- taskLoopRerouteState.setLastIssueTime(System.currentTimeMillis());
- }
-
- private int resolveCurrentLoopIssuedCount(TaskLoopRerouteState taskLoopRerouteState,
- LoopIdentity currentLoopIdentity) {
- if (taskLoopRerouteState == null || currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) {
- return 0;
- }
- return taskLoopRerouteState.getLoopIssueCountMap().getOrDefault(currentLoopIdentity.getLoopFingerprint(), 0);
- }
-
- private int resolveExpectedLoopIssuedCount(TaskLoopRerouteState taskLoopRerouteState,
- LoopIdentity currentLoopIdentity) {
- if (currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) {
- return 0;
- }
- return resolveCurrentLoopIssuedCount(taskLoopRerouteState, currentLoopIdentity) + 1;
- }
-
- private boolean isLoopRepeatTriggered(Integer loopIssuedCount) {
- return loopIssuedCount != null && loopIssuedCount >= LOOP_REPEAT_TRIGGER_COUNT;
- }
-
- private void syncTaskTraceLoopAlert(Integer taskNo,
- Integer blockedStationId,
- LoopIdentity currentLoopIdentity,
- int loopIssuedCount) {
- StationTaskTraceRegistry traceRegistry;
- try {
- traceRegistry = SpringUtils.getBean(StationTaskTraceRegistry.class);
- } catch (Exception ignore) {
- return;
- }
- if (traceRegistry == null || taskNo == null || taskNo <= 0) {
- return;
- }
-
- boolean active = currentLoopIdentity != null
- && !Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())
- && isLoopRepeatTriggered(loopIssuedCount);
- Map<String, Object> details = new HashMap<>();
- details.put("blockedStationId", blockedStationId);
- details.put("loopScopeType", currentLoopIdentity == null ? "" : currentLoopIdentity.getScopeType());
- details.put("loopStationCount", currentLoopIdentity == null ? 0 : currentLoopIdentity.getLocalStationCount());
- details.put("sourceLoopStationCount", currentLoopIdentity == null ? 0 : currentLoopIdentity.getSourceLoopStationCount());
- details.put("loopRepeatCount", loopIssuedCount);
- String loopAlertType = resolveLoopAlertType(currentLoopIdentity);
- String loopAlertText = buildLoopAlertText(loopAlertType, currentLoopIdentity, loopIssuedCount);
- traceRegistry.updateLoopHint(taskNo, active, loopAlertType, loopAlertText, loopIssuedCount, details);
- }
-
- private String resolveLoopAlertType(LoopIdentity currentLoopIdentity) {
- if (currentLoopIdentity == null || Cools.isEmpty(currentLoopIdentity.getLoopFingerprint())) {
- return "";
- }
- return "wholeLoop".equals(currentLoopIdentity.getScopeType()) ? "LARGE_LOOP" : "SMALL_LOOP";
- }
-
- private String buildLoopAlertText(String loopAlertType,
- LoopIdentity currentLoopIdentity,
- int loopIssuedCount) {
- if (Cools.isEmpty(loopAlertType) || currentLoopIdentity == null || !isLoopRepeatTriggered(loopIssuedCount)) {
- return "";
- }
- String typeLabel = "LARGE_LOOP".equals(loopAlertType) ? "澶х幆绾�" : "灏忕幆绾�";
- return typeLabel + "缁曞湀棰勮锛岀疮璁¢噸瑙勫垝" + loopIssuedCount + "娆★紝褰撳墠璇嗗埆鑼冨洿"
- + currentLoopIdentity.getLocalStationCount() + "绔�";
- }
-
- private int countCurrentLoopStationHit(List<Integer> path, Set<Integer> currentLoopStationIdSet) {
- if (path == null || path.isEmpty() || currentLoopStationIdSet == null || currentLoopStationIdSet.isEmpty()) {
- return 0;
- }
- int hitCount = 0;
- for (Integer stationId : path) {
- if (stationId != null && currentLoopStationIdSet.contains(stationId)) {
- hitCount++;
- }
- }
- return hitCount;
- }
-
- private String buildPathSignature(List<Integer> path) {
- if (path == null || path.isEmpty()) {
- return "";
- }
- StringBuilder builder = new StringBuilder();
- for (Integer stationNo : path) {
- if (stationNo == null) {
- continue;
- }
- if (builder.length() > 0) {
- builder.append("->");
- }
- builder.append(stationNo);
- }
- return builder.toString();
- }
-
private StationTaskLoopService loadStationTaskLoopService() {
try {
return SpringUtils.getBean(StationTaskLoopService.class);
} catch (Exception ignore) {
return null;
}
- }
-
- private String buildRunBlockRerouteStateKey(Integer taskNo, Integer blockStationId) {
- return RedisKeyType.STATION_RUN_BLOCK_REROUTE_STATE_.key + taskNo + "_" + blockStationId;
- }
-
- private LoopIdentity resolveStationLoopIdentity(Integer stationId) {
- if (stationId == null || stationId <= 0) {
- return LoopIdentity.empty();
- }
- try {
- StationCycleCapacityService stationCycleCapacityService = SpringUtils.getBean(StationCycleCapacityService.class);
- if (stationCycleCapacityService == null) {
- return LoopIdentity.empty();
- }
- StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot();
- if (capacityVo == null || capacityVo.getLoopList() == null || capacityVo.getLoopList().isEmpty()) {
- return LoopIdentity.empty();
- }
- for (StationCycleLoopVo loopVo : capacityVo.getLoopList()) {
- List<Integer> loopStationIdList = normalizeLoopStationIdList(loopVo == null ? null : loopVo.getStationIdList());
- if (loopStationIdList.isEmpty() || !loopStationIdList.contains(stationId)) {
- continue;
- }
- Set<Integer> loopStationIdSet = new HashSet<>(loopStationIdList);
- Map<Integer, Set<Integer>> stationGraph = loadUndirectedStationGraph();
- List<Integer> localCycleStationIdList = resolveLocalCycleStationIdList(stationId, loopStationIdSet, stationGraph);
- if (localCycleStationIdList.size() >= 3) {
- return buildLoopIdentity(localCycleStationIdList, loopStationIdList.size(), "localCycle");
- }
-
- List<Integer> localNeighborhoodStationIdList = resolveLocalNeighborhoodStationIdList(stationId, loopStationIdSet, stationGraph);
- if (localNeighborhoodStationIdList.size() >= 3 && localNeighborhoodStationIdList.size() < loopStationIdList.size()) {
- return buildLoopIdentity(localNeighborhoodStationIdList, loopStationIdList.size(), "localNeighborhood");
- }
- return buildLoopIdentity(loopStationIdList, loopStationIdList.size(), "wholeLoop");
- }
- } catch (Exception ignore) {
- }
- return LoopIdentity.empty();
- }
-
- private LoopIdentity buildLoopIdentity(List<Integer> stationIdList,
- int sourceLoopStationCount,
- String scopeType) {
- List<Integer> normalizedStationIdList = normalizeLoopStationIdList(stationIdList);
- if (normalizedStationIdList.isEmpty()) {
- return LoopIdentity.empty();
- }
- return new LoopIdentity(
- buildLoopFingerprint(normalizedStationIdList),
- new HashSet<>(normalizedStationIdList),
- sourceLoopStationCount,
- normalizedStationIdList.size(),
- scopeType
- );
- }
-
- private List<Integer> resolveLocalCycleStationIdList(Integer stationId,
- Set<Integer> loopStationIdSet,
- Map<Integer, Set<Integer>> stationGraph) {
- if (stationId == null
- || stationId <= 0
- || loopStationIdSet == null
- || loopStationIdSet.isEmpty()
- || stationGraph == null
- || stationGraph.isEmpty()) {
- return new ArrayList<>();
- }
- Set<Integer> localNeighborhoodStationIdSet = collectLoopNeighborhoodStationIdSet(
- stationId,
- loopStationIdSet,
- stationGraph,
- LOCAL_LOOP_NEIGHBOR_HOP
- );
- if (localNeighborhoodStationIdSet.size() < 3) {
- return new ArrayList<>();
- }
-
- Set<Integer> directNeighborStationIdSet = filterLoopNeighborStationIdSet(
- stationGraph.getOrDefault(stationId, Collections.emptySet()),
- localNeighborhoodStationIdSet,
- stationId
- );
- if (directNeighborStationIdSet.size() < 2) {
- return new ArrayList<>();
- }
-
- List<Integer> bestCycleStationIdList = new ArrayList<>();
- List<Integer> neighborStationIdList = new ArrayList<>(directNeighborStationIdSet);
- for (int i = 0; i < neighborStationIdList.size(); i++) {
- Integer leftNeighborStationId = neighborStationIdList.get(i);
- if (leftNeighborStationId == null) {
- continue;
- }
- for (int j = i + 1; j < neighborStationIdList.size(); j++) {
- Integer rightNeighborStationId = neighborStationIdList.get(j);
- if (rightNeighborStationId == null) {
- continue;
- }
- List<Integer> pathBetweenNeighbors = findShortestScopePath(
- leftNeighborStationId,
- rightNeighborStationId,
- stationId,
- localNeighborhoodStationIdSet,
- stationGraph
- );
- if (pathBetweenNeighbors.isEmpty()) {
- continue;
- }
- List<Integer> cycleStationIdList = new ArrayList<>();
- cycleStationIdList.add(stationId);
- cycleStationIdList.addAll(pathBetweenNeighbors);
- cycleStationIdList = normalizeLoopStationIdList(cycleStationIdList);
- if (cycleStationIdList.size() < 3) {
- continue;
- }
- if (bestCycleStationIdList.isEmpty() || cycleStationIdList.size() < bestCycleStationIdList.size()) {
- bestCycleStationIdList = cycleStationIdList;
- }
- }
- }
- return bestCycleStationIdList;
- }
-
- private List<Integer> resolveLocalNeighborhoodStationIdList(Integer stationId,
- Set<Integer> loopStationIdSet,
- Map<Integer, Set<Integer>> stationGraph) {
- return normalizeLoopStationIdList(new ArrayList<>(collectLoopNeighborhoodStationIdSet(
- stationId,
- loopStationIdSet,
- stationGraph,
- LOCAL_LOOP_NEIGHBOR_HOP
- )));
- }
-
- private Set<Integer> collectLoopNeighborhoodStationIdSet(Integer stationId,
- Set<Integer> loopStationIdSet,
- Map<Integer, Set<Integer>> stationGraph,
- int maxHop) {
- Set<Integer> neighborhoodStationIdSet = new LinkedHashSet<>();
- if (stationId == null
- || stationId <= 0
- || loopStationIdSet == null
- || loopStationIdSet.isEmpty()
- || !loopStationIdSet.contains(stationId)
- || stationGraph == null
- || stationGraph.isEmpty()
- || maxHop < 0) {
- return neighborhoodStationIdSet;
- }
-
- Deque<StationHopNode> queue = new ArrayDeque<>();
- queue.offer(new StationHopNode(stationId, 0));
- neighborhoodStationIdSet.add(stationId);
- while (!queue.isEmpty()) {
- StationHopNode current = queue.poll();
- if (current == null || current.getHop() >= maxHop) {
- continue;
- }
- Set<Integer> neighborStationIdSet = filterLoopNeighborStationIdSet(
- stationGraph.getOrDefault(current.getStationId(), Collections.emptySet()),
- loopStationIdSet,
- null
- );
- for (Integer neighborStationId : neighborStationIdSet) {
- if (neighborStationId == null || !neighborhoodStationIdSet.add(neighborStationId)) {
- continue;
- }
- queue.offer(new StationHopNode(neighborStationId, current.getHop() + 1));
- }
- }
- return neighborhoodStationIdSet;
- }
-
- private Set<Integer> filterLoopNeighborStationIdSet(Set<Integer> candidateNeighborStationIdSet,
- Set<Integer> allowedStationIdSet,
- Integer excludedStationId) {
- Set<Integer> result = new LinkedHashSet<>();
- if (candidateNeighborStationIdSet == null || candidateNeighborStationIdSet.isEmpty()
- || allowedStationIdSet == null || allowedStationIdSet.isEmpty()) {
- return result;
- }
- for (Integer stationId : candidateNeighborStationIdSet) {
- if (stationId == null
- || !allowedStationIdSet.contains(stationId)
- || (excludedStationId != null && excludedStationId.equals(stationId))) {
- continue;
- }
- result.add(stationId);
- }
- return result;
- }
-
- private List<Integer> findShortestScopePath(Integer startStationId,
- Integer endStationId,
- Integer excludedStationId,
- Set<Integer> allowedStationIdSet,
- Map<Integer, Set<Integer>> stationGraph) {
- if (startStationId == null
- || endStationId == null
- || Objects.equals(startStationId, excludedStationId)
- || Objects.equals(endStationId, excludedStationId)
- || allowedStationIdSet == null
- || !allowedStationIdSet.contains(startStationId)
- || !allowedStationIdSet.contains(endStationId)
- || stationGraph == null
- || stationGraph.isEmpty()) {
- return new ArrayList<>();
- }
-
- Deque<Integer> queue = new ArrayDeque<>();
- Map<Integer, Integer> parentMap = new HashMap<>();
- Set<Integer> visitedStationIdSet = new HashSet<>();
- queue.offer(startStationId);
- visitedStationIdSet.add(startStationId);
- while (!queue.isEmpty()) {
- Integer currentStationId = queue.poll();
- if (currentStationId == null) {
- continue;
- }
- if (Objects.equals(currentStationId, endStationId)) {
- break;
- }
- Set<Integer> neighborStationIdSet = filterLoopNeighborStationIdSet(
- stationGraph.getOrDefault(currentStationId, Collections.emptySet()),
- allowedStationIdSet,
- excludedStationId
- );
- for (Integer neighborStationId : neighborStationIdSet) {
- if (neighborStationId == null || !visitedStationIdSet.add(neighborStationId)) {
- continue;
- }
- parentMap.put(neighborStationId, currentStationId);
- queue.offer(neighborStationId);
- }
- }
- if (!visitedStationIdSet.contains(endStationId)) {
- return new ArrayList<>();
- }
-
- List<Integer> pathStationIdList = new ArrayList<>();
- Integer cursorStationId = endStationId;
- while (cursorStationId != null) {
- pathStationIdList.add(cursorStationId);
- if (Objects.equals(cursorStationId, startStationId)) {
- break;
- }
- cursorStationId = parentMap.get(cursorStationId);
- }
- if (pathStationIdList.isEmpty()
- || !Objects.equals(pathStationIdList.get(pathStationIdList.size() - 1), startStationId)) {
- return new ArrayList<>();
- }
- Collections.reverse(pathStationIdList);
- return pathStationIdList;
- }
-
- private Map<Integer, Set<Integer>> loadUndirectedStationGraph() {
- NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class);
- if (navigateUtils == null) {
- return new HashMap<>();
- }
- Map<Integer, Set<Integer>> stationGraph = navigateUtils.loadUndirectedStationGraphSnapshot();
- return stationGraph == null ? new HashMap<>() : stationGraph;
- }
-
- private List<Integer> normalizeLoopStationIdList(List<Integer> stationIdList) {
- if (stationIdList == null || stationIdList.isEmpty()) {
- return new ArrayList<>();
- }
- List<Integer> normalizedList = new ArrayList<>();
- Set<Integer> seenStationIdSet = new HashSet<>();
- for (Integer stationId : stationIdList) {
- if (stationId == null || stationId <= 0 || !seenStationIdSet.add(stationId)) {
- continue;
- }
- normalizedList.add(stationId);
- }
- Collections.sort(normalizedList);
- return normalizedList;
- }
-
- private String buildLoopFingerprint(List<Integer> stationIdList) {
- if (stationIdList == null || stationIdList.isEmpty()) {
- return "";
- }
- StringBuilder builder = new StringBuilder();
- for (Integer stationId : stationIdList) {
- if (stationId == null) {
- continue;
- }
- if (builder.length() > 0) {
- builder.append("|");
- }
- builder.append(stationId);
- }
- return builder.toString();
- }
-
- private int safeInt(Integer value) {
- return value == null ? 0 : value;
- }
-
- @Data
- private static class RunBlockRerouteState {
- private Integer taskNo;
- private Integer blockStationId;
- private Integer planCount = 0;
- private Integer lastTargetStationId;
- private Long lastPlanTime;
- private List<List<Integer>> issuedRoutePathList = new ArrayList<>();
- private List<Integer> lastSelectedRoute = new ArrayList<>();
- private Set<String> issuedRouteSignatureSet = new LinkedHashSet<>();
- private Map<String, Integer> routeIssueCountMap = new HashMap<>();
-
- private RunBlockRerouteState normalize() {
- if (planCount == null || planCount < 0) {
- planCount = 0;
- }
- if (issuedRoutePathList == null) {
- issuedRoutePathList = new ArrayList<>();
- }
- if (lastSelectedRoute == null) {
- lastSelectedRoute = new ArrayList<>();
- }
- if (issuedRouteSignatureSet == null) {
- issuedRouteSignatureSet = new LinkedHashSet<>();
- }
- if (routeIssueCountMap == null) {
- routeIssueCountMap = new HashMap<>();
- }
- for (List<Integer> routePath : issuedRoutePathList) {
- if (routePath == null || routePath.isEmpty()) {
- continue;
- }
- String pathSignature = buildPathSignatureText(routePath);
- if (!Cools.isEmpty(pathSignature)) {
- issuedRouteSignatureSet.add(pathSignature);
- routeIssueCountMap.putIfAbsent(pathSignature, 1);
- }
- }
- return this;
- }
-
- private void resetIssuedRoutes() {
- this.issuedRoutePathList = new ArrayList<>();
- this.lastSelectedRoute = new ArrayList<>();
- this.issuedRouteSignatureSet = new LinkedHashSet<>();
- }
-
- private static String buildPathSignatureText(List<Integer> routePath) {
- if (routePath == null || routePath.isEmpty()) {
- return "";
- }
- StringBuilder builder = new StringBuilder();
- for (Integer stationId : routePath) {
- if (stationId == null) {
- continue;
- }
- if (builder.length() > 0) {
- builder.append("->");
- }
- builder.append(stationId);
- }
- return builder.toString();
- }
- }
-
- @Data
- private static class RerouteCandidateCommand {
- private StationCommand command;
- private String routeSignature;
- private Integer pathLength;
- private Integer issuedCount;
- private String loopFingerprint;
- private Integer loopIssuedCount;
- private Boolean loopTriggered;
- private Integer currentLoopHitCount;
- }
-
- @Data
- private static class TaskLoopRerouteState {
- private Integer taskNo;
- private String lastLoopFingerprint;
- private Long lastIssueTime;
- private Map<String, Integer> loopIssueCountMap = new HashMap<>();
-
- private TaskLoopRerouteState normalize() {
- if (loopIssueCountMap == null) {
- loopIssueCountMap = new HashMap<>();
- }
- return this;
- }
- }
-
- @Data
- private static class LoopIdentity {
- private String loopFingerprint;
- private Set<Integer> stationIdSet = new HashSet<>();
- private int sourceLoopStationCount;
- private int localStationCount;
- private String scopeType;
-
- private LoopIdentity(String loopFingerprint,
- Set<Integer> stationIdSet,
- int sourceLoopStationCount,
- int localStationCount,
- String scopeType) {
- this.loopFingerprint = loopFingerprint;
- this.stationIdSet = stationIdSet == null ? new HashSet<>() : stationIdSet;
- this.sourceLoopStationCount = sourceLoopStationCount;
- this.localStationCount = localStationCount;
- this.scopeType = scopeType == null ? "" : scopeType;
- }
-
- private static LoopIdentity empty() {
- return new LoopIdentity("", new HashSet<>(), 0, 0, "none");
- }
- }
-
- @Data
- private static class StationHopNode {
- private final Integer stationId;
- private final int hop;
}
}
diff --git a/src/main/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlanner.java b/src/main/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlanner.java
new file mode 100644
index 0000000..5c1d3c3
--- /dev/null
+++ b/src/main/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlanner.java
@@ -0,0 +1,375 @@
+package com.zy.core.thread.impl.v5;
+
+import com.alibaba.fastjson.JSON;
+import com.core.common.Cools;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.model.command.StationCommand;
+import com.zy.core.service.StationTaskLoopService;
+import lombok.Data;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class StationV5RunBlockReroutePlanner {
+
+ private static final int RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS = 60 * 60 * 24;
+ private static final int SHORT_PATH_REPEAT_AVOID_THRESHOLD = 2;
+
+ private final RedisUtil redisUtil;
+
+ public StationV5RunBlockReroutePlanner(RedisUtil redisUtil) {
+ this.redisUtil = redisUtil;
+ }
+
+ public PlanResult plan(Integer taskNo,
+ Integer blockStationId,
+ StationTaskLoopService.LoopEvaluation loopEvaluation,
+ List<StationCommand> candidateCommands) {
+ RunBlockRerouteState rerouteState = loadRunBlockRerouteState(taskNo, blockStationId);
+ rerouteState.setTaskNo(taskNo);
+ rerouteState.setBlockStationId(blockStationId);
+ rerouteState.setPlanCount((rerouteState.getPlanCount() == null ? 0 : rerouteState.getPlanCount()) + 1);
+
+ StationCommand rerouteCommand = selectAvailableRerouteCommand(rerouteState, loopEvaluation, candidateCommands);
+ if (rerouteCommand == null && candidateCommands != null && !candidateCommands.isEmpty()) {
+ rerouteState.resetIssuedRoutes();
+ rerouteCommand = selectAvailableRerouteCommand(rerouteState, loopEvaluation, candidateCommands);
+ }
+
+ saveRunBlockRerouteState(rerouteState);
+ return new PlanResult(
+ rerouteCommand,
+ rerouteState.getPlanCount() == null ? 0 : rerouteState.getPlanCount(),
+ copyRoutePathList(rerouteState.getIssuedRoutePathList())
+ );
+ }
+
+ private StationCommand selectAvailableRerouteCommand(RunBlockRerouteState rerouteState,
+ StationTaskLoopService.LoopEvaluation loopEvaluation,
+ List<StationCommand> candidateCommands) {
+ if (rerouteState == null || candidateCommands == null || candidateCommands.isEmpty()) {
+ return null;
+ }
+
+ Set<String> issuedRouteSignatureSet = rerouteState.getIssuedRouteSignatureSet();
+ StationTaskLoopService.LoopIdentitySnapshot loopIdentity = loopEvaluation == null
+ ? StationTaskLoopService.LoopIdentitySnapshot.empty()
+ : loopEvaluation.getLoopIdentity();
+ if (loopIdentity == null) {
+ loopIdentity = StationTaskLoopService.LoopIdentitySnapshot.empty();
+ }
+
+ List<RerouteCandidateCommand> candidateCommandList = new ArrayList<>();
+ for (StationCommand candidateCommand : candidateCommands) {
+ if (candidateCommand == null || candidateCommand.getNavigatePath() == null || candidateCommand.getNavigatePath().isEmpty()) {
+ continue;
+ }
+ String routeSignature = buildPathSignature(candidateCommand.getNavigatePath());
+ if (Cools.isEmpty(routeSignature)) {
+ continue;
+ }
+ RerouteCandidateCommand rerouteCandidateCommand = new RerouteCandidateCommand();
+ rerouteCandidateCommand.setCommand(candidateCommand);
+ rerouteCandidateCommand.setRouteSignature(routeSignature);
+ rerouteCandidateCommand.setPathLength(candidateCommand.getNavigatePath().size());
+ rerouteCandidateCommand.setIssuedCount(rerouteState.getRouteIssueCountMap().getOrDefault(routeSignature, 0));
+ rerouteCandidateCommand.setLoopFingerprint(loopIdentity.getLoopFingerprint());
+ rerouteCandidateCommand.setLoopTriggered(loopEvaluation != null && loopEvaluation.isLargeLoopTriggered());
+ rerouteCandidateCommand.setCurrentLoopHitCount(countCurrentLoopStationHit(
+ candidateCommand.getNavigatePath(),
+ loopIdentity.getStationIdSet()
+ ));
+ candidateCommandList.add(rerouteCandidateCommand);
+ }
+ if (candidateCommandList.isEmpty()) {
+ return null;
+ }
+
+ List<RerouteCandidateCommand> orderedCandidateCommandList = reorderCandidateCommandsForLoopRelease(candidateCommandList);
+ for (RerouteCandidateCommand candidateCommand : orderedCandidateCommandList) {
+ if (candidateCommand == null || candidateCommand.getCommand() == null) {
+ continue;
+ }
+ if (issuedRouteSignatureSet.contains(candidateCommand.getRouteSignature())) {
+ continue;
+ }
+
+ StationCommand rerouteCommand = candidateCommand.getCommand();
+ issuedRouteSignatureSet.add(candidateCommand.getRouteSignature());
+ rerouteState.getIssuedRoutePathList().add(new ArrayList<>(rerouteCommand.getNavigatePath()));
+ rerouteState.setLastSelectedRoute(new ArrayList<>(rerouteCommand.getNavigatePath()));
+ rerouteState.getRouteIssueCountMap().put(
+ candidateCommand.getRouteSignature(),
+ rerouteState.getRouteIssueCountMap().getOrDefault(candidateCommand.getRouteSignature(), 0) + 1
+ );
+ return rerouteCommand;
+ }
+ return null;
+ }
+
+ private List<RerouteCandidateCommand> reorderCandidateCommandsForLoopRelease(List<RerouteCandidateCommand> candidateCommandList) {
+ if (candidateCommandList == null || candidateCommandList.isEmpty()) {
+ return new ArrayList<>();
+ }
+
+ int shortestPathLength = Integer.MAX_VALUE;
+ int shortestPathLoopHitCount = Integer.MAX_VALUE;
+ boolean shortestPathOverused = false;
+ boolean currentLoopOverused = false;
+ boolean hasLongerCandidate = false;
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) {
+ continue;
+ }
+ shortestPathLength = Math.min(shortestPathLength, candidateCommand.getPathLength());
+ }
+ if (shortestPathLength == Integer.MAX_VALUE) {
+ return candidateCommandList;
+ }
+
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand == null || candidateCommand.getPathLength() == null || candidateCommand.getPathLength() <= 0) {
+ continue;
+ }
+ if (candidateCommand.getPathLength() == shortestPathLength) {
+ shortestPathLoopHitCount = Math.min(shortestPathLoopHitCount, safeInt(candidateCommand.getCurrentLoopHitCount()));
+ }
+ if (candidateCommand.getPathLength() > shortestPathLength) {
+ hasLongerCandidate = true;
+ }
+ if (candidateCommand.getPathLength() == shortestPathLength
+ && candidateCommand.getIssuedCount() != null
+ && candidateCommand.getIssuedCount() >= SHORT_PATH_REPEAT_AVOID_THRESHOLD) {
+ shortestPathOverused = true;
+ }
+ if (!Cools.isEmpty(candidateCommand.getLoopFingerprint())
+ && Boolean.TRUE.equals(candidateCommand.getLoopTriggered())) {
+ currentLoopOverused = true;
+ }
+ }
+ if (!shortestPathOverused && !currentLoopOverused) {
+ return candidateCommandList;
+ }
+ if (shortestPathLoopHitCount == Integer.MAX_VALUE) {
+ shortestPathLoopHitCount = 0;
+ }
+
+ boolean hasLoopExitCandidate = false;
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand == null) {
+ continue;
+ }
+ if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) {
+ hasLoopExitCandidate = true;
+ break;
+ }
+ }
+ if (!hasLongerCandidate && !hasLoopExitCandidate) {
+ return candidateCommandList;
+ }
+
+ List<RerouteCandidateCommand> reorderedList = new ArrayList<>();
+ if (currentLoopOverused && hasLoopExitCandidate) {
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand == null) {
+ continue;
+ }
+ if (safeInt(candidateCommand.getCurrentLoopHitCount()) < shortestPathLoopHitCount) {
+ appendCandidateIfAbsent(reorderedList, candidateCommand);
+ }
+ }
+ }
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand != null
+ && candidateCommand.getPathLength() != null
+ && candidateCommand.getPathLength() > shortestPathLength) {
+ appendCandidateIfAbsent(reorderedList, candidateCommand);
+ }
+ }
+ for (RerouteCandidateCommand candidateCommand : candidateCommandList) {
+ if (candidateCommand == null || candidateCommand.getPathLength() == null) {
+ continue;
+ }
+ appendCandidateIfAbsent(reorderedList, candidateCommand);
+ }
+ return reorderedList;
+ }
+
+ private void appendCandidateIfAbsent(List<RerouteCandidateCommand> reorderedList,
+ RerouteCandidateCommand candidateCommand) {
+ if (reorderedList == null || candidateCommand == null) {
+ return;
+ }
+ if (!reorderedList.contains(candidateCommand)) {
+ reorderedList.add(candidateCommand);
+ }
+ }
+
+ private RunBlockRerouteState loadRunBlockRerouteState(Integer taskNo, Integer blockStationId) {
+ if (redisUtil == null || taskNo == null || taskNo <= 0 || blockStationId == null || blockStationId <= 0) {
+ return new RunBlockRerouteState();
+ }
+ Object stateObj = redisUtil.get(buildRunBlockRerouteStateKey(taskNo, blockStationId));
+ if (stateObj == null) {
+ return new RunBlockRerouteState();
+ }
+ try {
+ RunBlockRerouteState state = JSON.parseObject(String.valueOf(stateObj), RunBlockRerouteState.class);
+ return state == null ? new RunBlockRerouteState() : state.normalize();
+ } catch (Exception ignore) {
+ return new RunBlockRerouteState();
+ }
+ }
+
+ private void saveRunBlockRerouteState(RunBlockRerouteState rerouteState) {
+ if (redisUtil == null
+ || rerouteState == null
+ || rerouteState.getTaskNo() == null
+ || rerouteState.getTaskNo() <= 0
+ || rerouteState.getBlockStationId() == null
+ || rerouteState.getBlockStationId() <= 0) {
+ return;
+ }
+ rerouteState.normalize();
+ redisUtil.set(
+ buildRunBlockRerouteStateKey(rerouteState.getTaskNo(), rerouteState.getBlockStationId()),
+ JSON.toJSONString(rerouteState),
+ RUN_BLOCK_REROUTE_STATE_EXPIRE_SECONDS
+ );
+ }
+
+ private String buildRunBlockRerouteStateKey(Integer taskNo, Integer blockStationId) {
+ return RedisKeyType.STATION_RUN_BLOCK_REROUTE_STATE_.key + taskNo + "_" + blockStationId;
+ }
+
+ private int countCurrentLoopStationHit(List<Integer> path, Set<Integer> currentLoopStationIdSet) {
+ if (path == null || path.isEmpty() || currentLoopStationIdSet == null || currentLoopStationIdSet.isEmpty()) {
+ return 0;
+ }
+ int hitCount = 0;
+ for (Integer stationId : path) {
+ if (stationId != null && currentLoopStationIdSet.contains(stationId)) {
+ hitCount++;
+ }
+ }
+ return hitCount;
+ }
+
+ private String buildPathSignature(List<Integer> path) {
+ if (path == null || path.isEmpty()) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ for (Integer stationNo : path) {
+ if (stationNo == null) {
+ continue;
+ }
+ if (builder.length() > 0) {
+ builder.append("->");
+ }
+ builder.append(stationNo);
+ }
+ return builder.toString();
+ }
+
+ private int safeInt(Integer value) {
+ return value == null ? 0 : value;
+ }
+
+ private List<List<Integer>> copyRoutePathList(List<List<Integer>> source) {
+ List<List<Integer>> copy = new ArrayList<>();
+ if (source == null || source.isEmpty()) {
+ return copy;
+ }
+ for (List<Integer> route : source) {
+ copy.add(route == null ? new ArrayList<>() : new ArrayList<>(route));
+ }
+ return copy;
+ }
+
+ @Data
+ public static class PlanResult {
+ private final StationCommand command;
+ private final int planCount;
+ private final List<List<Integer>> issuedRoutePathList;
+ }
+
+ @Data
+ private static class RunBlockRerouteState {
+ private Integer taskNo;
+ private Integer blockStationId;
+ private Integer planCount = 0;
+ private List<List<Integer>> issuedRoutePathList = new ArrayList<>();
+ private List<Integer> lastSelectedRoute = new ArrayList<>();
+ private Set<String> issuedRouteSignatureSet = new LinkedHashSet<>();
+ private Map<String, Integer> routeIssueCountMap = new HashMap<>();
+
+ private RunBlockRerouteState normalize() {
+ if (planCount == null || planCount < 0) {
+ planCount = 0;
+ }
+ if (issuedRoutePathList == null) {
+ issuedRoutePathList = new ArrayList<>();
+ }
+ if (lastSelectedRoute == null) {
+ lastSelectedRoute = new ArrayList<>();
+ }
+ if (issuedRouteSignatureSet == null) {
+ issuedRouteSignatureSet = new LinkedHashSet<>();
+ }
+ if (routeIssueCountMap == null) {
+ routeIssueCountMap = new HashMap<>();
+ }
+ for (List<Integer> routePath : issuedRoutePathList) {
+ if (routePath == null || routePath.isEmpty()) {
+ continue;
+ }
+ String pathSignature = buildPathSignatureText(routePath);
+ if (!Cools.isEmpty(pathSignature)) {
+ issuedRouteSignatureSet.add(pathSignature);
+ routeIssueCountMap.putIfAbsent(pathSignature, 1);
+ }
+ }
+ return this;
+ }
+
+ private void resetIssuedRoutes() {
+ this.issuedRoutePathList = new ArrayList<>();
+ this.lastSelectedRoute = new ArrayList<>();
+ this.issuedRouteSignatureSet = new LinkedHashSet<>();
+ }
+
+ private static String buildPathSignatureText(List<Integer> routePath) {
+ if (routePath == null || routePath.isEmpty()) {
+ return "";
+ }
+ StringBuilder builder = new StringBuilder();
+ for (Integer stationId : routePath) {
+ if (stationId == null) {
+ continue;
+ }
+ if (builder.length() > 0) {
+ builder.append("->");
+ }
+ builder.append(stationId);
+ }
+ return builder.toString();
+ }
+ }
+
+ @Data
+ private static class RerouteCandidateCommand {
+ private StationCommand command;
+ private String routeSignature;
+ private Integer pathLength;
+ private Integer issuedCount;
+ private String loopFingerprint;
+ private Boolean loopTriggered;
+ private Integer currentLoopHitCount;
+ }
+}
diff --git a/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentExecutionPlan.java b/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentExecutionPlan.java
deleted file mode 100644
index e2f6d06..0000000
--- a/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentExecutionPlan.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.zy.core.thread.impl.v5;
-
-import com.zy.core.model.command.StationCommand;
-import lombok.Data;
-
-import java.util.ArrayList;
-import java.util.List;
-
-@Data
-public class StationV5SegmentExecutionPlan {
-
- private List<Integer> fullPathStationIds = new ArrayList<>();
-
- private List<StationCommand> segmentCommands = new ArrayList<>();
-
- public int getTotalSegmentCount() {
- return segmentCommands == null ? 0 : segmentCommands.size();
- }
-}
diff --git a/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentPlanner.java b/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentPlanner.java
deleted file mode 100644
index ece2fbf..0000000
--- a/src/main/java/com/zy/core/thread/impl/v5/StationV5SegmentPlanner.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.zy.core.thread.impl.v5;
-
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.serializer.SerializerFeature;
-import com.zy.core.model.command.StationCommand;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-public class StationV5SegmentPlanner {
-
- public StationV5SegmentExecutionPlan buildPlan(StationCommand original) {
- StationV5SegmentExecutionPlan plan = new StationV5SegmentExecutionPlan();
- if (original == null) {
- return plan;
- }
-
- List<Integer> path = copyIntegerList(original.getNavigatePath());
- List<Integer> liftTransferPath = copyIntegerList(original.getLiftTransferPath());
- Integer startStationId = original.getStationId();
- Integer targetStationId = original.getTargetStaNo();
-
- if ((path == null || path.isEmpty()) && Objects.equals(startStationId, targetStationId) && startStationId != null) {
- path = new ArrayList<>();
- path.add(startStationId);
- }
-
- if (path == null || path.isEmpty()) {
- return plan;
- }
-
- plan.setFullPathStationIds(copyIntegerList(path));
-
- int total = path.size();
- List<Integer> segmentEndIndices = new ArrayList<>();
- if (liftTransferPath != null) {
- for (Integer liftTransferStationId : liftTransferPath) {
- int endIndex = path.indexOf(liftTransferStationId);
- if (endIndex <= 0) {
- continue;
- }
- if (segmentEndIndices.isEmpty() || endIndex > segmentEndIndices.get(segmentEndIndices.size() - 1)) {
- segmentEndIndices.add(endIndex);
- }
- }
- }
- if (segmentEndIndices.isEmpty() || segmentEndIndices.get(segmentEndIndices.size() - 1) != total - 1) {
- segmentEndIndices.add(total - 1);
- }
-
- List<StationCommand> segmentCommands = new ArrayList<>();
- int buildStartIdx = 0;
- for (Integer endIdx : segmentEndIndices) {
- if (endIdx == null || endIdx < buildStartIdx) {
- continue;
- }
- List<Integer> segmentPath = new ArrayList<>(path.subList(buildStartIdx, endIdx + 1));
- if (segmentPath.isEmpty()) {
- buildStartIdx = endIdx + 1;
- continue;
- }
-
- StationCommand segmentCommand = new StationCommand();
- segmentCommand.setTaskNo(original.getTaskNo());
- segmentCommand.setCommandType(original.getCommandType());
- segmentCommand.setPalletSize(original.getPalletSize());
- segmentCommand.setBarcode(original.getBarcode());
- segmentCommand.setOriginalNavigatePath(copyIntegerList(path));
- segmentCommand.setNavigatePath(segmentPath);
- segmentCommand.setStationId(segmentPath.get(0));
- segmentCommand.setTargetStaNo(segmentPath.get(segmentPath.size() - 1));
- segmentCommand.setSegmentStartIndex(buildStartIdx);
- segmentCommand.setSegmentEndIndex(endIdx);
- segmentCommands.add(segmentCommand);
-
- buildStartIdx = endIdx;
- }
-
- int segmentCount = segmentCommands.size();
- for (int i = 0; i < segmentCommands.size(); i++) {
- StationCommand segmentCommand = segmentCommands.get(i);
- segmentCommand.setSegmentNo(i + 1);
- segmentCommand.setSegmentCount(segmentCount);
- }
- plan.setSegmentCommands(segmentCommands);
- return plan;
- }
-
- private List<Integer> copyIntegerList(List<Integer> source) {
- if (source == null) {
- return new ArrayList<>();
- }
- return JSON.parseArray(JSON.toJSONString(source, SerializerFeature.DisableCircularReferenceDetect), Integer.class);
- }
-}
diff --git a/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java b/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java
new file mode 100644
index 0000000..fdd6f3a
--- /dev/null
+++ b/src/main/java/com/zy/core/thread/impl/v5/StationV5StatusReader.java
@@ -0,0 +1,127 @@
+package com.zy.core.thread.impl.v5;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.core.common.Cools;
+import com.core.common.DateUtils;
+import com.core.common.SpringUtils;
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.DeviceConfig;
+import com.zy.asrs.entity.DeviceDataLog;
+import com.zy.asrs.service.BasDevpService;
+import com.zy.asrs.utils.Utils;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.cache.OutputQueue;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.network.ZyStationConnectDriver;
+import com.zy.core.network.entity.ZyStationStatusEntity;
+import com.zy.core.thread.support.RecentStationArrivalTracker;
+import com.zy.core.utils.DeviceLogRedisKeyBuilder;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+public class StationV5StatusReader {
+
+ private final DeviceConfig deviceConfig;
+ private final RedisUtil redisUtil;
+ private final RecentStationArrivalTracker recentArrivalTracker;
+ private final List<StationProtocol> statusList = new ArrayList<>();
+ private boolean initialized = false;
+ private long deviceDataLogTime = System.currentTimeMillis();
+
+ public StationV5StatusReader(DeviceConfig deviceConfig,
+ RedisUtil redisUtil,
+ RecentStationArrivalTracker recentArrivalTracker) {
+ this.deviceConfig = deviceConfig;
+ this.redisUtil = redisUtil;
+ this.recentArrivalTracker = recentArrivalTracker;
+ }
+
+ public void readStatus(ZyStationConnectDriver zyStationConnectDriver) {
+ if (zyStationConnectDriver == null) {
+ return;
+ }
+
+ if (statusList.isEmpty()) {
+ BasDevpService basDevpService = null;
+ try {
+ basDevpService = SpringUtils.getBean(BasDevpService.class);
+ } catch (Exception ignore) {
+ }
+ if (basDevpService == null) {
+ return;
+ }
+
+ BasDevp basDevp = basDevpService
+ .getOne(new QueryWrapper<BasDevp>().eq("devp_no", deviceConfig.getDeviceNo()));
+ if (basDevp == null) {
+ return;
+ }
+
+ List<ZyStationStatusEntity> list = JSONObject.parseArray(basDevp.getStationList(), ZyStationStatusEntity.class);
+ for (ZyStationStatusEntity entity : list) {
+ StationProtocol stationProtocol = new StationProtocol();
+ stationProtocol.setStationId(entity.getStationId());
+ statusList.add(stationProtocol);
+ }
+ initialized = true;
+ }
+
+ int deviceLogCollectTime = initialized ? Utils.getDeviceLogCollectTime() : 200;
+ List<ZyStationStatusEntity> zyStationStatusEntities = zyStationConnectDriver.getStatus();
+ for (ZyStationStatusEntity statusEntity : zyStationStatusEntities) {
+ for (StationProtocol stationProtocol : statusList) {
+ if (stationProtocol.getStationId().equals(statusEntity.getStationId())) {
+ stationProtocol.setTaskNo(statusEntity.getTaskNo());
+ stationProtocol.setTargetStaNo(statusEntity.getTargetStaNo());
+ stationProtocol.setAutoing(statusEntity.isAutoing());
+ stationProtocol.setLoading(statusEntity.isLoading());
+ stationProtocol.setInEnable(statusEntity.isInEnable());
+ stationProtocol.setOutEnable(statusEntity.isOutEnable());
+ stationProtocol.setEmptyMk(statusEntity.isEmptyMk());
+ stationProtocol.setFullPlt(statusEntity.isFullPlt());
+ stationProtocol.setPalletHeight(statusEntity.getPalletHeight());
+ stationProtocol.setError(statusEntity.getError());
+ stationProtocol.setErrorMsg(statusEntity.getErrorMsg());
+ stationProtocol.setBarcode(statusEntity.getBarcode());
+ stationProtocol.setRunBlock(statusEntity.isRunBlock());
+ stationProtocol.setEnableIn(statusEntity.isEnableIn());
+ stationProtocol.setWeight(statusEntity.getWeight());
+ stationProtocol.setTaskWriteIdx(statusEntity.getTaskWriteIdx());
+ stationProtocol.setTaskBufferItems(statusEntity.getTaskBufferItems());
+ recentArrivalTracker.observe(statusEntity.getStationId(), statusEntity.getTaskNo(), statusEntity.isLoading());
+ }
+
+ if (!Cools.isEmpty(stationProtocol.getSystemWarning())) {
+ if (stationProtocol.isAutoing() && !stationProtocol.isLoading()) {
+ stationProtocol.setSystemWarning("");
+ }
+ }
+ }
+ }
+
+ OutputQueue.DEVP.offer(MessageFormat.format("銆恵0}銆慬id:{1}] <<<<< 瀹炴椂鏁版嵁鏇存柊鎴愬姛",
+ DateUtils.convert(new Date()), deviceConfig.getDeviceNo()));
+
+ if (System.currentTimeMillis() - deviceDataLogTime > deviceLogCollectTime) {
+ DeviceDataLog deviceDataLog = new DeviceDataLog();
+ deviceDataLog.setOriginData(JSON.toJSONString(zyStationStatusEntities));
+ deviceDataLog.setWcsData(JSON.toJSONString(statusList));
+ deviceDataLog.setType(String.valueOf(SlaveType.Devp));
+ deviceDataLog.setDeviceNo(deviceConfig.getDeviceNo());
+ deviceDataLog.setCreateTime(new Date());
+
+ redisUtil.set(DeviceLogRedisKeyBuilder.build(deviceDataLog), deviceDataLog, 60 * 60 * 24);
+ deviceDataLogTime = System.currentTimeMillis();
+ }
+ }
+
+ public List<StationProtocol> getStatusList() {
+ return statusList;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
index 951c437..834800b 100644
--- a/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/DualCrnOperateProcessUtils.java
@@ -24,6 +24,7 @@
import com.zy.core.News;
import com.zy.core.cache.MessageQueue;
import com.zy.core.cache.SlaveConnection;
+import com.zy.core.dispatch.StationCommandDispatcher;
import com.zy.core.enums.*;
import com.zy.core.model.StationObjModel;
import com.zy.core.model.Task;
@@ -69,6 +70,8 @@
private StationOperateProcessUtils stationOperateProcessUtils;
@Autowired
private WrkAnalysisService wrkAnalysisService;
+ @Autowired
+ private StationCommandDispatcher stationCommandDispatcher;
private static final String CRN_OUT_REQUIRE_STATION_OUT_ENABLE_CONFIG = "crnOutRequireStationOutEnable";
@@ -817,7 +820,7 @@
}
//鐢熸垚浠跨湡绔欑偣鏁版嵁
StationCommand command = stationThread.getCommand(StationCommandType.WRITE_INFO, 9998, wrkMast.getSourceStaNo(), 0, 0);
- MessageQueue.offer(SlaveType.Devp, stationObjModel.getDeviceNo(), new Task(2, command));
+ stationCommandDispatcher.dispatch(stationObjModel.getDeviceNo(), command, "dual-crn-operate-process", "fake-out-complete-write-info");
}
}
diff --git a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
index e3ece21..85dc30d 100644
--- a/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
+++ b/src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
@@ -1,586 +1,74 @@
package com.zy.core.utils;
-import com.alibaba.fastjson.JSON;
-import com.alibaba.fastjson.JSONObject;
-import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
-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.*;
-import com.zy.asrs.service.*;
-import com.zy.asrs.utils.NotifyUtils;
-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.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;
-import com.zy.core.enums.*;
-import com.zy.core.model.StationObjModel;
-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 com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.core.enums.WrkIoType;
+import com.zy.core.enums.WrkStsType;
+import com.zy.core.utils.station.StationDispatchLoadSupport;
+import com.zy.core.utils.station.StationOutboundDispatchProcessor;
+import com.zy.core.utils.station.StationRegularDispatchProcessor;
+import com.zy.core.utils.station.StationRerouteProcessor;
+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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import java.util.*;
+import java.util.List;
@Component
public class StationOperateProcessUtils {
- private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120;
- 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;
@Autowired
private WrkMastService wrkMastService;
@Autowired
- private CommonService commonService;
+ private StationRegularDispatchProcessor stationRegularDispatchProcessor;
@Autowired
- private RedisUtil redisUtil;
+ private StationDispatchLoadSupport stationDispatchLoadSupport;
@Autowired
- private LocMastService locMastService;
+ private StationOutboundDispatchProcessor stationOutboundDispatchProcessor;
@Autowired
- private WmsOperateUtils wmsOperateUtils;
- @Autowired
- private NotifyUtils notifyUtils;
- @Autowired
- private NavigateUtils navigateUtils;
- @Autowired
- 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;
+ private StationRerouteProcessor stationRerouteProcessor;
//鎵ц杈撻�佺珯鐐瑰叆搴撲换鍔�
public synchronized void stationInExecute() {
- try {
- DispatchLimitConfig baseLimitConfig = getDispatchLimitConfig(null, null);
- int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()};
- LoadGuardState loadGuardState = buildLoadGuardState(baseLimitConfig);
-
- List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>());
- for (BasDevp basDevp : basDevps) {
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
- if (stationThread == null) {
- continue;
- }
-
- Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
-
- List<StationObjModel> list = basDevp.getBarcodeStationList$();
- for (StationObjModel entity : list) {
- Integer stationId = entity.getStationId();
- if (!stationMap.containsKey(stationId)) {
- continue;
- }
-
- StationProtocol stationProtocol = stationMap.get(stationId);
- if (stationProtocol == null) {
- continue;
- }
-
- Object lock = redisUtil.get(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId);
- if (lock != null) {
- continue;
- }
-
- //婊¤冻鑷姩銆佹湁鐗┿�佹湁宸ヤ綔鍙�
- if (stationProtocol.isAutoing()
- && stationProtocol.isLoading()
- && stationProtocol.getTaskNo() > 0
- ) {
- //妫�娴嬩换鍔℃槸鍚︾敓鎴�
- WrkMast wrkMast = wrkMastService.getOne(new QueryWrapper<WrkMast>().eq("barcode", stationProtocol.getBarcode()));
- if (wrkMast == null) {
- continue;
- }
-
- if (!Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_INBOUND.sts)) {
- continue;
- }
-
- String locNo = wrkMast.getLocNo();
- FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
- if (findCrnNoResult == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鏈尮閰嶅埌鍫嗗灈鏈�", wrkMast.getWrkNo());
- continue;
- }
-
- Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId);
- if (targetStationId == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}绔欑偣,鎼滅储鍏ュ簱绔欑偣澶辫触", stationId);
- continue;
- }
-
- DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), targetStationId);
- LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), targetStationId, loadGuardState);
-
- if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
- return;
- }
-
- StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, targetStationId, 0);
- if (command == null) {
- News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鑾峰彇杈撻�佺嚎鍛戒护澶辫触", wrkMast.getWrkNo());
- continue;
- }
-
- Date now = new Date();
- wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
- wrkMast.setSourceStaNo(stationProtocol.getStationId());
- wrkMast.setStaNo(targetStationId);
- wrkMast.setSystemMsg("");
- wrkMast.setIoTime(now);
- wrkMast.setModiTime(now);
- if (wrkMastService.updateById(wrkMast)) {
- 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());
- saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
- }
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
+ stationRegularDispatchProcessor.stationInExecute();
}
//鎵ц鍫嗗灈鏈鸿緭閫佺珯鐐瑰嚭搴撲换鍔�
public synchronized void crnStationOutExecute() {
- try {
- DispatchLimitConfig baseLimitConfig = getDispatchLimitConfig(null, null);
- int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()};
- LoadGuardState loadGuardState = buildLoadGuardState(baseLimitConfig);
-
- List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
- .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
- .isNotNull("crn_no")
- );
- List<Integer> outOrderList = getAllOutOrderList();
-
- for (WrkMast wrkMast : wrkMasts) {
- Object infoObj = redisUtil.get(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo());
- if (infoObj == null) {
- News.info("鍑哄簱浠诲姟{}鏁版嵁缂撳瓨涓嶅瓨鍦�", wrkMast.getWrkNo());
- continue;
- }
-
- StationObjModel stationObjModel = JSON.parseObject(infoObj.toString(), StationObjModel.class);
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
- if (stationThread == null) {
- continue;
- }
-
- Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
- StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
- if (stationProtocol == null) {
- continue;
- }
-
- Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
- if (lock != null) {
- continue;
- }
-
- //婊¤冻鑷姩銆佹湁鐗┿�佸伐浣滃彿0
- if (stationProtocol.isAutoing()
- && stationProtocol.isLoading()
- && stationProtocol.getTaskNo() == 0
- ) {
- // 鍏堢畻褰撳墠浠诲姟鍦ㄦ壒娆″嚭搴撲腑鐨勮矾寰勫�惧悜绯绘暟锛屽啀甯︾潃杩欎釜绯绘暟鍘诲喅绛栫洰鏍囩珯锛�
- // 杩欐牱鍚屼竴鎵规涓嶅悓搴忓彿浠诲姟鍦ㄦ帓搴忕偣銆佺粫鍦堢偣鍜屽牭濉為噸绠楁椂浼氬緱鍒颁竴鑷寸殑鐩爣瑁佸喅銆�
- Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
- OutOrderDispatchDecision dispatchDecision = resolveOutboundDispatchDecision(
- stationProtocol.getStationId(),
- wrkMast,
- outOrderList,
- pathLenFactor
- );
- Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId();
- if (moveStaNo == null) {
- continue;
- }
-
- DispatchLimitConfig limitConfig = getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo);
- LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState, wrkMast, pathLenFactor);
-
- if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
- return;
- }
-
- 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(now);
- wrkMast.setModiTime(now);
- if (wrkMastService.updateById(wrkMast)) {
- 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());
- currentStationTaskCountRef[0]++;
- loadGuardState.reserveLoopTask(loopHitResult.getLoopNo());
- saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
+ stationOutboundDispatchProcessor.crnStationOutExecute();
}
//鎵ц鍙屽伐浣嶅爢鍨涙満杈撻�佺珯鐐瑰嚭搴撲换鍔�
public synchronized void dualCrnStationOutExecute() {
- try {
- List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
- .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
- .isNotNull("dual_crn_no")
- );
- for (WrkMast wrkMast : wrkMasts) {
- Object infoObj = redisUtil.get(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo());
- if (infoObj == null) {
- News.info("鍑哄簱浠诲姟{}鏁版嵁缂撳瓨涓嶅瓨鍦�", wrkMast.getWrkNo());
- continue;
- }
-
- StationObjModel stationObjModel = JSON.parseObject(infoObj.toString(), StationObjModel.class);
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
- if (stationThread == null) {
- continue;
- }
-
- Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
- StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
- if (stationProtocol == null) {
- continue;
- }
-
- Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
- if (lock != null) {
- continue;
- }
-
- //婊¤冻鑷姩銆佹湁鐗┿�佸伐浣滃彿0
- if (stationProtocol.isAutoing()
- && stationProtocol.isLoading()
- && stationProtocol.getTaskNo() == 0
- ) {
- Double pathLenFactor = resolveOutboundPathLenFactor(wrkMast);
- StationCommand command = buildOutboundMoveCommand(
- stationThread,
- wrkMast,
- stationProtocol.getStationId(),
- wrkMast.getStaNo(),
- pathLenFactor
- );
- if (command == null) {
- News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
- continue;
- }
-
- wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts);
- wrkMast.setSystemMsg("");
- wrkMast.setIoTime(new Date());
- if (wrkMastService.updateById(wrkMast)) {
- 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);
- redisUtil.del(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo());
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
+ stationOutboundDispatchProcessor.dualCrnStationOutExecute();
}
//妫�娴嬭緭閫佺珯鐐瑰嚭搴撲换鍔℃墽琛屽畬鎴�
public synchronized void stationOutExecuteFinish() {
- try {
- List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN.sts));
- for (WrkMast wrkMast : wrkMasts) {
- Integer wrkNo = wrkMast.getWrkNo();
- Integer targetStaNo = wrkMast.getStaNo();
- if (wrkNo == null || targetStaNo == null) {
- continue;
- }
-
- boolean complete = false;
- Integer targetDeviceNo = null;
- StationThread stationThread = null;
- BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", targetStaNo));
- if (basStation != null) {
- targetDeviceNo = basStation.getDeviceNo();
- 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) {
- attemptClearTaskPath(stationThread, wrkNo);
- completeStationRunTask(wrkMast, targetDeviceNo);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void attemptClearTaskPath(StationThread stationThread, Integer taskNo) {
- if (stationThread == null || taskNo == null || taskNo <= 0) {
- return;
- }
- try {
- boolean cleared = stationThread.clearPath(taskNo);
- if (cleared) {
- News.info("杈撻�佺珯鐐逛换鍔¤繍琛屽畬鎴愬悗娓呯悊娈嬬暀璺緞锛屽伐浣滃彿={}", taskNo);
- }
- } catch (Exception e) {
- News.error("杈撻�佺珯鐐逛换鍔¤繍琛屽畬鎴愬悗娓呯悊娈嬬暀璺緞寮傚父锛屽伐浣滃彿={}", taskNo, e);
- }
- }
-
- 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);
+ stationRegularDispatchProcessor.stationOutExecuteFinish();
}
// 妫�娴嬩换鍔¤浆瀹屾垚
public synchronized void checkTaskToComplete() {
- try {
- List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN_COMPLETE.sts));
- for (WrkMast wrkMast : wrkMasts) {
- Integer wrkNo = wrkMast.getWrkNo();
- Integer targetStaNo = wrkMast.getStaNo();
-
- Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkNo);
- if (lock != null) {
- continue;
- }
-
- boolean complete = false;
- 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 (complete) {
- if (stationMoveCoordinator != null) {
- stationMoveCoordinator.finishSession(wrkNo);
- }
- wrkMast.setWrkSts(WrkStsType.COMPLETE_OUTBOUND.sts);
- wrkMast.setIoTime(new Date());
- wrkMastService.updateById(wrkMast);
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
+ stationRegularDispatchProcessor.checkTaskToComplete();
}
//妫�娴嬭緭閫佺珯鐐规槸鍚﹁繍琛屽牭濉�
public synchronized void checkStationRunBlock() {
- 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<Integer> runBlockReassignLocStationList = new ArrayList<>();
- for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) {
- runBlockReassignLocStationList.add(stationObjModel.getStationId());
- }
- List<Integer> outOrderStationIds = basDevp.getOutOrderIntList();
-
- List<StationProtocol> list = stationThread.getStatus();
- for (StationProtocol stationProtocol : list) {
- if (stationProtocol.isAutoing()
- && stationProtocol.isLoading()
- && stationProtocol.getTaskNo() > 0
- && stationProtocol.isRunBlock()
- ) {
- WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
- if (wrkMast == null) {
- News.info("杈撻�佺珯鐐瑰彿={} 杩愯闃诲锛屼絾鏃犳硶鎵惧埌瀵瑰簲浠诲姟锛屽伐浣滃彿={}", stationProtocol.getStationId(), stationProtocol.getTaskNo());
- continue;
- }
-
- Object lock = redisUtil.get(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo());
- if (lock != null) {
- continue;
- }
- redisUtil.set(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo(), "lock", 15);
-
- 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();
- }
+ stationRerouteProcessor.checkStationRunBlock();
}
//妫�娴嬭緭閫佺珯鐐逛换鍔″仠鐣欒秴鏃跺悗閲嶆柊璁$畻璺緞
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());
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
+ stationRerouteProcessor.checkStationIdleRecover();
}
//鑾峰彇杈撻�佺嚎浠诲姟鏁伴噺
public synchronized int getCurrentStationTaskCount() {
- return countCurrentStationTask();
+ return stationDispatchLoadSupport.countCurrentStationTask();
}
public synchronized int getCurrentOutboundTaskCountByTargetStation(Integer stationId) {
@@ -598,2457 +86,35 @@
// 妫�娴嬪嚭搴撴帓搴�
public synchronized void checkStationOutOrder() {
- List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
- for (BasDevp basDevp : basDevps) {
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
- if (stationThread == null) {
- continue;
- }
- Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
- List<StationObjModel> orderList = basDevp.getOutOrderList$();
- List<Integer> outOrderStationIds = basDevp.getOutOrderIntList();
- for (StationObjModel stationObjModel : orderList) {
- StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId());
- if (stationProtocol == null) {
- continue;
- }
-
- if (!stationProtocol.isAutoing()) {
- continue;
- }
-
- if (!stationProtocol.isLoading()) {
- continue;
- }
-
- if (stationProtocol.getTaskNo() <= 0) {
- continue;
- }
-
- // 鎺掑簭鐐规湰韬凡缁忓牭濉炴椂锛屼笉鍦� out-order 閲屽仛浜屾鍐崇瓥锛岀粺涓�浜ょ粰 run-block 閲嶈鍒掑鐞嗐��
- if (stationProtocol.isRunBlock()) {
- continue;
- }
-
- if (!stationProtocol.getStationId().equals(stationProtocol.getTargetStaNo())) {
- continue;
- }
-
- WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
- if (wrkMast == null) {
- continue;
- }
- 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;
- }
-
- 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);
- }
- }
+ stationRerouteProcessor.checkStationOutOrder();
}
// 鐩戞帶缁曞湀绔欑偣
public synchronized void watchCircleStation() {
- List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
- for (BasDevp basDevp : basDevps) {
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
- if (stationThread == null) {
- continue;
- }
-
- List<Integer> outOrderList = basDevp.getOutOrderIntList();
-
- for (StationProtocol stationProtocol : stationThread.getStatus()) {
- if (!stationProtocol.isAutoing()) {
- continue;
- }
-
- if (!stationProtocol.isLoading()) {
- continue;
- }
-
- if (stationProtocol.getTaskNo() <= 0) {
- continue;
- }
-
- // 缁曞湀瑙﹀彂浼樺厛璇� session 鐨勪笅涓�鍐崇瓥绔欙紝legacy WATCH_CIRCLE key 鍙仛鍏煎鍥為��銆�
- if (!isWatchingCircleArrival(stationProtocol.getTaskNo(), stationProtocol.getStationId())) {
- continue;
- }
-
- WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
- if (wrkMast == null) {
- continue;
- }
- 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,
- 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)
- );
+ stationRerouteProcessor.watchCircleStation();
}
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());
+ return stationRerouteProcessor.buildRerouteCommandPlan(context, decision);
}
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);
- }
-
- private RerouteExecutionResult executeReroutePlanWithTaskLock(RerouteContext context,
- RerouteCommandPlan plan,
- StationProtocol stationProtocol,
- Integer taskNo,
- Integer stationId) {
- boolean runBlockReroute = context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE;
- 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 (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
- // 鍒囪矾鍓嶅厛鎶婃棫 session 缃负 CANCEL_PENDING锛岃宸茬粡鎺掗槦涓殑鏃у垎娈电嚎绋嬪湪鏈�缁堝彂閫佸墠鍋滀笅銆�
- stationMoveCoordinator.markCancelPending(taskNo, "reroute_pending");
- }
-
- if (runBlockReroute) {
- // 绔欑偣杩涘叆鍫靛鍚庯紝璁惧渚у彲鑳藉凡缁忔妸涔嬪墠棰勪笅鍙戠殑鍒嗘鍛戒护娓呮帀浜嗐��
- // 鍏堜綔搴熸湰鍦� session/segment 鐘舵�侊紝鍐嶆寜鏂拌矾绾块噸鍙戯紝閬垮厤琚棫鐘舵�佸弽鍚戝崱浣忋��
- if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
- stationMoveCoordinator.cancelSession(taskNo);
- }
- if (context.resetSegmentCommandsBeforeDispatch()) {
- resetSegmentMoveCommandsBeforeReroute(taskNo);
- }
- }
-
- 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);
+ return stationRerouteProcessor.executeReroutePlan(context, plan);
}
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() {
- List<Integer> list = new ArrayList<>();
- List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
- for (BasDevp basDevp : basDevps) {
- List<Integer> orderList = basDevp.getOutOrderIntList();
- list.addAll(orderList);
- }
- return list;
- }
-
- /**
- * 缁熶竴璁$畻褰撳墠浠诲姟鈥滄鍒诲簲璇ユ湞鍝釜鐩爣绔欑户缁繍琛屸�濄��
- *
- * <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,
- Double pathLenFactor) {
- if (finalTargetStationId == null) {
- return null;
- }
- if (sourceStationId == null || outOrderList == null || outOrderList.isEmpty()) {
- return finalTargetStationId;
- }
-
- try {
- 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 (Objects.equals(stationId, finalTargetStationId)) {
- continue;
- }
- if (outOrderList.contains(stationId)) {
- return stationId;
- }
- }
- } catch (Exception ignore) {}
- return finalTargetStationId;
- }
-
- /**
- * 鍒ゆ柇浠庡綋鍓嶆帓搴忕偣缁х画鍓嶅線鏈�缁堜笟鍔$洰鏍囩珯鏃讹紝璺緞涓婃槸鍚﹁嚦灏戝瓨鍦ㄤ竴涓彲杩涘叆鐨勫悗缁珯鐐广��
- *
- * <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);
- if (candidateStationId == null || currentStationId.equals(candidateStationId)) {
- continue;
- }
- try {
- List<NavigateNode> path = calcOutboundNavigatePath(wrkMast, currentStationId, candidateStationId, pathLenFactor);
- if (path != null && !path.isEmpty()) {
- candidateList.add(new CircleTargetCandidate(candidateStationId, path.size(), offset));
- }
- } catch (Exception ignore) {}
- }
- 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) {
- if (wrkNo == null || wrkNo <= 0 || stationId == null) {
- return true;
- }
- String key = RedisKeyType.STATION_OUT_ORDER_DISPATCH_LIMIT_.key + wrkNo + "_" + stationId;
- Object lock = redisUtil.get(key);
- if (lock != null) {
- return false;
- }
- redisUtil.set(key, "lock", OUT_ORDER_DISPATCH_LIMIT_SECONDS);
- return true;
- }
-
- private boolean 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) {
- if (wrkNo == null || wrkNo <= 0) {
- return null;
- }
- Object circleObj = redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo);
- if (circleObj == null) {
- return null;
- }
- try {
- return JSON.parseObject(circleObj.toString(), StationCommand.class);
- } catch (Exception ignore) {
- return null;
- }
- }
-
- private void saveWatchCircleCommand(Integer wrkNo, StationCommand command) {
- if (wrkNo == null || wrkNo <= 0 || command == null) {
- return;
- }
- redisUtil.set(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo,
- JSON.toJSONString(command, SerializerFeature.DisableCircularReferenceDetect), 60 * 60 * 24);
- }
-
- private void clearWatchCircleCommand(Integer wrkNo) {
- if (wrkNo == null || wrkNo <= 0) {
- return;
- }
- redisUtil.del(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo);
- }
-
- private void checkStationIdleRecover(BasDevp basDevp,
- StationThread stationThread,
- StationProtocol stationProtocol,
- List<Integer> outOrderList) {
- if (stationProtocol == null || stationProtocol.getTaskNo() == null || stationProtocol.getTaskNo() <= 0) {
- return;
- }
- if (!Objects.equals(stationProtocol.getStationId(), stationProtocol.getTargetStaNo())) {
- return;
- }
-
- StationTaskIdleTrack idleTrack = touchStationTaskIdleTrack(stationProtocol.getTaskNo(), stationProtocol.getStationId());
- if (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);
+ return stationRerouteProcessor.resolveSharedRerouteDecision(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);
+ return stationRerouteProcessor.shouldUseRunBlockDirectReassign(wrkMast, stationId, runBlockReassignLocStationList);
}
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;
+ return stationRerouteProcessor.shouldSkipIdleRecoverForRecentDispatch(taskNo, stationId);
}
-
- 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);
- JSONObject v = JSONObject.parseObject(node.getNodeValue());
- if (v != null) {
- Integer stationId = v.getInteger("stationId");
- if (searchStationId.equals(stationId)) {
- break;
- } else {
- checkList.add(stationId);
- }
- }
- }
-
- // 涓嬫父绔欑偣鍙兘鍚屾椂鎸傜潃澶氫釜涓嶅悓鎵规浠诲姟锛涜繖閲屽彧鎶藉彇涓庡綋鍓嶆壒娆$浉鍏崇殑鐜板満搴忓彿蹇収銆�
- HashMap<String, Integer> batchMap = new HashMap<>();
- for (Integer station : checkList) {
- BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", station));
- if (basStation == null) {
- continue;
- }
-
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
- if (stationThread == null) {
- continue;
- }
- Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
- StationProtocol checkStationProtocol = statusMap.get(station);
- if (checkStationProtocol == null) {
- continue;
- }
- if (checkStationProtocol.getTaskNo() > 0) {
- WrkMast checkWrkMast = wrkMastService.selectByWorkNo(checkStationProtocol.getTaskNo());
- if (checkWrkMast == null) {
- continue;
- }
-
- if (!Cools.isEmpty(checkWrkMast.getBatch())) {
- batchMap.put(checkWrkMast.getBatch(), checkWrkMast.getBatchSeq());
- }
- }
- }
-
- Integer seq = batchMap.get(searchBatch);
- return seq;
- }
-
- private int countCurrentStationTask() {
- int currentStationTaskCount = 0;
- List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
- for (BasDevp basDevp : basDevps) {
- StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
- if (stationThread == null) {
- continue;
- }
-
- for (StationProtocol stationProtocol : stationThread.getStatus()) {
- if (stationProtocol.getTaskNo() > 0) {
- currentStationTaskCount++;
- }
- }
- }
- return currentStationTaskCount;
- }
-
- private boolean isDispatchBlocked(DispatchLimitConfig config,
- int currentStationTaskCount,
- LoadGuardState loadGuardState,
- boolean needReserveLoopLoad) {
- if (config.loopModeEnable) {
- double currentLoad = loadGuardState.currentLoad();
- if (currentLoad >= config.circleMaxLoadLimit) {
- News.warn("褰撳墠鎵胯浇閲忚揪鍒颁笂闄愶紝宸插仠姝㈢珯鐐逛换鍔′笅鍙戙�傚綋鍓嶆壙杞介噺={}锛屼笂闄�={}", formatPercent(currentLoad), formatPercent(config.circleMaxLoadLimit));
- return true;
- }
-
- if (needReserveLoopLoad) {
- double reserveLoad = loadGuardState.loadAfterReserve();
- if (reserveLoad >= config.circleMaxLoadLimit) {
- News.warn("棰勫崰鍚庢壙杞介噺杈惧埌涓婇檺锛屽凡鍋滄绔欑偣浠诲姟涓嬪彂銆傞鍗犲悗鎵胯浇閲�={}锛屼笂闄�={}", formatPercent(reserveLoad), formatPercent(config.circleMaxLoadLimit));
- return true;
- }
- }
- }
- return false;
- }
-
- private LoadGuardState buildLoadGuardState(DispatchLimitConfig config) {
- LoadGuardState state = new LoadGuardState();
- if (!config.loopModeEnable) {
- return state;
- }
-
- StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot();
- if (capacityVo == null) {
- return state;
- }
-
- state.totalStationCount = toNonNegative(capacityVo.getTotalStationCount());
- Integer occupiedStationCount = capacityVo.getOccupiedStationCount();
- state.projectedTaskStationCount = toNonNegative(occupiedStationCount != null ? occupiedStationCount : capacityVo.getTaskStationCount());
-
- List<StationCycleLoopVo> loopList = capacityVo.getLoopList();
- if (loopList != null) {
- for (StationCycleLoopVo loopVo : loopList) {
- if (loopVo == null || loopVo.getStationIdList() == null) {
- continue;
- }
- Integer loopNo = loopVo.getLoopNo();
- for (Integer stationId : loopVo.getStationIdList()) {
- if (stationId != null) {
- if (loopNo != null) {
- state.stationLoopNoMap.put(stationId, loopNo);
- }
- }
- }
- }
- }
- return state;
- }
-
- private LoopHitResult findPathLoopHit(DispatchLimitConfig config,
- 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;
- }
- if (sourceStationId == null || targetStationId == null) {
- return LoopHitResult.NO_HIT;
- }
- if (loadGuardState.stationLoopNoMap.isEmpty()) {
- return LoopHitResult.NO_HIT;
- }
-
- try {
- List<NavigateNode> nodes = wrkMast == null
- ? navigateUtils.calcByStationId(sourceStationId, targetStationId)
- : calcOutboundNavigatePath(wrkMast, sourceStationId, targetStationId, pathLenFactor);
- if (nodes == null || nodes.isEmpty()) {
- return LoopHitResult.NO_HIT;
- }
-
- for (NavigateNode node : nodes) {
- Integer stationId = getStationIdFromNode(node);
- if (stationId == null) {
- continue;
- }
- Integer loopNo = loadGuardState.stationLoopNoMap.get(stationId);
- if (loopNo != null) {
- return new LoopHitResult(true, loopNo, stationId);
- }
- }
- } catch (Exception e) {
- return LoopHitResult.NO_HIT;
- }
-
- return LoopHitResult.NO_HIT;
- }
-
- private Integer getStationIdFromNode(NavigateNode node) {
- if (node == null || isBlank(node.getNodeValue())) {
- return null;
- }
- try {
- JSONObject v = JSONObject.parseObject(node.getNodeValue());
- if (v == null) {
- return null;
- }
- return v.getInteger("stationId");
- } catch (Exception e) {
- return null;
- }
- }
-
- private int toNonNegative(Integer value) {
- if (value == null || value < 0) {
- return 0;
- }
- 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;
- }
- JSONObject reserveJson = new JSONObject();
- reserveJson.put("wrkNo", wrkNo);
- reserveJson.put("loopNo", loopHitResult.getLoopNo());
- reserveJson.put("hitStationId", loopHitResult.getHitStationId());
- reserveJson.put("createTime", System.currentTimeMillis());
- redisUtil.hset(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, String.valueOf(wrkNo), reserveJson.toJSONString());
- redisUtil.expire(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, LOOP_LOAD_RESERVE_EXPIRE_SECONDS);
- }
-
- private DispatchLimitConfig getDispatchLimitConfig(Integer startStationId, Integer endStationId) {
- DispatchLimitConfig config = new DispatchLimitConfig();
- Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
- 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);
- }
-
- 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) {
- }
- }
-
- return config;
- }
-
- private String getConfigValue(Map<?, ?> configMap, String key) {
- Object value = configMap.get(key);
- if (value == null) {
- return null;
- }
- return String.valueOf(value).trim();
- }
-
- private boolean parseBoolean(String value, boolean defaultValue) {
- if (isBlank(value)) {
- return defaultValue;
- }
- String lowValue = value.toLowerCase(Locale.ROOT);
- if ("y".equals(lowValue) || "yes".equals(lowValue) || "true".equals(lowValue)
- || "1".equals(lowValue) || "on".equals(lowValue)) {
- return true;
- }
- if ("n".equals(lowValue) || "no".equals(lowValue) || "false".equals(lowValue)
- || "0".equals(lowValue) || "off".equals(lowValue)) {
- return false;
- }
- return defaultValue;
- }
-
- private double parseLoadLimit(String value, double defaultValue) {
- if (isBlank(value)) {
- return defaultValue;
- }
- try {
- String normalized = value.replace("%", "").trim();
- double parsed = Double.parseDouble(normalized);
- if (parsed > 1.0) {
- parsed = parsed / 100.0;
- }
- if (parsed < 0.0) {
- return 0.0;
- }
- if (parsed > 1.0) {
- return 1.0;
- }
- return parsed;
- } catch (Exception e) {
- return defaultValue;
- }
- }
-
- private int parseInt(String value, int defaultValue) {
- if (isBlank(value)) {
- return defaultValue;
- }
- try {
- int parsed = Integer.parseInt(value.trim());
- return parsed < 0 ? defaultValue : parsed;
- } catch (Exception e) {
- return defaultValue;
- }
- }
-
- private String formatPercent(double value) {
- return String.format(Locale.ROOT, "%.1f%%", value * 100.0);
- }
-
- private boolean isBlank(String value) {
- return value == null || value.trim().isEmpty();
- }
-
- private static class DispatchLimitConfig {
- // 鍦堟渶澶ф壙杞借兘鍔涳紝榛樿80%
- private double circleMaxLoadLimit = 0.8d;
- // 鏄惁鍚敤缁曞湀妯″紡锛堜粎鍚敤鏃舵墠鐢熸晥鎵胯浇闄愬埗锛�
- private boolean loopModeEnable = false;
- }
-
- private static class LoadGuardState {
- private int totalStationCount = 0;
- private int projectedTaskStationCount = 0;
- private final Map<Integer, Integer> stationLoopNoMap = new HashMap<>();
-
- private double currentLoad() {
- return calcLoad(this.projectedTaskStationCount, this.totalStationCount);
- }
-
- private double loadAfterReserve() {
- return calcLoad(this.projectedTaskStationCount + 1, this.totalStationCount);
- }
-
- private void reserveLoopTask(Integer loopNo) {
- if (loopNo == null || loopNo <= 0) {
- return;
- }
- if (this.totalStationCount <= 0) {
- return;
- }
- this.projectedTaskStationCount++;
- }
-
- private double calcLoad(int taskCount, int stationCount) {
- if (stationCount <= 0 || taskCount <= 0) {
- return 0.0;
- }
- double load = (double) taskCount / (double) stationCount;
- if (load < 0.0) {
- return 0.0;
- }
- if (load > 1.0) {
- return 1.0;
- }
- return load;
- }
- }
-
- private static class LoopHitResult {
- private static final LoopHitResult NO_HIT = new LoopHitResult(false, null, null);
- private final boolean throughLoop;
- private final Integer loopNo;
- private final Integer hitStationId;
-
- private LoopHitResult(boolean throughLoop, Integer loopNo, Integer hitStationId) {
- this.throughLoop = throughLoop;
- this.loopNo = loopNo;
- this.hitStationId = hitStationId;
- }
-
- private boolean isThroughLoop() {
- return throughLoop;
- }
-
- private Integer getLoopNo() {
- return loopNo;
- }
-
- private Integer getHitStationId() {
- return hitStationId;
- }
- }
-
- 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;
- }
- }
-
}
diff --git a/src/main/java/com/zy/core/utils/station/StationDispatchLoadSupport.java b/src/main/java/com/zy/core/utils/station/StationDispatchLoadSupport.java
new file mode 100644
index 0000000..55d951c
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationDispatchLoadSupport.java
@@ -0,0 +1,300 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSONObject;
+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.BasDevp;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasDevpService;
+import com.zy.asrs.service.StationCycleCapacityService;
+import com.zy.asrs.service.StationPathPolicyService;
+import com.zy.common.model.NavigateNode;
+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.enums.RedisKeyType;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.model.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+@Component
+public class StationDispatchLoadSupport {
+
+ private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120;
+
+ @Autowired
+ private RedisUtil redisUtil;
+ @Autowired
+ private BasDevpService basDevpService;
+ @Autowired
+ private StationCycleCapacityService stationCycleCapacityService;
+ @Autowired
+ private StationPathPolicyService stationPathPolicyService;
+ @Autowired
+ private NavigateUtils navigateUtils;
+
+ public int countCurrentStationTask() {
+ int currentStationTaskCount = 0;
+ List<BasDevp> basDevps = basDevpService.list();
+ for (BasDevp basDevp : basDevps) {
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ for (StationProtocol stationProtocol : stationThread.getStatus()) {
+ if (stationProtocol.getTaskNo() > 0) {
+ currentStationTaskCount++;
+ }
+ }
+ }
+ return currentStationTaskCount;
+ }
+
+ public boolean isDispatchBlocked(DispatchLimitConfig config,
+ int currentStationTaskCount,
+ LoadGuardState loadGuardState,
+ boolean needReserveLoopLoad) {
+ if (config != null && config.isLoopModeEnable()) {
+ double currentLoad = loadGuardState.currentLoad();
+ if (currentLoad >= config.getCircleMaxLoadLimit()) {
+ News.warn("褰撳墠鎵胯浇閲忚揪鍒颁笂闄愶紝宸插仠姝㈢珯鐐逛换鍔′笅鍙戙�傚綋鍓嶆壙杞介噺={}锛屼笂闄�={}", formatPercent(currentLoad), formatPercent(config.getCircleMaxLoadLimit()));
+ return true;
+ }
+
+ if (needReserveLoopLoad) {
+ double reserveLoad = loadGuardState.loadAfterReserve();
+ if (reserveLoad >= config.getCircleMaxLoadLimit()) {
+ News.warn("棰勫崰鍚庢壙杞介噺杈惧埌涓婇檺锛屽凡鍋滄绔欑偣浠诲姟涓嬪彂銆傞鍗犲悗鎵胯浇閲�={}锛屼笂闄�={}", formatPercent(reserveLoad), formatPercent(config.getCircleMaxLoadLimit()));
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public LoadGuardState buildLoadGuardState(DispatchLimitConfig config) {
+ LoadGuardState state = new LoadGuardState();
+ if (config == null || !config.isLoopModeEnable()) {
+ return state;
+ }
+
+ StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot();
+ if (capacityVo == null) {
+ return state;
+ }
+
+ Integer occupiedStationCount = capacityVo.getOccupiedStationCount();
+ state.setTotalStationCount(toNonNegative(capacityVo.getTotalStationCount()));
+ state.setProjectedTaskStationCount(toNonNegative(occupiedStationCount != null ? occupiedStationCount : capacityVo.getTaskStationCount()));
+
+ List<StationCycleLoopVo> loopList = capacityVo.getLoopList();
+ if (loopList != null) {
+ for (StationCycleLoopVo loopVo : loopList) {
+ if (loopVo == null || loopVo.getStationIdList() == null) {
+ continue;
+ }
+ Integer loopNo = loopVo.getLoopNo();
+ for (Integer stationId : loopVo.getStationIdList()) {
+ if (stationId != null && loopNo != null) {
+ state.putStationLoopNo(stationId, loopNo);
+ }
+ }
+ }
+ }
+ return state;
+ }
+
+ public LoopHitResult findPathLoopHit(DispatchLimitConfig config,
+ Integer sourceStationId,
+ Integer targetStationId,
+ LoadGuardState loadGuardState) {
+ return findPathLoopHit(config, sourceStationId, targetStationId, loadGuardState, null, null);
+ }
+
+ public LoopHitResult findPathLoopHit(DispatchLimitConfig config,
+ Integer sourceStationId,
+ Integer targetStationId,
+ LoadGuardState loadGuardState,
+ WrkMast wrkMast,
+ Double pathLenFactor) {
+ if (config == null || !config.isLoopModeEnable()) {
+ return LoopHitResult.noHit();
+ }
+ if (sourceStationId == null || targetStationId == null) {
+ return LoopHitResult.noHit();
+ }
+ if (loadGuardState == null || loadGuardState.getStationLoopNoMap().isEmpty()) {
+ return LoopHitResult.noHit();
+ }
+
+ try {
+ List<NavigateNode> nodes = wrkMast == null
+ ? navigateUtils.calcByStationId(sourceStationId, targetStationId)
+ : calcOutboundNavigatePath(wrkMast, sourceStationId, targetStationId, pathLenFactor);
+ if (nodes == null || nodes.isEmpty()) {
+ return LoopHitResult.noHit();
+ }
+
+ for (NavigateNode node : nodes) {
+ Integer stationId = getStationIdFromNode(node);
+ if (stationId == null) {
+ continue;
+ }
+ Integer loopNo = loadGuardState.getStationLoopNoMap().get(stationId);
+ if (loopNo != null) {
+ return new LoopHitResult(true, loopNo, stationId);
+ }
+ }
+ } catch (Exception ignore) {
+ return LoopHitResult.noHit();
+ }
+
+ return LoopHitResult.noHit();
+ }
+
+ public void saveLoopLoadReserve(Integer wrkNo, LoopHitResult loopHitResult) {
+ if (wrkNo == null || wrkNo <= 0 || loopHitResult == null || !loopHitResult.isThroughLoop()) {
+ return;
+ }
+ JSONObject reserveJson = new JSONObject();
+ reserveJson.put("wrkNo", wrkNo);
+ reserveJson.put("loopNo", loopHitResult.getLoopNo());
+ reserveJson.put("hitStationId", loopHitResult.getHitStationId());
+ reserveJson.put("createTime", System.currentTimeMillis());
+ redisUtil.hset(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, String.valueOf(wrkNo), reserveJson.toJSONString());
+ redisUtil.expire(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, LOOP_LOAD_RESERVE_EXPIRE_SECONDS);
+ }
+
+ public DispatchLimitConfig getDispatchLimitConfig(Integer startStationId, Integer endStationId) {
+ DispatchLimitConfig config = new DispatchLimitConfig();
+ Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
+ if (systemConfigMapObj instanceof Map) {
+ Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj;
+ config.setCircleMaxLoadLimit(parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.getCircleMaxLoadLimit()));
+ String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable");
+ if (isBlank(loopModeValue)) {
+ loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable");
+ }
+ if (isBlank(loopModeValue)) {
+ loopModeValue = getConfigValue(systemConfigMap, "isCircleMode");
+ }
+ config.setLoopModeEnable(parseBoolean(loopModeValue, config.isLoopModeEnable()));
+ }
+
+ if (stationPathPolicyService != null && startStationId != null && endStationId != null) {
+ try {
+ StationPathResolvedPolicy resolvedPolicy = stationPathPolicyService.resolvePolicy(startStationId, endStationId);
+ if (resolvedPolicy != null && resolvedPolicy.getProfileConfig() != null) {
+ config.setCircleMaxLoadLimit(parseLoadLimit(String.valueOf(resolvedPolicy.getProfileConfig().getCircleMaxLoadLimit()), config.getCircleMaxLoadLimit()));
+ }
+ } catch (Exception ignore) {
+ }
+ }
+
+ return config;
+ }
+
+ 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);
+ }
+
+ private Integer getStationIdFromNode(NavigateNode node) {
+ if (node == null || isBlank(node.getNodeValue())) {
+ return null;
+ }
+ try {
+ JSONObject value = JSONObject.parseObject(node.getNodeValue());
+ return value == null ? null : value.getInteger("stationId");
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ private int toNonNegative(Integer value) {
+ if (value == null || value < 0) {
+ return 0;
+ }
+ 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;
+ }
+
+ private String getConfigValue(Map<?, ?> configMap, String key) {
+ Object value = configMap.get(key);
+ return value == null ? null : String.valueOf(value).trim();
+ }
+
+ private boolean parseBoolean(String value, boolean defaultValue) {
+ if (isBlank(value)) {
+ return defaultValue;
+ }
+ String lowValue = value.toLowerCase(Locale.ROOT);
+ if ("y".equals(lowValue) || "yes".equals(lowValue) || "true".equals(lowValue)
+ || "1".equals(lowValue) || "on".equals(lowValue)) {
+ return true;
+ }
+ if ("n".equals(lowValue) || "no".equals(lowValue) || "false".equals(lowValue)
+ || "0".equals(lowValue) || "off".equals(lowValue)) {
+ return false;
+ }
+ return defaultValue;
+ }
+
+ private double parseLoadLimit(String value, double defaultValue) {
+ if (isBlank(value)) {
+ return defaultValue;
+ }
+ try {
+ String normalized = value.replace("%", "").trim();
+ double parsed = Double.parseDouble(normalized);
+ if (parsed > 1.0) {
+ parsed = parsed / 100.0;
+ }
+ if (parsed < 0.0) {
+ return 0.0;
+ }
+ if (parsed > 1.0) {
+ return 1.0;
+ }
+ return parsed;
+ } catch (Exception ignore) {
+ return defaultValue;
+ }
+ }
+
+ private String formatPercent(double value) {
+ return String.format(Locale.ROOT, "%.1f%%", value * 100.0);
+ }
+
+ private boolean isBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupport.java b/src/main/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupport.java
new file mode 100644
index 0000000..7328225
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupport.java
@@ -0,0 +1,231 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.core.common.Cools;
+import com.zy.asrs.entity.BasStationOpt;
+import com.zy.asrs.service.BasStationOptService;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.enums.StationCommandType;
+import com.zy.core.model.command.StationCommand;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+@Component
+public class StationDispatchRuntimeStateSupport {
+ private static final int STATION_IDLE_TRACK_EXPIRE_SECONDS = 60 * 60;
+ private static final String IDLE_RECOVER_CLEARED_MEMO = "idleRecoverRerouteCleared";
+
+ @Autowired
+ private RedisUtil redisUtil;
+ @Autowired
+ private BasStationOptService basStationOptService;
+
+ public StationTaskIdleTrack touchIdleTrack(Integer taskNo, Integer stationId) {
+ if (taskNo == null || taskNo <= 0 || stationId == null) {
+ return null;
+ }
+ long now = System.currentTimeMillis();
+ StationTaskIdleTrack idleTrack = loadIdleTrack(taskNo);
+ if (idleTrack == null || !Objects.equals(idleTrack.getStationId(), stationId)) {
+ idleTrack = new StationTaskIdleTrack(taskNo, stationId, now);
+ saveIdleTrack(idleTrack);
+ }
+ return idleTrack;
+ }
+
+ public StationTaskIdleTrack loadIdleTrack(Integer taskNo) {
+ if (taskNo == null || taskNo <= 0 || redisUtil == null) {
+ 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;
+ }
+ }
+
+ public void saveIdleTrack(StationTaskIdleTrack idleTrack) {
+ if (idleTrack == null || idleTrack.getTaskNo() == null || idleTrack.getTaskNo() <= 0 || redisUtil == null) {
+ return;
+ }
+ redisUtil.set(
+ RedisKeyType.STATION_TASK_IDLE_TRACK_.key + idleTrack.getTaskNo(),
+ JSON.toJSONString(idleTrack, SerializerFeature.DisableCircularReferenceDetect),
+ STATION_IDLE_TRACK_EXPIRE_SECONDS
+ );
+ }
+
+ public 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();
+ }
+
+ public 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;
+ }
+
+ public boolean tryAcquireLock(String key, int seconds) {
+ if (redisUtil == null || isBlank(key)) {
+ return true;
+ }
+ Object lock = redisUtil.get(key);
+ if (lock != null) {
+ return false;
+ }
+ redisUtil.set(key, "lock", seconds);
+ return true;
+ }
+
+ public boolean tryAcquireOutOrderDispatchLock(Integer wrkNo, Integer stationId, int seconds) {
+ if (wrkNo == null || wrkNo <= 0 || stationId == null) {
+ return true;
+ }
+ return tryAcquireLock(RedisKeyType.STATION_OUT_ORDER_DISPATCH_LIMIT_.key + wrkNo + "_" + stationId, seconds);
+ }
+
+ public void signalSegmentReset(Integer taskNo, long waitMs) {
+ if (redisUtil == null || taskNo == null || taskNo <= 0) {
+ return;
+ }
+ String key = RedisKeyType.DEVICE_STATION_MOVE_RESET.key + taskNo;
+ redisUtil.set(key, "cancel", 3);
+ try {
+ if (waitMs > 0L) {
+ Thread.sleep(waitMs);
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (Exception ignore) {
+ }
+ redisUtil.del(key);
+ }
+
+ public StationCommand loadWatchCircleCommand(Integer wrkNo) {
+ if (wrkNo == null || wrkNo <= 0 || redisUtil == null) {
+ return null;
+ }
+ Object circleObj = redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo);
+ if (circleObj == null) {
+ return null;
+ }
+ try {
+ return JSON.parseObject(circleObj.toString(), StationCommand.class);
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ public void saveWatchCircleCommand(Integer wrkNo, StationCommand command) {
+ if (wrkNo == null || wrkNo <= 0 || command == null || redisUtil == null) {
+ return;
+ }
+ redisUtil.set(
+ RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo,
+ JSON.toJSONString(command, SerializerFeature.DisableCircularReferenceDetect),
+ 60 * 60 * 24
+ );
+ }
+
+ public void clearWatchCircleCommand(Integer wrkNo) {
+ if (wrkNo == null || wrkNo <= 0 || redisUtil == null) {
+ return;
+ }
+ redisUtil.del(RedisKeyType.WATCH_CIRCLE_STATION_.key + wrkNo);
+ }
+
+ private List<BasStationOpt> listIssuedMoveCommandsDuringIdleStay(StationTaskIdleTrack idleTrack,
+ Integer taskNo) {
+ if (idleTrack == null || taskNo == null || taskNo <= 0 || idleTrack.getFirstSeenTime() == 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.getFirstSeenTime()))
+ .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 boolean isBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java b/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java
new file mode 100644
index 0000000..5bfc4a0
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationOutboundDecisionSupport.java
@@ -0,0 +1,615 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.core.common.Cools;
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.BasStation;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasDevpService;
+import com.zy.asrs.service.BasStationService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.common.model.NavigateNode;
+import com.zy.common.utils.NavigateUtils;
+import com.zy.core.News;
+import com.zy.core.cache.SlaveConnection;
+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.command.StationCommand;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.move.StationMoveCoordinator;
+import com.zy.core.move.StationMoveDispatchMode;
+import com.zy.core.move.StationMoveSession;
+import com.zy.core.service.StationTaskLoopService;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.model.CircleTargetCandidate;
+import com.zy.core.utils.station.model.OutOrderDispatchDecision;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@Component
+public class StationOutboundDecisionSupport {
+
+ @Autowired
+ private WrkMastService wrkMastService;
+ @Autowired
+ private BasDevpService basDevpService;
+ @Autowired
+ private BasStationService basStationService;
+ @Autowired
+ private NavigateUtils navigateUtils;
+ @Autowired
+ private StationTaskLoopService stationTaskLoopService;
+ @Autowired
+ private StationMoveCoordinator stationMoveCoordinator;
+ @Autowired
+ private StationDispatchRuntimeStateSupport stationDispatchRuntimeStateSupport;
+
+ public 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)
+ );
+ }
+
+ public 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));
+ }
+
+ public List<Integer> getAllOutOrderList() {
+ List<Integer> list = new ArrayList<>();
+ List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
+ for (BasDevp basDevp : basDevps) {
+ List<Integer> orderList = basDevp.getOutOrderIntList();
+ list.addAll(orderList);
+ }
+ return list;
+ }
+
+ public OutOrderDispatchDecision resolveOutboundDispatchDecision(Integer currentStationId,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor) {
+ if (wrkMast == null || wrkMast.getStaNo() == null) {
+ return null;
+ }
+ if (!shouldApplyOutOrder(wrkMast, outOrderStationIds)) {
+ return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
+ }
+ 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 OutOrderDispatchDecision.circle(dispatchStationId, null, false);
+ }
+ return OutOrderDispatchDecision.direct(dispatchStationId);
+ }
+
+ public 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()) {
+ stationDispatchRuntimeStateSupport.saveWatchCircleCommand(wrkMast.getWrkNo(), command);
+ if (dispatchDecision.shouldCountLoopIssue()
+ && stationTaskLoopService != null
+ && dispatchDecision.getLoopEvaluation() != null) {
+ stationTaskLoopService.recordLoopIssue(dispatchDecision.getLoopEvaluation(), "OUT_ORDER_CIRCLE");
+ }
+ } else {
+ stationDispatchRuntimeStateSupport.clearWatchCircleCommand(wrkMast.getWrkNo());
+ }
+ }
+
+ public 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());
+ }
+
+ public boolean isWatchingCircleArrival(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 true;
+ }
+ if (session.containsStation(stationId)) {
+ return false;
+ }
+ }
+ }
+ StationCommand command = stationDispatchRuntimeStateSupport.loadWatchCircleCommand(wrkNo);
+ return command != null && stationId != null && stationId.equals(command.getTargetStaNo());
+ }
+
+ 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);
+ }
+
+ 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;
+ }
+
+ 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));
+ }
+
+ private boolean isFactorCandidateTask(WrkMast wrkMast) {
+ return wrkMast != null
+ && Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)
+ && wrkMast.getBatchSeq() != null
+ && !"taskCancel".equals(wrkMast.getMk());
+ }
+
+ private boolean shouldApplyOutOrder(WrkMast wrkMast, List<Integer> outOrderStationIds) {
+ return wrkMast != null
+ && wrkMast.getStaNo() != null
+ && Objects.equals(wrkMast.getIoType(), WrkIoType.OUT.id)
+ && !Cools.isEmpty(wrkMast.getBatch())
+ && wrkMast.getBatchSeq() != null
+ && outOrderStationIds != null
+ && !outOrderStationIds.isEmpty();
+ }
+
+ private boolean isCurrentOutOrderDispatchStation(Integer currentStationId,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ 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);
+ }
+
+ private boolean isCurrentOutOrderStation(Integer currentStationId,
+ List<Integer> outOrderStationIds) {
+ return currentStationId != null
+ && outOrderStationIds != null
+ && outOrderStationIds.contains(currentStationId);
+ }
+
+ 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 OutOrderDispatchDecision.direct(wrkMast.getStaNo());
+ }
+
+ 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 = seq == null
+ ? currentBatchSeq.equals(wrkMast.getBatchSeq())
+ : Integer.valueOf(seq + 1).equals(wrkMast.getBatchSeq());
+ if (toTarget) {
+ if (hasReachableOutReleaseSlot(wrkMast, currentStationId, wrkMast.getStaNo(), pathLenFactor)) {
+ return OutOrderDispatchDecision.direct(wrkMast.getStaNo());
+ }
+ 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 OutOrderDispatchDecision.circle(circleTarget, 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 OutOrderDispatchDecision.circle(circleTarget, loopEvaluation, true);
+ }
+
+ 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"
+ );
+ }
+
+ private Integer resolveDispatchOutOrderTarget(WrkMast wrkMast,
+ Integer sourceStationId,
+ Integer finalTargetStationId,
+ List<Integer> outOrderList,
+ Double pathLenFactor) {
+ if (finalTargetStationId == null) {
+ return null;
+ }
+ if (sourceStationId == null || outOrderList == null || outOrderList.isEmpty()) {
+ return finalTargetStationId;
+ }
+
+ try {
+ 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 (Objects.equals(stationId, finalTargetStationId)) {
+ continue;
+ }
+ if (outOrderList.contains(stationId)) {
+ return stationId;
+ }
+ }
+ } catch (Exception ignore) {
+ }
+ return finalTargetStationId;
+ }
+
+ 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);
+ }
+
+ 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);
+ if (candidateStationId == null || currentStationId.equals(candidateStationId)) {
+ continue;
+ }
+ try {
+ List<NavigateNode> path = calcOutboundNavigatePath(wrkMast, currentStationId, candidateStationId, pathLenFactor);
+ if (path != null && !path.isEmpty()) {
+ candidateList.add(new CircleTargetCandidate(candidateStationId, path.size(), offset));
+ }
+ } catch (Exception ignore) {
+ }
+ }
+ 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);
+ }
+
+ 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 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);
+ JSONObject value = JSONObject.parseObject(node.getNodeValue());
+ if (value == null) {
+ continue;
+ }
+ Integer stationId = value.getInteger("stationId");
+ if (searchStationId.equals(stationId)) {
+ break;
+ }
+ checkList.add(stationId);
+ }
+
+ HashMap<String, Integer> batchMap = new HashMap<>();
+ for (Integer station : checkList) {
+ BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", station));
+ if (basStation == null) {
+ continue;
+ }
+
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basStation.getDeviceNo());
+ if (stationThread == null) {
+ continue;
+ }
+ StationProtocol checkStationProtocol = stationThread.getStatusMap().get(station);
+ if (checkStationProtocol == null) {
+ continue;
+ }
+ if (checkStationProtocol.getTaskNo() > 0) {
+ WrkMast checkWrkMast = wrkMastService.selectByWorkNo(checkStationProtocol.getTaskNo());
+ if (checkWrkMast == null) {
+ continue;
+ }
+
+ if (!Cools.isEmpty(checkWrkMast.getBatch())) {
+ batchMap.put(checkWrkMast.getBatch(), checkWrkMast.getBatchSeq());
+ }
+ }
+ }
+
+ return batchMap.get(searchBatch);
+ }
+
+ private Integer getStationIdFromNode(NavigateNode node) {
+ if (node == null || isBlank(node.getNodeValue())) {
+ return null;
+ }
+ try {
+ JSONObject value = JSONObject.parseObject(node.getNodeValue());
+ return value == null ? null : value.getInteger("stationId");
+ } catch (Exception ignore) {
+ return null;
+ }
+ }
+
+ private Double normalizePathLenFactor(Double pathLenFactor) {
+ if (pathLenFactor == null || pathLenFactor < 0.0d) {
+ return 0.0d;
+ }
+ if (pathLenFactor > 1.0d) {
+ return 1.0d;
+ }
+ return pathLenFactor;
+ }
+
+ private boolean isBlank(String value) {
+ return value == null || value.trim().isEmpty();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
new file mode 100644
index 0000000..644090a
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationOutboundDispatchProcessor.java
@@ -0,0 +1,249 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.zy.asrs.domain.enums.NotifyMsgType;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.WrkAnalysisService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.asrs.utils.NotifyUtils;
+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.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.move.StationMoveCoordinator;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.model.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+import com.zy.core.utils.station.model.OutOrderDispatchDecision;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+@Component
+public class StationOutboundDispatchProcessor {
+
+ @Autowired
+ private WrkMastService wrkMastService;
+ @Autowired
+ private WrkAnalysisService wrkAnalysisService;
+ @Autowired
+ private RedisUtil redisUtil;
+ @Autowired
+ private NotifyUtils notifyUtils;
+ @Autowired
+ private StationMoveCoordinator stationMoveCoordinator;
+ @Autowired(required = false)
+ private StationCommandDispatcher stationCommandDispatcher;
+ @Autowired
+ private StationDispatchLoadSupport stationDispatchLoadSupport;
+ @Autowired
+ private StationOutboundDecisionSupport stationOutboundDecisionSupport;
+
+ public void crnStationOutExecute() {
+ try {
+ DispatchLimitConfig baseLimitConfig =
+ stationDispatchLoadSupport.getDispatchLimitConfig(null, null);
+ int[] currentStationTaskCountRef = new int[]{stationDispatchLoadSupport.countCurrentStationTask()};
+ LoadGuardState loadGuardState =
+ stationDispatchLoadSupport.buildLoadGuardState(baseLimitConfig);
+
+ List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
+ .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
+ .isNotNull("crn_no"));
+ List<Integer> outOrderList = stationOutboundDecisionSupport.getAllOutOrderList();
+
+ for (WrkMast wrkMast : wrkMasts) {
+ Object infoObj = redisUtil.get(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo());
+ if (infoObj == null) {
+ News.info("鍑哄簱浠诲姟{}鏁版嵁缂撳瓨涓嶅瓨鍦�", wrkMast.getWrkNo());
+ continue;
+ }
+
+ StationObjModel stationObjModel = JSON.parseObject(infoObj.toString(), StationObjModel.class);
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
+ StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
+ if (stationProtocol == null) {
+ continue;
+ }
+
+ Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
+ if (lock != null) {
+ continue;
+ }
+
+ if (stationProtocol.isAutoing()
+ && stationProtocol.isLoading()
+ && stationProtocol.getTaskNo() == 0) {
+ Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
+ OutOrderDispatchDecision dispatchDecision =
+ stationOutboundDecisionSupport.resolveOutboundDispatchDecision(
+ stationProtocol.getStationId(),
+ wrkMast,
+ outOrderList,
+ pathLenFactor
+ );
+ Integer moveStaNo = dispatchDecision == null ? null : dispatchDecision.getTargetStationId();
+ if (moveStaNo == null) {
+ continue;
+ }
+
+ DispatchLimitConfig limitConfig =
+ stationDispatchLoadSupport.getDispatchLimitConfig(stationProtocol.getStationId(), moveStaNo);
+ LoopHitResult loopHitResult =
+ stationDispatchLoadSupport.findPathLoopHit(
+ limitConfig,
+ stationProtocol.getStationId(),
+ moveStaNo,
+ loadGuardState,
+ wrkMast,
+ pathLenFactor
+ );
+ if (stationDispatchLoadSupport.isDispatchBlocked(
+ limitConfig,
+ currentStationTaskCountRef[0],
+ loadGuardState,
+ loopHitResult.isThroughLoop())) {
+ return;
+ }
+
+ StationCommand command = stationOutboundDecisionSupport.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(now);
+ wrkMast.setModiTime(now);
+ if (wrkMastService.updateById(wrkMast)) {
+ 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());
+ currentStationTaskCountRef[0]++;
+ loadGuardState.reserveLoopTask(loopHitResult.getLoopNo());
+ stationDispatchLoadSupport.saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void dualCrnStationOutExecute() {
+ try {
+ List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>()
+ .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
+ .isNotNull("dual_crn_no"));
+ for (WrkMast wrkMast : wrkMasts) {
+ Object infoObj = redisUtil.get(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo());
+ if (infoObj == null) {
+ News.info("鍑哄簱浠诲姟{}鏁版嵁缂撳瓨涓嶅瓨鍦�", wrkMast.getWrkNo());
+ continue;
+ }
+
+ StationObjModel stationObjModel = JSON.parseObject(infoObj.toString(), StationObjModel.class);
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, stationObjModel.getDeviceNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
+ StationProtocol stationProtocol = stationMap.get(stationObjModel.getStationId());
+ if (stationProtocol == null) {
+ continue;
+ }
+
+ Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId());
+ if (lock != null) {
+ continue;
+ }
+
+ if (stationProtocol.isAutoing()
+ && stationProtocol.isLoading()
+ && stationProtocol.getTaskNo() == 0) {
+ Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
+ StationCommand command = stationOutboundDecisionSupport.buildOutboundMoveCommand(
+ stationThread,
+ wrkMast,
+ stationProtocol.getStationId(),
+ wrkMast.getStaNo(),
+ pathLenFactor
+ );
+ if (command == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "鑾峰彇杈撻�佺嚎鍛戒护澶辫触");
+ continue;
+ }
+
+ wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts);
+ wrkMast.setSystemMsg("");
+ wrkMast.setIoTime(new Date());
+ if (wrkMastService.updateById(wrkMast)) {
+ boolean offered = offerDevpCommandWithDedup(stationObjModel.getDeviceNo(), command, "dualCrnStationOutExecute");
+ if (offered && stationMoveCoordinator != null) {
+ 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);
+ redisUtil.del(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + wrkMast.getWrkNo());
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private boolean offerDevpCommandWithDedup(Integer deviceNo, StationCommand command, String scene) {
+ StationCommandDispatchResult dispatchResult = stationCommandDispatcher
+ .dispatch(deviceNo, command, "station-operate-process", scene);
+ return dispatchResult.isAccepted();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationRegularDispatchProcessor.java b/src/main/java/com/zy/core/utils/station/StationRegularDispatchProcessor.java
new file mode 100644
index 0000000..a1e02ae
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationRegularDispatchProcessor.java
@@ -0,0 +1,284 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.zy.asrs.domain.enums.NotifyMsgType;
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.BasStation;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasDevpService;
+import com.zy.asrs.service.BasStationService;
+import com.zy.asrs.service.WrkAnalysisService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.asrs.utils.NotifyUtils;
+import com.zy.common.entity.FindCrnNoResult;
+import com.zy.common.service.CommonService;
+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.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.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@Component
+public class StationRegularDispatchProcessor {
+
+ @Autowired
+ private BasDevpService basDevpService;
+ @Autowired
+ private WrkMastService wrkMastService;
+ @Autowired
+ private CommonService commonService;
+ @Autowired
+ private RedisUtil redisUtil;
+ @Autowired
+ private WrkAnalysisService wrkAnalysisService;
+ @Autowired
+ private BasStationService basStationService;
+ @Autowired
+ private NotifyUtils notifyUtils;
+ @Autowired
+ private StationMoveCoordinator stationMoveCoordinator;
+ @Autowired(required = false)
+ private StationCommandDispatcher stationCommandDispatcher;
+ @Autowired
+ private StationDispatchLoadSupport stationDispatchLoadSupport;
+
+ public void stationInExecute() {
+ try {
+ DispatchLimitConfig baseLimitConfig = stationDispatchLoadSupport.getDispatchLimitConfig(null, null);
+ int[] currentStationTaskCountRef = new int[]{stationDispatchLoadSupport.countCurrentStationTask()};
+ LoadGuardState loadGuardState = stationDispatchLoadSupport.buildLoadGuardState(baseLimitConfig);
+
+ List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<>());
+ for (BasDevp basDevp : basDevps) {
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ Map<Integer, StationProtocol> stationMap = stationThread.getStatusMap();
+ List<StationObjModel> stationList = basDevp.getBarcodeStationList$();
+ for (StationObjModel entity : stationList) {
+ Integer stationId = entity.getStationId();
+ if (!stationMap.containsKey(stationId)) {
+ continue;
+ }
+
+ StationProtocol stationProtocol = stationMap.get(stationId);
+ if (stationProtocol == null) {
+ continue;
+ }
+
+ Object lock = redisUtil.get(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId);
+ if (lock != null) {
+ continue;
+ }
+
+ if (!stationProtocol.isAutoing()
+ || !stationProtocol.isLoading()
+ || stationProtocol.getTaskNo() <= 0) {
+ continue;
+ }
+
+ WrkMast wrkMast = wrkMastService.getOne(new QueryWrapper<WrkMast>().eq("barcode", stationProtocol.getBarcode()));
+ if (wrkMast == null || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.NEW_INBOUND.sts)) {
+ continue;
+ }
+
+ String locNo = wrkMast.getLocNo();
+ FindCrnNoResult findCrnNoResult = commonService.findCrnNoByLocNo(locNo);
+ if (findCrnNoResult == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鏈尮閰嶅埌鍫嗗灈鏈�", wrkMast.getWrkNo());
+ continue;
+ }
+
+ Integer targetStationId = commonService.findInStationId(findCrnNoResult, stationId);
+ if (targetStationId == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "{}绔欑偣,鎼滅储鍏ュ簱绔欑偣澶辫触", stationId);
+ continue;
+ }
+
+ DispatchLimitConfig limitConfig = stationDispatchLoadSupport.getDispatchLimitConfig(stationProtocol.getStationId(), targetStationId);
+ LoopHitResult loopHitResult = stationDispatchLoadSupport.findPathLoopHit(
+ limitConfig,
+ stationProtocol.getStationId(),
+ targetStationId,
+ loadGuardState
+ );
+ if (stationDispatchLoadSupport.isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
+ return;
+ }
+
+ StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, targetStationId, 0);
+ if (command == null) {
+ News.taskInfo(wrkMast.getWrkNo(), "{}宸ヤ綔,鑾峰彇杈撻�佺嚎鍛戒护澶辫触", wrkMast.getWrkNo());
+ continue;
+ }
+
+ Date now = new Date();
+ wrkMast.setWrkSts(WrkStsType.INBOUND_STATION_RUN.sts);
+ wrkMast.setSourceStaNo(stationProtocol.getStationId());
+ wrkMast.setStaNo(targetStationId);
+ wrkMast.setSystemMsg("");
+ wrkMast.setIoTime(now);
+ wrkMast.setModiTime(now);
+ if (wrkMastService.updateById(wrkMast)) {
+ wrkAnalysisService.markInboundStationStart(wrkMast, now);
+ boolean offered = offerDevpCommandWithDedup(basDevp.getDevpNo(), command, "stationInExecute");
+ if (offered && stationMoveCoordinator != null) {
+ 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());
+ stationDispatchLoadSupport.saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void stationOutExecuteFinish() {
+ try {
+ List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN.sts));
+ for (WrkMast wrkMast : wrkMasts) {
+ Integer wrkNo = wrkMast.getWrkNo();
+ Integer targetStaNo = wrkMast.getStaNo();
+ if (wrkNo == null || targetStaNo == null) {
+ continue;
+ }
+
+ boolean complete = false;
+ Integer targetDeviceNo = null;
+ StationThread stationThread = null;
+ BasStation basStation = basStationService.getOne(new QueryWrapper<BasStation>().eq("station_id", targetStaNo));
+ if (basStation != null) {
+ targetDeviceNo = basStation.getDeviceNo();
+ 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) {
+ attemptClearTaskPath(stationThread, wrkNo);
+ completeStationRunTask(wrkMast, targetDeviceNo);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void checkTaskToComplete() {
+ try {
+ List<WrkMast> wrkMasts = wrkMastService.list(new QueryWrapper<WrkMast>().eq("wrk_sts", WrkStsType.STATION_RUN_COMPLETE.sts));
+ for (WrkMast wrkMast : wrkMasts) {
+ Integer wrkNo = wrkMast.getWrkNo();
+ Integer targetStaNo = wrkMast.getStaNo();
+
+ Object lock = redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_COMPLETE_LIMIT.key + wrkNo);
+ if (lock != null) {
+ continue;
+ }
+
+ 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 (!Objects.equals(stationProtocol.getTaskNo(), wrkNo)) {
+ if (stationMoveCoordinator != null) {
+ stationMoveCoordinator.finishSession(wrkNo);
+ }
+ wrkMast.setWrkSts(WrkStsType.COMPLETE_OUTBOUND.sts);
+ wrkMast.setIoTime(new Date());
+ wrkMastService.updateById(wrkMast);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void attemptClearTaskPath(StationThread stationThread, Integer taskNo) {
+ if (stationThread == null || taskNo == null || taskNo <= 0) {
+ return;
+ }
+ try {
+ boolean cleared = stationThread.clearPath(taskNo);
+ if (cleared) {
+ News.info("杈撻�佺珯鐐逛换鍔¤繍琛屽畬鎴愬悗娓呯悊娈嬬暀璺緞锛屽伐浣滃彿={}", taskNo);
+ }
+ } catch (Exception e) {
+ News.error("杈撻�佺珯鐐逛换鍔¤繍琛屽畬鎴愬悗娓呯悊娈嬬暀璺緞寮傚父锛屽伐浣滃彿={}", taskNo, e);
+ }
+ }
+
+ 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);
+ }
+
+ private boolean offerDevpCommandWithDedup(Integer deviceNo, StationCommand command, String scene) {
+ StationCommandDispatchResult dispatchResult = stationCommandDispatcher.dispatch(deviceNo, command, "station-operate-process", scene);
+ return dispatchResult.isAccepted();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java b/src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java
new file mode 100644
index 0000000..42034af
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationRerouteProcessor.java
@@ -0,0 +1,734 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+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.BasDevpService;
+import com.zy.asrs.service.LocMastService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.common.entity.FindCrnNoResult;
+import com.zy.common.model.StartupDto;
+import com.zy.common.service.CommonService;
+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.move.StationMoveSession;
+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 org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+@Component
+public class StationRerouteProcessor {
+ private static final int OUT_ORDER_DISPATCH_LIMIT_SECONDS = 2;
+ private static final int STATION_IDLE_RECOVER_SECONDS = 10;
+ private static final int STATION_IDLE_RECOVER_LIMIT_SECONDS = 30;
+ private static final long STATION_MOVE_RESET_WAIT_MS = 1000L;
+
+ @Autowired
+ private BasDevpService basDevpService;
+ @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;
+
+ public void checkStationRunBlock() {
+ 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<Integer> runBlockReassignLocStationList = new ArrayList<>();
+ for (StationObjModel stationObjModel : basDevp.getRunBlockReassignLocStationList$()) {
+ runBlockReassignLocStationList.add(stationObjModel.getStationId());
+ }
+ List<Integer> outOrderStationIds = basDevp.getOutOrderIntList();
+
+ for (StationProtocol stationProtocol : stationThread.getStatus()) {
+ if (stationProtocol.isAutoing()
+ && stationProtocol.isLoading()
+ && stationProtocol.getTaskNo() > 0
+ && stationProtocol.isRunBlock()) {
+ WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
+ if (wrkMast == null) {
+ News.info("杈撻�佺珯鐐瑰彿={} 杩愯闃诲锛屼絾鏃犳硶鎵惧埌瀵瑰簲浠诲姟锛屽伐浣滃彿={}", stationProtocol.getStationId(), stationProtocol.getTaskNo());
+ continue;
+ }
+
+ Object lock = redisUtil.get(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo());
+ if (lock != null) {
+ continue;
+ }
+ redisUtil.set(RedisKeyType.CHECK_STATION_RUN_BLOCK_LIMIT_.key + stationProtocol.getTaskNo(), "lock", 15);
+
+ if (shouldUseRunBlockDirectReassign(wrkMast, stationProtocol.getStationId(), runBlockReassignLocStationList)) {
+ executeRunBlockDirectReassign(basDevp, stationThread, stationProtocol, wrkMast);
+ continue;
+ }
+
+ 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 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;
+ }
+
+ for (StationProtocol stationProtocol : stationThread.getStatus()) {
+ if (stationProtocol.isAutoing()
+ && stationProtocol.isLoading()
+ && stationProtocol.getTaskNo() > 0
+ && !stationProtocol.isRunBlock()) {
+ checkStationIdleRecover(basDevp, stationThread, stationProtocol, basDevp.getOutOrderIntList());
+ }
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void checkStationOutOrder() {
+ List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
+ for (BasDevp basDevp : basDevps) {
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
+ if (stationThread == null) {
+ continue;
+ }
+ Map<Integer, StationProtocol> statusMap = stationThread.getStatusMap();
+ List<StationObjModel> orderList = basDevp.getOutOrderList$();
+ List<Integer> outOrderStationIds = basDevp.getOutOrderIntList();
+ for (StationObjModel stationObjModel : orderList) {
+ StationProtocol stationProtocol = statusMap.get(stationObjModel.getStationId());
+ if (stationProtocol == null
+ || !stationProtocol.isAutoing()
+ || !stationProtocol.isLoading()
+ || stationProtocol.getTaskNo() <= 0
+ || stationProtocol.isRunBlock()
+ || !stationProtocol.getStationId().equals(stationProtocol.getTargetStaNo())) {
+ continue;
+ }
+
+ WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
+ if (wrkMast == null
+ || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)
+ || Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) {
+ continue;
+ }
+ if (stationOutboundDecisionSupport.shouldSkipOutOrderDispatchForExistingRoute(wrkMast.getWrkNo(), stationProtocol.getStationId())) {
+ continue;
+ }
+
+ Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.OUT_ORDER,
+ basDevp,
+ stationThread,
+ stationProtocol,
+ wrkMast,
+ outOrderStationIds,
+ pathLenFactor,
+ "checkStationOutOrder"
+ ).withDispatchDeviceNo(stationObjModel.getDeviceNo())
+ .withSuppressDispatchGuard()
+ .withOutOrderDispatchLock()
+ .withResetSegmentCommandsBeforeDispatch();
+ executeSharedReroute(context);
+ }
+ }
+ }
+
+ public void watchCircleStation() {
+ List<BasDevp> basDevps = basDevpService.list(new QueryWrapper<BasDevp>());
+ for (BasDevp basDevp : basDevps) {
+ StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
+ if (stationThread == null) {
+ continue;
+ }
+
+ List<Integer> outOrderList = basDevp.getOutOrderIntList();
+ for (StationProtocol stationProtocol : stationThread.getStatus()) {
+ if (!stationProtocol.isAutoing()
+ || !stationProtocol.isLoading()
+ || stationProtocol.getTaskNo() <= 0
+ || !stationOutboundDecisionSupport.isWatchingCircleArrival(stationProtocol.getTaskNo(), stationProtocol.getStationId())) {
+ continue;
+ }
+
+ WrkMast wrkMast = wrkMastService.selectByWorkNo(stationProtocol.getTaskNo());
+ if (wrkMast == null
+ || !Objects.equals(wrkMast.getWrkSts(), WrkStsType.STATION_RUN.sts)
+ || Objects.equals(stationProtocol.getStationId(), wrkMast.getStaNo())) {
+ continue;
+ }
+
+ Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(wrkMast);
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.WATCH_CIRCLE,
+ basDevp,
+ stationThread,
+ stationProtocol,
+ wrkMast,
+ outOrderList,
+ pathLenFactor,
+ "watchCircleStation"
+ ).withSuppressDispatchGuard()
+ .withOutOrderDispatchLock()
+ .withResetSegmentCommandsBeforeDispatch();
+ executeSharedReroute(context);
+ }
+ }
+ }
+
+ 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) {
+ 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 =
+ stationOutboundDecisionSupport.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);
+ }
+
+ public 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);
+ }
+
+ public 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) {
+ if (Objects.equals(stationId, session.getCurrentStationId())
+ || Objects.equals(stationId, session.getDispatchStationId())
+ || session.containsStation(stationId)) {
+ long elapsedMs = System.currentTimeMillis() - session.getLastIssuedAt();
+ if (elapsedMs < thresholdMs) {
+ stationDispatchRuntimeStateSupport.saveIdleTrack(new StationTaskIdleTrack(taskNo, stationId, System.currentTimeMillis()));
+ News.info("杈撻�佺珯鐐逛换鍔″垰瀹屾垚鍛戒护涓嬪彂锛屽凡璺宠繃鍋滅暀閲嶇畻銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛岃窛涓婃涓嬪彂={}ms锛宺outeVersion={}",
+ stationId, taskNo, elapsedMs, session.getRouteVersion());
+ return true;
+ }
+ }
+ }
+ if (!stationDispatchRuntimeStateSupport.hasRecentIssuedMoveCommand(taskNo, stationId, thresholdMs)) {
+ return false;
+ }
+ stationDispatchRuntimeStateSupport.saveIdleTrack(new StationTaskIdleTrack(taskNo, stationId, System.currentTimeMillis()));
+ News.info("杈撻�佺珯鐐逛换鍔″垰瀹屾垚鍛戒护涓嬪彂锛屽凡璺宠繃鍋滅暀閲嶇畻銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛岃窛鏈�杩戝懡浠や笅鍙�<{}ms锛宺outeVersion={}",
+ stationId, taskNo, thresholdMs, session == null ? null : session.getRouteVersion());
+ return true;
+ }
+
+ private RerouteExecutionResult executeReroutePlanWithTaskLock(RerouteContext context,
+ RerouteCommandPlan plan,
+ StationProtocol stationProtocol,
+ Integer taskNo,
+ Integer stationId) {
+ boolean runBlockReroute = context.sceneType() == RerouteSceneType.RUN_BLOCK_REROUTE;
+ 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) {
+ News.info("杈撻�佺珯鐐硅繍琛屽牭濉為噸瑙勫垝妫�娴嬪埌鏃у垎娈靛懡浠ゆ畫鐣欙紝宸插厛娓呯悊鏈湴鐘舵�佸悗缁х画閲嶅彂銆傜珯鐐瑰彿={}锛屽伐浣滃彿={}锛屽綋鍓嶄换鍔″懡浠ゆ暟={}",
+ stationId,
+ taskNo,
+ currentTaskBufferCommandCount);
+ }
+ if (!runBlockReroute
+ && context.checkSuppressDispatch()
+ && stationMoveCoordinator != null
+ && stationMoveCoordinator.shouldSuppressDispatch(taskNo, stationId, plan.command())) {
+ return RerouteExecutionResult.skip("dispatch-suppressed");
+ }
+ if (context.requireOutOrderDispatchLock()
+ && !stationDispatchRuntimeStateSupport.tryAcquireOutOrderDispatchLock(taskNo, stationId, OUT_ORDER_DISPATCH_LIMIT_SECONDS)) {
+ return RerouteExecutionResult.skip("out-order-lock");
+ }
+
+ if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
+ stationMoveCoordinator.markCancelPending(taskNo, "reroute_pending");
+ }
+
+ if (runBlockReroute) {
+ if (context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
+ stationMoveCoordinator.cancelSession(taskNo);
+ }
+ if (context.resetSegmentCommandsBeforeDispatch()) {
+ stationDispatchRuntimeStateSupport.signalSegmentReset(taskNo, STATION_MOVE_RESET_WAIT_MS);
+ }
+ }
+
+ if (!runBlockReroute
+ && context.cancelSessionBeforeDispatch() && stationMoveCoordinator != null) {
+ stationMoveCoordinator.cancelSession(taskNo);
+ }
+ if (!isBlank(context.executionLockKey())
+ && !stationDispatchRuntimeStateSupport.tryAcquireLock(context.executionLockKey(), context.executionLockSeconds())) {
+ return RerouteExecutionResult.skip("scene-lock");
+ }
+ if (!runBlockReroute && context.resetSegmentCommandsBeforeDispatch()) {
+ stationDispatchRuntimeStateSupport.signalSegmentReset(taskNo, STATION_MOVE_RESET_WAIT_MS);
+ }
+
+ int clearedCommandCount = 0;
+ if (context.clearIdleIssuedCommands()) {
+ clearedCommandCount = stationDispatchRuntimeStateSupport.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);
+ }
+
+ 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();
+
+ stationOutboundDecisionSupport.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) {
+ stationDispatchRuntimeStateSupport.saveIdleTrack(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 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 = stationDispatchRuntimeStateSupport.touchIdleTrack(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 = stationOutboundDecisionSupport.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);
+ }
+
+ 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 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();
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/StationTaskIdleTrack.java b/src/main/java/com/zy/core/utils/station/StationTaskIdleTrack.java
new file mode 100644
index 0000000..7ab5eb4
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/StationTaskIdleTrack.java
@@ -0,0 +1,47 @@
+package com.zy.core.utils.station;
+
+public class StationTaskIdleTrack {
+ private Integer taskNo;
+ private Integer stationId;
+ private Long firstSeenTime;
+
+ public StationTaskIdleTrack() {
+ }
+
+ public StationTaskIdleTrack(Integer taskNo, Integer stationId, Long firstSeenTime) {
+ this.taskNo = taskNo;
+ this.stationId = stationId;
+ this.firstSeenTime = firstSeenTime;
+ }
+
+ public 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;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/CircleTargetCandidate.java b/src/main/java/com/zy/core/utils/station/model/CircleTargetCandidate.java
new file mode 100644
index 0000000..8aa9de4
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/CircleTargetCandidate.java
@@ -0,0 +1,25 @@
+package com.zy.core.utils.station.model;
+
+public final class CircleTargetCandidate {
+ private final Integer stationId;
+ private final Integer pathLength;
+ private final Integer offset;
+
+ public CircleTargetCandidate(Integer stationId, Integer pathLength, Integer offset) {
+ this.stationId = stationId;
+ this.pathLength = pathLength == null ? 0 : pathLength;
+ this.offset = offset == null ? 0 : offset;
+ }
+
+ public Integer getStationId() {
+ return stationId;
+ }
+
+ public Integer getPathLength() {
+ return pathLength;
+ }
+
+ public Integer getOffset() {
+ return offset;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/DispatchLimitConfig.java b/src/main/java/com/zy/core/utils/station/model/DispatchLimitConfig.java
new file mode 100644
index 0000000..24367e1
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/DispatchLimitConfig.java
@@ -0,0 +1,11 @@
+package com.zy.core.utils.station.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+@Getter
+@Setter
+public class DispatchLimitConfig {
+ private double circleMaxLoadLimit = 0.8d;
+ private boolean loopModeEnable = false;
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/LoadGuardState.java b/src/main/java/com/zy/core/utils/station/model/LoadGuardState.java
new file mode 100644
index 0000000..5747a0f
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/LoadGuardState.java
@@ -0,0 +1,52 @@
+package com.zy.core.utils.station.model;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Getter
+public class LoadGuardState {
+ @Setter
+ private int totalStationCount = 0;
+ @Setter
+ private int projectedTaskStationCount = 0;
+ private final Map<Integer, Integer> stationLoopNoMap = new HashMap<>();
+
+ public double currentLoad() {
+ return calcLoad(this.projectedTaskStationCount, this.totalStationCount);
+ }
+
+ public double loadAfterReserve() {
+ return calcLoad(this.projectedTaskStationCount + 1, this.totalStationCount);
+ }
+
+ public void reserveLoopTask(Integer loopNo) {
+ if (loopNo == null || loopNo <= 0 || this.totalStationCount <= 0) {
+ return;
+ }
+ this.projectedTaskStationCount++;
+ }
+
+ public void putStationLoopNo(Integer stationId, Integer loopNo) {
+ if (stationId == null || loopNo == null) {
+ return;
+ }
+ this.stationLoopNoMap.put(stationId, loopNo);
+ }
+
+ private double calcLoad(int taskCount, int stationCount) {
+ if (stationCount <= 0 || taskCount <= 0) {
+ return 0.0;
+ }
+ double load = (double) taskCount / (double) stationCount;
+ if (load < 0.0) {
+ return 0.0;
+ }
+ if (load > 1.0) {
+ return 1.0;
+ }
+ return load;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/LoopHitResult.java b/src/main/java/com/zy/core/utils/station/model/LoopHitResult.java
new file mode 100644
index 0000000..490a87f
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/LoopHitResult.java
@@ -0,0 +1,20 @@
+package com.zy.core.utils.station.model;
+
+import lombok.Getter;
+
+@Getter
+public class LoopHitResult {
+ private final boolean throughLoop;
+ private final Integer loopNo;
+ private final Integer hitStationId;
+
+ public LoopHitResult(boolean throughLoop, Integer loopNo, Integer hitStationId) {
+ this.throughLoop = throughLoop;
+ this.loopNo = loopNo;
+ this.hitStationId = hitStationId;
+ }
+
+ public static LoopHitResult noHit() {
+ return new LoopHitResult(false, null, null);
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/OutOrderDispatchDecision.java b/src/main/java/com/zy/core/utils/station/model/OutOrderDispatchDecision.java
new file mode 100644
index 0000000..eec41b5
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/OutOrderDispatchDecision.java
@@ -0,0 +1,46 @@
+package com.zy.core.utils.station.model;
+
+import com.zy.core.service.StationTaskLoopService;
+
+public final 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,
+ StationTaskLoopService.LoopEvaluation loopEvaluation,
+ boolean countLoopIssue) {
+ this.targetStationId = targetStationId;
+ this.circle = circle;
+ this.loopEvaluation = loopEvaluation;
+ this.countLoopIssue = countLoopIssue;
+ }
+
+ public static OutOrderDispatchDecision direct(Integer targetStationId) {
+ return new OutOrderDispatchDecision(targetStationId, false, null, false);
+ }
+
+ public static OutOrderDispatchDecision circle(Integer targetStationId,
+ StationTaskLoopService.LoopEvaluation loopEvaluation,
+ boolean countLoopIssue) {
+ return new OutOrderDispatchDecision(targetStationId, true, loopEvaluation, countLoopIssue);
+ }
+
+ public Integer getTargetStationId() {
+ return targetStationId;
+ }
+
+ public boolean isCircle() {
+ return circle;
+ }
+
+ public StationTaskLoopService.LoopEvaluation getLoopEvaluation() {
+ return loopEvaluation;
+ }
+
+ public boolean shouldCountLoopIssue() {
+ return countLoopIssue;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/RerouteCommandPlan.java b/src/main/java/com/zy/core/utils/station/model/RerouteCommandPlan.java
new file mode 100644
index 0000000..1b70e5d
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/RerouteCommandPlan.java
@@ -0,0 +1,53 @@
+package com.zy.core.utils.station.model;
+
+import com.zy.core.model.command.StationCommand;
+
+public 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;
+ }
+
+ public static RerouteCommandPlan skip(String reason) {
+ return new RerouteCommandPlan(true, reason, null, null, null);
+ }
+
+ public static RerouteCommandPlan dispatch(StationCommand command,
+ RerouteDecision decision,
+ String dispatchScene) {
+ return new RerouteCommandPlan(false, null, command, decision, dispatchScene);
+ }
+
+ public boolean skip() {
+ return skip;
+ }
+
+ public String skipReason() {
+ return skipReason;
+ }
+
+ public StationCommand command() {
+ return command;
+ }
+
+ public RerouteDecision decision() {
+ return decision;
+ }
+
+ public String dispatchScene() {
+ return dispatchScene;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/RerouteContext.java b/src/main/java/com/zy/core/utils/station/model/RerouteContext.java
new file mode 100644
index 0000000..2f39856
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/RerouteContext.java
@@ -0,0 +1,185 @@
+package com.zy.core.utils.station.model;
+
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.StationTaskIdleTrack;
+
+import java.util.Collections;
+import java.util.List;
+
+public final class RerouteContext {
+ private final RerouteSceneType sceneType;
+ private final BasDevp basDevp;
+ private final StationThread stationThread;
+ private final StationProtocol stationProtocol;
+ private final WrkMast wrkMast;
+ private final List<Integer> outOrderStationIds;
+ private final Double pathLenFactor;
+ private final String dispatchScene;
+ private Integer dispatchDeviceNo;
+ private boolean useRunBlockCommand;
+ private boolean checkSuppressDispatch;
+ private boolean requireOutOrderDispatchLock;
+ private boolean cancelSessionBeforeDispatch;
+ private boolean resetSegmentCommandsBeforeDispatch;
+ private boolean clearIdleIssuedCommands;
+ private boolean checkRecentDispatch;
+ private String executionLockKey;
+ private int executionLockSeconds;
+ 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();
+ }
+
+ public static RerouteContext create(RerouteSceneType sceneType,
+ BasDevp basDevp,
+ StationThread stationThread,
+ StationProtocol stationProtocol,
+ WrkMast wrkMast,
+ List<Integer> outOrderStationIds,
+ Double pathLenFactor,
+ String dispatchScene) {
+ return new RerouteContext(sceneType, basDevp, stationThread, stationProtocol, wrkMast, outOrderStationIds, pathLenFactor, dispatchScene);
+ }
+
+ public RerouteContext withDispatchDeviceNo(Integer dispatchDeviceNo) {
+ this.dispatchDeviceNo = dispatchDeviceNo;
+ return this;
+ }
+
+ public RerouteContext withRunBlockCommand() {
+ this.useRunBlockCommand = true;
+ return this;
+ }
+
+ public RerouteContext withSuppressDispatchGuard() {
+ this.checkSuppressDispatch = true;
+ return this;
+ }
+
+ public RerouteContext withOutOrderDispatchLock() {
+ this.requireOutOrderDispatchLock = true;
+ return this;
+ }
+
+ public RerouteContext withCancelSessionBeforeDispatch() {
+ this.cancelSessionBeforeDispatch = true;
+ return this;
+ }
+
+ public RerouteContext withResetSegmentCommandsBeforeDispatch() {
+ this.resetSegmentCommandsBeforeDispatch = true;
+ return this;
+ }
+
+ public RerouteContext clearIdleIssuedCommands(StationTaskIdleTrack idleTrack) {
+ this.clearIdleIssuedCommands = true;
+ this.idleTrack = idleTrack;
+ return this;
+ }
+
+ public RerouteContext withRecentDispatchGuard() {
+ this.checkRecentDispatch = true;
+ return this;
+ }
+
+ public RerouteContext withExecutionLock(String executionLockKey, int executionLockSeconds) {
+ this.executionLockKey = executionLockKey;
+ this.executionLockSeconds = executionLockSeconds;
+ return this;
+ }
+
+ public RerouteSceneType sceneType() {
+ return sceneType;
+ }
+
+ public BasDevp basDevp() {
+ return basDevp;
+ }
+
+ public StationThread stationThread() {
+ return stationThread;
+ }
+
+ public StationProtocol stationProtocol() {
+ return stationProtocol;
+ }
+
+ public WrkMast wrkMast() {
+ return wrkMast;
+ }
+
+ public List<Integer> outOrderStationIds() {
+ return outOrderStationIds;
+ }
+
+ public Double pathLenFactor() {
+ return pathLenFactor;
+ }
+
+ public String dispatchScene() {
+ return dispatchScene;
+ }
+
+ public Integer dispatchDeviceNo() {
+ return dispatchDeviceNo;
+ }
+
+ public boolean useRunBlockCommand() {
+ return useRunBlockCommand;
+ }
+
+ public boolean checkSuppressDispatch() {
+ return checkSuppressDispatch;
+ }
+
+ public boolean requireOutOrderDispatchLock() {
+ return requireOutOrderDispatchLock;
+ }
+
+ public boolean cancelSessionBeforeDispatch() {
+ return cancelSessionBeforeDispatch;
+ }
+
+ public boolean resetSegmentCommandsBeforeDispatch() {
+ return resetSegmentCommandsBeforeDispatch;
+ }
+
+ public boolean clearIdleIssuedCommands() {
+ return clearIdleIssuedCommands;
+ }
+
+ public boolean checkRecentDispatch() {
+ return checkRecentDispatch;
+ }
+
+ public String executionLockKey() {
+ return executionLockKey;
+ }
+
+ public int executionLockSeconds() {
+ return executionLockSeconds;
+ }
+
+ public StationTaskIdleTrack idleTrack() {
+ return idleTrack;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/RerouteDecision.java b/src/main/java/com/zy/core/utils/station/model/RerouteDecision.java
new file mode 100644
index 0000000..f6a6810
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/RerouteDecision.java
@@ -0,0 +1,47 @@
+package com.zy.core.utils.station.model;
+
+public 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;
+ }
+
+ public static RerouteDecision skip(String reason) {
+ return new RerouteDecision(true, reason, null, null);
+ }
+
+ public static RerouteDecision proceed(Integer targetStationId) {
+ return new RerouteDecision(false, null, targetStationId, null);
+ }
+
+ public static RerouteDecision proceed(Integer targetStationId,
+ OutOrderDispatchDecision dispatchDecision) {
+ return new RerouteDecision(false, null, targetStationId, dispatchDecision);
+ }
+
+ public boolean skip() {
+ return skip;
+ }
+
+ public String skipReason() {
+ return skipReason;
+ }
+
+ public Integer targetStationId() {
+ return targetStationId;
+ }
+
+ public OutOrderDispatchDecision dispatchDecision() {
+ return dispatchDecision;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/RerouteExecutionResult.java b/src/main/java/com/zy/core/utils/station/model/RerouteExecutionResult.java
new file mode 100644
index 0000000..24dba39
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/RerouteExecutionResult.java
@@ -0,0 +1,52 @@
+package com.zy.core.utils.station.model;
+
+import com.zy.core.model.command.StationCommand;
+
+public 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;
+ }
+
+ public static RerouteExecutionResult skip(String reason) {
+ return new RerouteExecutionResult(true, reason, false, null, 0);
+ }
+
+ public static RerouteExecutionResult dispatched(StationCommand command,
+ int clearedCommandCount) {
+ return new RerouteExecutionResult(false, null, true, command, clearedCommandCount);
+ }
+
+ public boolean skipped() {
+ return skipped;
+ }
+
+ public String skipReason() {
+ return skipReason;
+ }
+
+ public boolean dispatched() {
+ return dispatched;
+ }
+
+ public StationCommand command() {
+ return command;
+ }
+
+ public int clearedCommandCount() {
+ return clearedCommandCount;
+ }
+}
diff --git a/src/main/java/com/zy/core/utils/station/model/RerouteSceneType.java b/src/main/java/com/zy/core/utils/station/model/RerouteSceneType.java
new file mode 100644
index 0000000..3132400
--- /dev/null
+++ b/src/main/java/com/zy/core/utils/station/model/RerouteSceneType.java
@@ -0,0 +1,8 @@
+package com.zy.core.utils.station.model;
+
+public enum RerouteSceneType {
+ RUN_BLOCK_REROUTE,
+ IDLE_RECOVER,
+ OUT_ORDER,
+ WATCH_CIRCLE
+}
diff --git a/src/test/java/com/zy/asrs/controller/StationControllerTest.java b/src/test/java/com/zy/asrs/controller/StationControllerTest.java
index 5983e47..c6dcd32 100644
--- a/src/test/java/com/zy/asrs/controller/StationControllerTest.java
+++ b/src/test/java/com/zy/asrs/controller/StationControllerTest.java
@@ -4,8 +4,12 @@
import com.zy.asrs.domain.param.StationCommandMoveParam;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.service.BasDevpService;
+import com.zy.core.dispatch.StationCommandDispatchResult;
+import com.zy.core.dispatch.StationCommandDispatcher;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.SlaveType;
+import com.zy.core.enums.StationCommandType;
+import com.zy.core.model.command.StationCommand;
import com.zy.core.thread.StationThread;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
@@ -13,12 +17,21 @@
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class StationControllerTest {
+
+ @Test
+ void controller_noLongerKeepsDispatcherFallbackHelper() {
+ assertThrows(NoSuchMethodException.class,
+ () -> StationController.class.getDeclaredMethod("getStationCommandDispatcher"));
+ }
@Test
void commandClearPath_callsThreadClearPath() {
@@ -48,4 +61,39 @@
SlaveConnection.remove(SlaveType.Devp, 1);
}
}
+
+ @Test
+ void commandMove_dispatchesViaStationCommandDispatcher() {
+ StationController controller = new StationController();
+ BasDevpService basDevpService = mock(BasDevpService.class);
+ StationThread stationThread = mock(StationThread.class);
+ StationCommandDispatcher dispatcher = mock(StationCommandDispatcher.class);
+ StationCommand command = new StationCommand();
+
+ ReflectionTestUtils.setField(controller, "basDevpService", basDevpService);
+ ReflectionTestUtils.setField(controller, "stationCommandDispatcher", dispatcher);
+
+ BasDevp basDevp = new BasDevp();
+ basDevp.setStationList("[{\"deviceNo\":1,\"stationId\":145}]");
+ when(basDevpService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(basDevp));
+ when(stationThread.getCommand(StationCommandType.MOVE, 10335, 145, 188, 0)).thenReturn(command);
+ when(dispatcher.dispatch(1, command, "station-controller", "manual-move"))
+ .thenReturn(StationCommandDispatchResult.accepted("accepted", 1, "station-controller", "manual-move"));
+
+ StationCommandMoveParam param = new StationCommandMoveParam();
+ param.setStationId(145);
+ param.setTaskNo(10335);
+ param.setTargetStationId(188);
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ R result = controller.commandMove(param);
+
+ assertEquals(200, result.get("code"));
+ verify(dispatcher).dispatch(eq(1), same(command), eq("station-controller"), eq("manual-move"));
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
}
diff --git a/src/test/java/com/zy/core/dispatch/StationCommandDispatcherTest.java b/src/test/java/com/zy/core/dispatch/StationCommandDispatcherTest.java
new file mode 100644
index 0000000..aab4866
--- /dev/null
+++ b/src/test/java/com/zy/core/dispatch/StationCommandDispatcherTest.java
@@ -0,0 +1,100 @@
+package com.zy.core.dispatch;
+
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.cache.MessageQueue;
+import com.zy.core.enums.SlaveType;
+import com.zy.core.enums.StationCommandType;
+import com.zy.core.model.command.StationCommand;
+import com.zy.core.move.StationMoveCoordinator;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class StationCommandDispatcherTest {
+
+ @Test
+ void dispatch_acceptsMoveCommandAndReturnsQueueDepth() {
+ StationCommandDispatcher dispatcher = new StationCommandDispatcher();
+
+ StationCommand command = new StationCommand();
+ command.setCommandType(StationCommandType.MOVE);
+ command.setTaskNo(100);
+ command.setStationId(10);
+ command.setTargetStaNo(20);
+
+ MessageQueue.init(SlaveType.Devp, 1);
+ try {
+ StationCommandDispatchResult result = dispatcher.dispatch(1, command, "unit-test", "move");
+
+ assertTrue(result.isAccepted());
+ assertEquals("accepted", result.getReason());
+ assertEquals(1, result.getQueueDepth());
+ assertEquals("unit-test", result.getSource());
+ assertEquals("move", result.getScene());
+ } finally {
+ MessageQueue.clear(SlaveType.Devp, 1);
+ }
+ }
+
+ @Test
+ void dispatch_suppressesDuplicateMoveCommandWithinDedupWindow() {
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationCommandDispatcher dispatcher = new StationCommandDispatcher(redisUtil, coordinator);
+ Map<String, Object> dedupStore = new HashMap<>();
+
+ when(redisUtil.get(anyString())).thenAnswer(invocation -> dedupStore.get(invocation.getArgument(0)));
+ when(redisUtil.set(anyString(), eq("lock"), anyLong())).thenAnswer(invocation -> {
+ dedupStore.put(invocation.getArgument(0), invocation.getArgument(1));
+ return true;
+ });
+ when(coordinator.buildPathSignatureHash(org.mockito.ArgumentMatchers.any(StationCommand.class)))
+ .thenReturn("same-path");
+
+ StationCommand command = new StationCommand();
+ command.setCommandType(StationCommandType.MOVE);
+ command.setTaskNo(100);
+ command.setStationId(10);
+ command.setTargetStaNo(20);
+
+ MessageQueue.init(SlaveType.Devp, 1);
+ try {
+ StationCommandDispatchResult first = dispatcher.dispatch(1, command, "unit-test", "move");
+ StationCommandDispatchResult second = dispatcher.dispatch(1, command, "unit-test", "move");
+
+ assertTrue(first.isAccepted());
+ assertFalse(second.isAccepted());
+ assertEquals("dedup-suppressed", second.getReason());
+ assertEquals(1, second.getQueueDepth());
+ } finally {
+ MessageQueue.clear(SlaveType.Devp, 1);
+ }
+ }
+
+ @Test
+ void dispatch_rejectsWhenDevpQueueIsNotInitialized() {
+ StationCommandDispatcher dispatcher = new StationCommandDispatcher();
+
+ StationCommand command = new StationCommand();
+ command.setCommandType(StationCommandType.MOVE);
+ command.setTaskNo(100);
+ command.setStationId(10);
+ command.setTargetStaNo(20);
+
+ StationCommandDispatchResult result = dispatcher.dispatch(999, command, "unit-test", "move");
+
+ assertFalse(result.isAccepted());
+ assertEquals("queue-not-initialized", result.getReason());
+ assertEquals(0, result.getQueueDepth());
+ }
+}
diff --git a/src/test/java/com/zy/core/thread/impl/ZyStationV5ThreadTest.java b/src/test/java/com/zy/core/thread/impl/ZyStationV5ThreadTest.java
index bb1da07..ad2ac25 100644
--- a/src/test/java/com/zy/core/thread/impl/ZyStationV5ThreadTest.java
+++ b/src/test/java/com/zy/core/thread/impl/ZyStationV5ThreadTest.java
@@ -2,9 +2,15 @@
import com.zy.asrs.entity.DeviceConfig;
import com.zy.common.utils.RedisUtil;
+import com.zy.core.cache.MessageQueue;
+import com.zy.core.enums.SlaveType;
+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.network.ZyStationConnectDriver;
+import com.zy.core.thread.impl.v5.StationV5SegmentExecutor;
+import com.zy.core.thread.impl.v5.StationV5StatusReader;
import org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
@@ -17,10 +23,35 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
class ZyStationV5ThreadTest {
+
+ @Test
+ void pollAndDispatchQueuedCommand_submitsQueuedMoveCommandToSegmentExecutor() {
+ DeviceConfig deviceConfig = new DeviceConfig();
+ deviceConfig.setDeviceNo(1);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationCommand command = new StationCommand();
+ StationV5SegmentExecutor segmentExecutor = mock(StationV5SegmentExecutor.class);
+
+ ZyStationV5Thread thread = new ZyStationV5Thread(deviceConfig, redisUtil);
+ ReflectionTestUtils.setField(thread, "segmentExecutor", segmentExecutor);
+
+ MessageQueue.init(SlaveType.Devp, 1);
+ try {
+ MessageQueue.offer(SlaveType.Devp, 1, new Task(2, command));
+
+ ReflectionTestUtils.invokeMethod(thread, "pollAndDispatchQueuedCommand");
+
+ verify(segmentExecutor, timeout(1000)).execute(command);
+ } finally {
+ MessageQueue.clear(SlaveType.Devp, 1);
+ thread.close();
+ }
+ }
@Test
void clearPath_delegatesPureSlotClearingToDriver() {
@@ -46,7 +77,8 @@
station10.setStationId(10);
station10.setTaskBufferItems(List.of(hitItem));
- ReflectionTestUtils.setField(thread, "statusList", Arrays.asList(station20, station10));
+ StationV5StatusReader statusReader = (StationV5StatusReader) ReflectionTestUtils.getField(thread, "statusReader");
+ ReflectionTestUtils.setField(statusReader, "statusList", Arrays.asList(station20, station10));
boolean result = thread.clearPath(100);
diff --git a/src/test/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlannerTest.java b/src/test/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlannerTest.java
new file mode 100644
index 0000000..be14570
--- /dev/null
+++ b/src/test/java/com/zy/core/thread/impl/v5/StationV5RunBlockReroutePlannerTest.java
@@ -0,0 +1,110 @@
+package com.zy.core.thread.impl.v5;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.model.command.StationCommand;
+import com.zy.core.service.StationTaskLoopService;
+import org.junit.jupiter.api.Test;
+
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class StationV5RunBlockReroutePlannerTest {
+
+ @Test
+ void plan_prefersLongerCandidateWhenShortestPathIsOverusedInsideTriggeredLoop() {
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ String stateKey = RedisKeyType.STATION_RUN_BLOCK_REROUTE_STATE_.key + "100_10";
+ when(redisUtil.get(stateKey)).thenReturn(JSON.toJSONString(seedState(
+ 100,
+ 10,
+ 2,
+ List.of(),
+ Map.of("10->11->20", 2)
+ )));
+
+ StationV5RunBlockReroutePlanner planner = new StationV5RunBlockReroutePlanner(redisUtil);
+ StationCommand shortest = moveCommand(100, 10, 20, 10, 11, 20);
+ StationCommand longer = moveCommand(100, 10, 20, 10, 31, 32, 20);
+
+ StationTaskLoopService.LoopIdentitySnapshot loopIdentity =
+ new StationTaskLoopService.LoopIdentitySnapshot("10|11|20", new HashSet<>(List.of(10, 11, 20)), 3, 3, "wholeLoop");
+ StationTaskLoopService.LoopEvaluation loopEvaluation =
+ new StationTaskLoopService.LoopEvaluation(100, 10, loopIdentity, 2, 3, true);
+
+ StationV5RunBlockReroutePlanner.PlanResult result = planner.plan(
+ 100,
+ 10,
+ loopEvaluation,
+ List.of(shortest, longer)
+ );
+
+ assertSame(longer, result.getCommand());
+ assertEquals(3, result.getPlanCount());
+ }
+
+ @Test
+ void plan_resetsIssuedRoutesWhenAllCandidatesHaveBeenTried() {
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ String stateKey = RedisKeyType.STATION_RUN_BLOCK_REROUTE_STATE_.key + "100_10";
+ when(redisUtil.get(stateKey)).thenReturn(JSON.toJSONString(seedState(
+ 100,
+ 10,
+ 1,
+ List.of(List.of(10, 11, 20), List.of(10, 31, 32, 20)),
+ Map.of("10->11->20", 1, "10->31->32->20", 1)
+ )));
+
+ StationV5RunBlockReroutePlanner planner = new StationV5RunBlockReroutePlanner(redisUtil);
+ StationCommand first = moveCommand(100, 10, 20, 10, 11, 20);
+ StationCommand second = moveCommand(100, 10, 20, 10, 31, 32, 20);
+
+ StationTaskLoopService.LoopEvaluation loopEvaluation =
+ new StationTaskLoopService.LoopEvaluation(100, 10, StationTaskLoopService.LoopIdentitySnapshot.empty(), 0, 0, false);
+
+ StationV5RunBlockReroutePlanner.PlanResult result = planner.plan(
+ 100,
+ 10,
+ loopEvaluation,
+ List.of(first, second)
+ );
+
+ assertSame(first, result.getCommand());
+ assertEquals(2, result.getPlanCount());
+ }
+
+ private static StationCommand moveCommand(Integer taskNo,
+ Integer stationId,
+ Integer targetStationId,
+ Integer... path) {
+ StationCommand command = new StationCommand();
+ command.setTaskNo(taskNo);
+ command.setStationId(stationId);
+ command.setTargetStaNo(targetStationId);
+ command.setNavigatePath(List.of(path));
+ return command;
+ }
+
+ private static JSONObject seedState(Integer taskNo,
+ Integer blockStationId,
+ Integer planCount,
+ List<List<Integer>> issuedRoutePathList,
+ Map<String, Integer> routeIssueCountMap) {
+ JSONObject state = new JSONObject();
+ state.put("taskNo", taskNo);
+ state.put("blockStationId", blockStationId);
+ state.put("planCount", planCount);
+ state.put("issuedRoutePathList", issuedRoutePathList);
+ state.put("routeIssueCountMap", new HashMap<>(routeIssueCountMap));
+ return state;
+ }
+}
diff --git a/src/test/java/com/zy/core/utils/StationOperateProcessUtilsReroutePipelineTest.java b/src/test/java/com/zy/core/utils/StationOperateProcessUtilsReroutePipelineTest.java
index 8abda6e..cdc99b8 100644
--- a/src/test/java/com/zy/core/utils/StationOperateProcessUtilsReroutePipelineTest.java
+++ b/src/test/java/com/zy/core/utils/StationOperateProcessUtilsReroutePipelineTest.java
@@ -21,16 +21,34 @@
import com.zy.core.move.StationMoveCoordinator;
import com.zy.core.move.StationMoveDispatchMode;
import com.zy.core.move.StationMoveSession;
+import com.zy.core.dispatch.StationCommandDispatchResult;
+import com.zy.core.dispatch.StationCommandDispatcher;
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.thread.StationThread;
import com.zy.common.utils.RedisUtil;
+import com.zy.core.utils.station.StationDispatchLoadSupport;
+import com.zy.core.utils.station.StationDispatchRuntimeStateSupport;
+import com.zy.core.utils.station.StationOutboundDecisionSupport;
+import com.zy.core.utils.station.StationOutboundDispatchProcessor;
+import com.zy.core.utils.station.StationRegularDispatchProcessor;
+import com.zy.core.utils.station.StationRerouteProcessor;
+import com.zy.core.utils.station.model.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+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 org.junit.jupiter.api.Test;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.Date;
import java.util.Collections;
+import java.util.HashMap;
+import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -51,6 +69,89 @@
import static org.mockito.Mockito.when;
class StationOperateProcessUtilsReroutePipelineTest {
+ private final Map<StationOperateProcessUtils, Map<String, Object>> dependencyOverrides = new IdentityHashMap<>();
+
+ private StationOperateProcessUtils newUtils() {
+ StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ stash(utils, "stationOutboundDecisionSupport", new StationOutboundDecisionSupport());
+ wireRerouteProcessor(utils);
+ return utils;
+ }
+
+ private void wireOutboundSupport(StationOperateProcessUtils utils) {
+ StationOutboundDecisionSupport support = new StationOutboundDecisionSupport();
+ copyIfPresent(utils, support, "wrkMastService");
+ copyIfPresent(utils, support, "basDevpService");
+ copyIfPresent(utils, support, "basStationService");
+ copyIfPresent(utils, support, "navigateUtils");
+ copyIfPresent(utils, support, "redisUtil");
+ copyIfPresent(utils, support, "stationTaskLoopService");
+ copyIfPresent(utils, support, "stationMoveCoordinator");
+ StationDispatchRuntimeStateSupport runtimeStateSupport = new StationDispatchRuntimeStateSupport();
+ copyIfPresent(utils, runtimeStateSupport, "redisUtil");
+ copyIfPresent(utils, runtimeStateSupport, "basStationOptService");
+ ReflectionTestUtils.setField(support, "stationDispatchRuntimeStateSupport", runtimeStateSupport);
+ stash(utils, "stationOutboundDecisionSupport", support);
+ }
+
+ private void wireRerouteProcessor(StationOperateProcessUtils utils) {
+ StationRerouteProcessor processor = new StationRerouteProcessor();
+ copyIfPresent(utils, processor, "basDevpService");
+ copyIfPresent(utils, processor, "wrkMastService");
+ copyIfPresent(utils, processor, "commonService");
+ copyIfPresent(utils, processor, "redisUtil");
+ copyIfPresent(utils, processor, "locMastService");
+ copyIfPresent(utils, processor, "wmsOperateUtils");
+ copyIfPresent(utils, processor, "basStationOptService");
+ copyIfPresent(utils, processor, "stationMoveCoordinator");
+ Object dispatcher = readIfPresent(utils, "stationCommandDispatcher");
+ if (dispatcher == null) {
+ dispatcher = new StationCommandDispatcher(
+ (RedisUtil) readIfPresent(utils, "redisUtil"),
+ (StationMoveCoordinator) readIfPresent(utils, "stationMoveCoordinator")
+ );
+ }
+ ReflectionTestUtils.setField(processor, "stationCommandDispatcher", dispatcher);
+ Object outboundSupport = readIfPresent(utils, "stationOutboundDecisionSupport");
+ if (outboundSupport == null) {
+ wireOutboundSupport(utils);
+ outboundSupport = readIfPresent(utils, "stationOutboundDecisionSupport");
+ }
+ if (outboundSupport != null) {
+ ReflectionTestUtils.setField(processor, "stationOutboundDecisionSupport", outboundSupport);
+ }
+ StationDispatchRuntimeStateSupport runtimeStateSupport = new StationDispatchRuntimeStateSupport();
+ copyIfPresent(utils, runtimeStateSupport, "redisUtil");
+ copyIfPresent(utils, runtimeStateSupport, "basStationOptService");
+ ReflectionTestUtils.setField(processor, "stationDispatchRuntimeStateSupport", runtimeStateSupport);
+ ReflectionTestUtils.setField(utils, "stationRerouteProcessor", processor);
+ }
+
+ private void stash(StationOperateProcessUtils utils, String fieldName, Object value) {
+ dependencyOverrides.computeIfAbsent(utils, key -> new HashMap<>()).put(fieldName, value);
+ }
+
+ private Object readIfPresent(Object source, String fieldName) {
+ try {
+ return ReflectionTestUtils.getField(source, fieldName);
+ } catch (IllegalArgumentException ignore) {
+ if (source instanceof StationOperateProcessUtils) {
+ Map<String, Object> values = dependencyOverrides.get(source);
+ return values == null ? null : values.get(fieldName);
+ }
+ return null;
+ }
+ }
+
+ private void copyIfPresent(Object source, Object target, String fieldName) {
+ Object value = readIfPresent(source, fieldName);
+ if (value != null) {
+ try {
+ ReflectionTestUtils.setField(target, fieldName, value);
+ } catch (IllegalArgumentException ignore) {
+ }
+ }
+ }
@SuppressWarnings("unchecked")
private void stubTaskDispatchLock(StationMoveCoordinator coordinator) {
@@ -62,16 +163,14 @@
@Test
void choosesRunBlockCommandBuilderForRunBlockRerouteScene() {
- StationOperateProcessUtils.RerouteSceneType scene =
- StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE;
+ RerouteSceneType scene = RerouteSceneType.RUN_BLOCK_REROUTE;
- assertSame(StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE, scene);
+ assertSame(RerouteSceneType.RUN_BLOCK_REROUTE, scene);
}
@Test
void resolveExecutionTarget_skipsWhenTargetEqualsCurrentStation() {
- StationOperateProcessUtils.RerouteDecision decision =
- StationOperateProcessUtils.RerouteDecision.skip("same-station");
+ RerouteDecision decision = RerouteDecision.skip("same-station");
assertTrue(decision.skip());
assertEquals("same-station", decision.skipReason());
@@ -79,7 +178,7 @@
@Test
void buildCommandPlan_usesRunBlockCommandBuilderForRunBlockScene() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationThread stationThread = mock(StationThread.class);
StationCommand command = new StationCommand();
command.setTaskNo(100);
@@ -87,8 +186,8 @@
command.setTargetStaNo(20);
when(stationThread.getRunBlockRerouteCommand(100, 10, 20, 0, 0.25d)).thenReturn(command);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.RUN_BLOCK_REROUTE,
buildBasDevp(1),
stationThread,
buildStationProtocol(10, 100, 10),
@@ -100,9 +199,9 @@
.withCancelSessionBeforeDispatch()
.withResetSegmentCommandsBeforeDispatch();
- StationOperateProcessUtils.RerouteCommandPlan plan = utils.buildRerouteCommandPlan(
+ RerouteCommandPlan plan = utils.buildRerouteCommandPlan(
context,
- StationOperateProcessUtils.RerouteDecision.proceed(20)
+ RerouteDecision.proceed(20)
);
verify(stationThread).getRunBlockRerouteCommand(100, 10, 20, 0, 0.25d);
@@ -111,7 +210,7 @@
@Test
void executePlan_skipsWhenCurrentTaskStillExistsInBuffer() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationCommand command = new StationCommand();
command.setTaskNo(100);
command.setStationId(10);
@@ -120,8 +219,8 @@
StationTaskBufferItem bufferItem = new StationTaskBufferItem();
bufferItem.setTaskNo(100);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.OUT_ORDER,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.OUT_ORDER,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(10, 100, 10, Collections.singletonList(bufferItem)),
@@ -131,11 +230,11 @@
"checkStationOutOrder"
);
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(20),
+ RerouteDecision.proceed(20),
"checkStationOutOrder"
)
);
@@ -146,11 +245,11 @@
@Test
void outOrderAndWatchCircle_shareDecisionFlow() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
WrkMast wrkMast = buildWrkMast(100, 20);
- StationOperateProcessUtils.RerouteContext outOrderContext = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.OUT_ORDER,
+ RerouteContext outOrderContext = RerouteContext.create(
+ RerouteSceneType.OUT_ORDER,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(10, 100, 10),
@@ -159,8 +258,8 @@
0.0d,
"checkStationOutOrder"
);
- StationOperateProcessUtils.RerouteContext watchCircleContext = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.WATCH_CIRCLE,
+ RerouteContext watchCircleContext = RerouteContext.create(
+ RerouteSceneType.WATCH_CIRCLE,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(10, 100, 10),
@@ -170,8 +269,8 @@
"watchCircleStation"
);
- StationOperateProcessUtils.RerouteDecision outOrderDecision = utils.resolveSharedRerouteDecision(outOrderContext);
- StationOperateProcessUtils.RerouteDecision watchCircleDecision = utils.resolveSharedRerouteDecision(watchCircleContext);
+ RerouteDecision outOrderDecision = utils.resolveSharedRerouteDecision(outOrderContext);
+ RerouteDecision watchCircleDecision = utils.resolveSharedRerouteDecision(watchCircleContext);
assertEquals(20, outOrderDecision.targetStationId());
assertEquals(20, watchCircleDecision.targetStationId());
@@ -179,7 +278,7 @@
@Test
void runBlockReroute_keepsDirectReassignAndNormalRerouteSeparate() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
WrkMast inboundWrkMast = buildWrkMast(100, 20);
inboundWrkMast.setIoType(WrkIoType.IN.id);
@@ -189,12 +288,13 @@
@Test
void idleRecover_skipsWhenLastDispatchIsTooRecent() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationMoveSession session = new StationMoveSession();
session.setStatus(StationMoveSession.STATUS_RUNNING);
@@ -208,8 +308,8 @@
command.setStationId(10);
command.setTargetStaNo(20);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.IDLE_RECOVER,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.IDLE_RECOVER,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(10, 100, 10),
@@ -219,11 +319,11 @@
"checkStationIdleRecover"
).withRecentDispatchGuard();
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(20),
+ RerouteDecision.proceed(20),
"checkStationIdleRecover"
)
);
@@ -235,12 +335,13 @@
@Test
void idleRecover_skipsWhenCurrentStationIsStillInsideRecentlyIssuedActiveRoute() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationMoveSession session = new StationMoveSession();
session.setStatus(StationMoveSession.STATUS_RUNNING);
@@ -255,8 +356,8 @@
command.setStationId(121);
command.setTargetStaNo(124);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.IDLE_RECOVER,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.IDLE_RECOVER,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(121, 10510, 121),
@@ -266,11 +367,11 @@
"checkStationIdleRecover"
).withRecentDispatchGuard();
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(124),
+ RerouteDecision.proceed(124),
"checkStationIdleRecover"
)
);
@@ -282,14 +383,15 @@
@Test
void idleRecover_skipsWhenStationCommandLogShowsRecentIssuedMove() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
BasStationOptService basStationOptService = mock(BasStationOptService.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "basStationOptService", basStationOptService);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "basStationOptService", basStationOptService);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationMoveSession session = new StationMoveSession();
session.setStatus(StationMoveSession.STATUS_RUNNING);
@@ -318,7 +420,7 @@
@Test
void checkStationOutOrder_skipsWhenActiveSessionAlreadyOwnsCurrentStation() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
BasDevpService basDevpService = mock(BasDevpService.class);
WrkMastService wrkMastService = mock(WrkMastService.class);
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
@@ -326,10 +428,12 @@
StationThread stationThread = mock(StationThread.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "basDevpService", basDevpService);
+ stash(utils, "basDevpService", basDevpService);
ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireOutboundSupport(utils);
+ wireRerouteProcessor(utils);
BasDevp basDevp = buildBasDevp(1);
basDevp.setIsOutOrderList("[{\"deviceNo\":1,\"stationId\":145}]");
@@ -380,7 +484,7 @@
@Test
void checkStationOutOrder_restartsWhenBlockedSessionExistsButStationNoLongerRunBlock() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
BasDevpService basDevpService = mock(BasDevpService.class);
WrkMastService wrkMastService = mock(WrkMastService.class);
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
@@ -388,10 +492,12 @@
StationThread stationThread = mock(StationThread.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "basDevpService", basDevpService);
+ stash(utils, "basDevpService", basDevpService);
ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireOutboundSupport(utils);
+ wireRerouteProcessor(utils);
BasDevp basDevp = buildBasDevp(1);
basDevp.setIsOutOrderList("[{\"deviceNo\":1,\"stationId\":145}]");
@@ -443,15 +549,17 @@
@Test
void checkStationOutOrder_skipsRunBlockStationBeforePlanning() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
BasDevpService basDevpService = mock(BasDevpService.class);
WrkMastService wrkMastService = mock(WrkMastService.class);
RedisUtil redisUtil = mock(RedisUtil.class);
StationThread stationThread = mock(StationThread.class);
- ReflectionTestUtils.setField(utils, "basDevpService", basDevpService);
+ stash(utils, "basDevpService", basDevpService);
ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "redisUtil", redisUtil);
+ wireOutboundSupport(utils);
+ wireRerouteProcessor(utils);
BasDevp basDevp = buildBasDevp(1);
basDevp.setIsOutOrderList("[{\"deviceNo\":1,\"stationId\":145}]");
@@ -493,12 +601,13 @@
@Test
void executePlan_runBlockReroute_reissuesWhenBlockedSessionMatchesCandidatePath() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationCommand command = new StationCommand();
command.setTaskNo(100);
@@ -516,8 +625,8 @@
when(coordinator.loadSession(100)).thenReturn(session);
when(coordinator.buildPathSignature(command)).thenReturn("same-path");
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.RUN_BLOCK_REROUTE,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(145, 100, 145),
@@ -530,11 +639,11 @@
MessageQueue.init(SlaveType.Devp, 1);
try {
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(111),
+ RerouteDecision.proceed(111),
"checkStationRunBlock_reroute"
)
);
@@ -551,12 +660,13 @@
@Test
void executePlan_runBlockReroute_ignoresCurrentTaskBufferAfterReset() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationCommand command = new StationCommand();
command.setTaskNo(10388);
@@ -570,8 +680,8 @@
when(redisUtil.get(anyString())).thenReturn(null);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.RUN_BLOCK_REROUTE,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(186, 10388, 189, Collections.singletonList(bufferItem)),
@@ -584,11 +694,11 @@
MessageQueue.init(SlaveType.Devp, 1);
try {
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(124),
+ RerouteDecision.proceed(124),
"checkStationRunBlock_reroute"
)
);
@@ -602,12 +712,13 @@
@Test
void executePlan_runBlockReroute_bypassesSuppressDispatchAfterReset() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
RedisUtil redisUtil = mock(RedisUtil.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireRerouteProcessor(utils);
StationCommand command = new StationCommand();
command.setTaskNo(10388);
@@ -619,8 +730,8 @@
when(redisUtil.get(anyString())).thenReturn(null);
when(coordinator.shouldSuppressDispatch(10388, 186, command)).thenReturn(true);
- StationOperateProcessUtils.RerouteContext context = StationOperateProcessUtils.RerouteContext.create(
- StationOperateProcessUtils.RerouteSceneType.RUN_BLOCK_REROUTE,
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.RUN_BLOCK_REROUTE,
buildBasDevp(1),
mock(StationThread.class),
buildStationProtocol(186, 10388, 186),
@@ -634,11 +745,11 @@
MessageQueue.init(SlaveType.Devp, 1);
try {
- StationOperateProcessUtils.RerouteExecutionResult result = utils.executeReroutePlan(
+ RerouteExecutionResult result = utils.executeReroutePlan(
context,
- StationOperateProcessUtils.RerouteCommandPlan.dispatch(
+ RerouteCommandPlan.dispatch(
command,
- StationOperateProcessUtils.RerouteDecision.proceed(124),
+ RerouteDecision.proceed(124),
"checkStationRunBlock_reroute"
)
);
@@ -652,22 +763,28 @@
@Test
void stationInExecute_recordsDispatchSessionAfterIssuingMoveCommand() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
BasDevpService basDevpService = mock(BasDevpService.class);
WrkMastService wrkMastService = mock(WrkMastService.class);
CommonService commonService = mock(CommonService.class);
RedisUtil redisUtil = mock(RedisUtil.class);
WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationCommandDispatcher dispatcher = mock(StationCommandDispatcher.class);
+ StationDispatchLoadSupport loadSupport = mock(StationDispatchLoadSupport.class);
StationThread stationThread = mock(StationThread.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "basDevpService", basDevpService);
- ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "commonService", commonService);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
- ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
+ StationRegularDispatchProcessor processor = new StationRegularDispatchProcessor();
+ ReflectionTestUtils.setField(processor, "basDevpService", basDevpService);
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "commonService", commonService);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "wrkAnalysisService", wrkAnalysisService);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(processor, "stationCommandDispatcher", dispatcher);
+ ReflectionTestUtils.setField(processor, "stationDispatchLoadSupport", loadSupport);
+ ReflectionTestUtils.setField(utils, "stationRegularDispatchProcessor", processor);
BasDevp basDevp = buildBasDevp(1);
basDevp.setBarcodeStationList("[{\"deviceNo\":1,\"stationId\":101}]");
@@ -700,6 +817,18 @@
when(stationThread.getCommand(com.zy.core.enums.StationCommandType.MOVE, 500670, 101, 102, 0))
.thenReturn(command);
when(redisUtil.get(anyString())).thenReturn(null);
+ when(dispatcher.dispatch(1, command, "station-operate-process", "stationInExecute"))
+ .thenReturn(StationCommandDispatchResult.accepted("accepted", 1, "station-operate-process", "stationInExecute"));
+
+ DispatchLimitConfig baseConfig = new DispatchLimitConfig();
+ LoadGuardState loadGuardState = new LoadGuardState();
+ LoopHitResult noHit = LoopHitResult.noHit();
+ when(loadSupport.getDispatchLimitConfig(null, null)).thenReturn(baseConfig);
+ when(loadSupport.countCurrentStationTask()).thenReturn(0);
+ when(loadSupport.buildLoadGuardState(baseConfig)).thenReturn(loadGuardState);
+ when(loadSupport.getDispatchLimitConfig(101, 102)).thenReturn(baseConfig);
+ when(loadSupport.findPathLoopHit(baseConfig, 101, 102, loadGuardState)).thenReturn(noHit);
+ when(loadSupport.isDispatchBlocked(baseConfig, 0, loadGuardState, false)).thenReturn(false);
MessageQueue.init(SlaveType.Devp, 1);
SlaveConnection.put(SlaveType.Devp, 1, stationThread);
@@ -714,62 +843,19 @@
}
@Test
- void dualCrnStationOutExecute_recordsDispatchSessionAfterIssuingMoveCommand() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
- WrkMastService wrkMastService = mock(WrkMastService.class);
- RedisUtil redisUtil = mock(RedisUtil.class);
- NotifyUtils notifyUtils = mock(NotifyUtils.class);
- StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
- StationThread stationThread = mock(StationThread.class);
- stubTaskDispatchLock(coordinator);
+ void dualCrnStationOutExecute_delegatesToOutboundProcessor() {
+ StationOperateProcessUtils utils = newUtils();
+ StationOutboundDispatchProcessor processor = mock(StationOutboundDispatchProcessor.class);
+ ReflectionTestUtils.setField(utils, "stationOutboundDispatchProcessor", processor);
- ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
- ReflectionTestUtils.setField(utils, "notifyUtils", notifyUtils);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
+ utils.dualCrnStationOutExecute();
- WrkMast wrkMast = buildWrkMast(10335, 145);
- wrkMast.setDualCrnNo(1);
- wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN_COMPLETE.sts);
- when(wrkMastService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
- .thenReturn(Collections.singletonList(wrkMast));
- when(wrkMastService.updateById(wrkMast)).thenReturn(true);
-
- when(redisUtil.get(anyString())).thenAnswer(invocation -> {
- String key = invocation.getArgument(0);
- if (Objects.equals(key, RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + 10335)) {
- return "{\"deviceNo\":1,\"stationId\":198}";
- }
- return null;
- });
-
- StationProtocol stationProtocol = buildStationProtocol(198, 0, 198);
- stationProtocol.setAutoing(true);
- stationProtocol.setLoading(true);
- when(stationThread.getStatusMap()).thenReturn(Map.of(198, stationProtocol));
-
- StationCommand command = new StationCommand();
- command.setTaskNo(10335);
- command.setStationId(198);
- command.setTargetStaNo(145);
- when(stationThread.getCommand(eq(com.zy.core.enums.StationCommandType.MOVE), eq(10335), eq(198), eq(145), eq(0), eq(0.0d)))
- .thenReturn(command);
-
- MessageQueue.init(SlaveType.Devp, 1);
- SlaveConnection.put(SlaveType.Devp, 1, stationThread);
- try {
- utils.dualCrnStationOutExecute();
-
- verify(coordinator, times(1)).recordDispatch(eq(10335), eq(198), eq("dualCrnStationOutExecute"), same(command), eq(false));
- } finally {
- MessageQueue.clear(SlaveType.Devp, 1);
- SlaveConnection.remove(SlaveType.Devp, 1);
- }
+ verify(processor, times(1)).dualCrnStationOutExecute();
}
@Test
void stationOutExecuteFinish_attemptsClearPathBeforeCompletingTask() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
WrkMastService wrkMastService = mock(WrkMastService.class);
BasStationService basStationService = mock(BasStationService.class);
WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
@@ -778,12 +864,14 @@
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
StationThread stationThread = mock(StationThread.class);
- ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "basStationService", basStationService);
- ReflectionTestUtils.setField(utils, "wrkAnalysisService", wrkAnalysisService);
- ReflectionTestUtils.setField(utils, "notifyUtils", notifyUtils);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
+ StationRegularDispatchProcessor processor = new StationRegularDispatchProcessor();
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "basStationService", basStationService);
+ ReflectionTestUtils.setField(processor, "wrkAnalysisService", wrkAnalysisService);
+ ReflectionTestUtils.setField(processor, "notifyUtils", notifyUtils);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(utils, "stationRegularDispatchProcessor", processor);
WrkMast wrkMast = buildWrkMast(10335, 145);
wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts);
@@ -816,7 +904,7 @@
@Test
void watchCircleStation_usesSessionArrivalStateWhenLegacyCommandMissing() {
- StationOperateProcessUtils utils = new StationOperateProcessUtils();
+ StationOperateProcessUtils utils = newUtils();
BasDevpService basDevpService = mock(BasDevpService.class);
WrkMastService wrkMastService = mock(WrkMastService.class);
StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
@@ -824,10 +912,12 @@
StationThread stationThread = mock(StationThread.class);
stubTaskDispatchLock(coordinator);
- ReflectionTestUtils.setField(utils, "basDevpService", basDevpService);
+ stash(utils, "basDevpService", basDevpService);
ReflectionTestUtils.setField(utils, "wrkMastService", wrkMastService);
- ReflectionTestUtils.setField(utils, "stationMoveCoordinator", coordinator);
- ReflectionTestUtils.setField(utils, "redisUtil", redisUtil);
+ stash(utils, "stationMoveCoordinator", coordinator);
+ stash(utils, "redisUtil", redisUtil);
+ wireOutboundSupport(utils);
+ wireRerouteProcessor(utils);
BasDevp basDevp = buildBasDevp(1);
when(basDevpService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
diff --git a/src/test/java/com/zy/core/utils/StationRerouteProcessorTest.java b/src/test/java/com/zy/core/utils/StationRerouteProcessorTest.java
new file mode 100644
index 0000000..744fcca
--- /dev/null
+++ b/src/test/java/com/zy/core/utils/StationRerouteProcessorTest.java
@@ -0,0 +1,133 @@
+package com.zy.core.utils;
+
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.WrkMast;
+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.thread.StationThread;
+import com.zy.core.utils.station.StationRerouteProcessor;
+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 org.junit.jupiter.api.Test;
+
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class StationRerouteProcessorTest {
+
+ @Test
+ void rerouteProcessor_noLongerKeepsDispatcherFallbackHelper() {
+ assertThrows(NoSuchMethodException.class,
+ () -> StationRerouteProcessor.class.getDeclaredMethod("getStationCommandDispatcher"));
+ }
+
+ @Test
+ void buildCommandPlan_usesRunBlockCommandBuilderForRunBlockScene() {
+ StationRerouteProcessor processor = new StationRerouteProcessor();
+ StationThread stationThread = mock(StationThread.class);
+ StationCommand command = new StationCommand();
+ command.setTaskNo(100);
+ command.setStationId(10);
+ command.setTargetStaNo(20);
+ when(stationThread.getRunBlockRerouteCommand(100, 10, 20, 0, 0.25d)).thenReturn(command);
+
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.RUN_BLOCK_REROUTE,
+ buildBasDevp(1),
+ stationThread,
+ buildStationProtocol(10, 100, 10),
+ buildWrkMast(100, 99),
+ Collections.emptyList(),
+ 0.25d,
+ "checkStationRunBlock_reroute"
+ ).withRunBlockCommand()
+ .withCancelSessionBeforeDispatch()
+ .withResetSegmentCommandsBeforeDispatch();
+
+ RerouteCommandPlan plan = processor.buildRerouteCommandPlan(
+ context,
+ RerouteDecision.proceed(20)
+ );
+
+ verify(stationThread).getRunBlockRerouteCommand(100, 10, 20, 0, 0.25d);
+ assertSame(command, plan.command());
+ }
+
+ @Test
+ void executePlan_skipsWhenCurrentTaskStillExistsInBuffer() {
+ StationRerouteProcessor processor = new StationRerouteProcessor();
+ StationCommand command = new StationCommand();
+ command.setTaskNo(100);
+ command.setStationId(10);
+ command.setTargetStaNo(20);
+
+ StationTaskBufferItem bufferItem = new StationTaskBufferItem();
+ bufferItem.setTaskNo(100);
+
+ RerouteContext context = RerouteContext.create(
+ RerouteSceneType.OUT_ORDER,
+ buildBasDevp(1),
+ mock(StationThread.class),
+ buildStationProtocol(10, 100, 10, Collections.singletonList(bufferItem)),
+ buildWrkMast(100, 20),
+ List.of(10, 20),
+ 0.0d,
+ "checkStationOutOrder"
+ );
+
+ RerouteExecutionResult result = processor.executeReroutePlan(
+ context,
+ RerouteCommandPlan.dispatch(
+ command,
+ RerouteDecision.proceed(20),
+ "checkStationOutOrder"
+ )
+ );
+
+ assertTrue(result.skipped());
+ assertEquals("buffer-has-current-task", result.skipReason());
+ }
+
+ private static BasDevp buildBasDevp(int devpNo) {
+ BasDevp basDevp = new BasDevp();
+ basDevp.setDevpNo(devpNo);
+ return basDevp;
+ }
+
+ private static WrkMast buildWrkMast(int wrkNo, int targetStationId) {
+ WrkMast wrkMast = new WrkMast();
+ wrkMast.setWrkNo(wrkNo);
+ wrkMast.setStaNo(targetStationId);
+ return wrkMast;
+ }
+
+ private static StationProtocol buildStationProtocol(int stationId,
+ int taskNo,
+ int targetStationId) {
+ return buildStationProtocol(stationId, taskNo, targetStationId, Collections.emptyList());
+ }
+
+ private static StationProtocol buildStationProtocol(int stationId,
+ int taskNo,
+ int targetStationId,
+ List<StationTaskBufferItem> taskBufferItems) {
+ StationProtocol stationProtocol = new StationProtocol();
+ stationProtocol.setStationId(stationId);
+ stationProtocol.setTaskNo(taskNo);
+ stationProtocol.setTargetStaNo(targetStationId);
+ stationProtocol.setTaskBufferItems(taskBufferItems);
+ return stationProtocol;
+ }
+}
diff --git a/src/test/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupportTest.java b/src/test/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupportTest.java
new file mode 100644
index 0000000..53d34ca
--- /dev/null
+++ b/src/test/java/com/zy/core/utils/station/StationDispatchRuntimeStateSupportTest.java
@@ -0,0 +1,139 @@
+package com.zy.core.utils.station;
+
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.zy.asrs.entity.BasStationOpt;
+import com.zy.asrs.service.BasStationOptService;
+import com.zy.common.utils.RedisUtil;
+import com.zy.core.enums.RedisKeyType;
+import com.zy.core.model.command.StationCommand;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class StationDispatchRuntimeStateSupportTest {
+
+ @Test
+ void idleTrack_isTimeoutUsesFirstSeenTime() {
+ StationTaskIdleTrack track = new StationTaskIdleTrack(100, 145, System.currentTimeMillis() - 11_000L);
+
+ assertTrue(track.isTimeout(10));
+ }
+
+ @Test
+ void touchIdleTrack_replacesTrackWhenStationChanges() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ ReflectionTestUtils.setField(support, "redisUtil", redisUtil);
+ when(redisUtil.get(RedisKeyType.STATION_TASK_IDLE_TRACK_.key + 100))
+ .thenReturn(JSON.toJSONString(new StationTaskIdleTrack(100, 145, System.currentTimeMillis() - 3_000L)));
+
+ StationTaskIdleTrack track = support.touchIdleTrack(100, 146);
+
+ assertEquals(146, track.getStationId());
+ verify(redisUtil).set(eq(RedisKeyType.STATION_TASK_IDLE_TRACK_.key + 100), org.mockito.ArgumentMatchers.any(String.class), eq(3600L));
+ }
+
+ @Test
+ void hasRecentIssuedMoveCommand_returnsTrueForRecentMoveLog() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ BasStationOptService basStationOptService = mock(BasStationOptService.class);
+ ReflectionTestUtils.setField(support, "basStationOptService", basStationOptService);
+ when(basStationOptService.list(org.mockito.ArgumentMatchers.<Wrapper<BasStationOpt>>any()))
+ .thenReturn(Collections.singletonList(new BasStationOpt()));
+
+ boolean result = support.hasRecentIssuedMoveCommand(100, 145, 10_000L);
+
+ assertTrue(result);
+ }
+
+ @Test
+ void clearIssuedMoveCommandsDuringIdleStay_marksSendZeroAndAppendsMemo() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ BasStationOptService basStationOptService = mock(BasStationOptService.class);
+ ReflectionTestUtils.setField(support, "basStationOptService", basStationOptService);
+ BasStationOpt opt = new BasStationOpt();
+ opt.setId(1L);
+ opt.setSend(1);
+ opt.setMemo("existing");
+ opt.setSendTime(new Date());
+ when(basStationOptService.list(org.mockito.ArgumentMatchers.<Wrapper<BasStationOpt>>any()))
+ .thenReturn(Collections.singletonList(opt));
+
+ int clearedCount = support.clearIssuedMoveCommandsDuringIdleStay(
+ new StationTaskIdleTrack(100, 145, System.currentTimeMillis() - 10_000L),
+ 100,
+ 145
+ );
+
+ assertEquals(1, clearedCount);
+ assertEquals(0, opt.getSend());
+ assertTrue(opt.getMemo().contains("existing"));
+ assertTrue(opt.getMemo().contains("idleRecoverRerouteCleared(stationId=145)"));
+ assertFalse(opt.getUpdateTime() == null);
+ verify(basStationOptService).updateBatchById(Collections.singletonList(opt));
+ }
+
+ @Test
+ void tryAcquireOutOrderDispatchLock_returnsFalseWhenKeyAlreadyExists() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ ReflectionTestUtils.setField(support, "redisUtil", redisUtil);
+ when(redisUtil.get(RedisKeyType.STATION_OUT_ORDER_DISPATCH_LIMIT_.key + "100_145")).thenReturn("lock");
+
+ boolean acquired = support.tryAcquireOutOrderDispatchLock(100, 145, 2);
+
+ assertFalse(acquired);
+ }
+
+ @Test
+ void signalSegmentReset_setsAndClearsResetKey() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ ReflectionTestUtils.setField(support, "redisUtil", redisUtil);
+
+ support.signalSegmentReset(100, 0L);
+
+ verify(redisUtil).set(RedisKeyType.DEVICE_STATION_MOVE_RESET.key + 100, "cancel", 3);
+ verify(redisUtil).del(RedisKeyType.DEVICE_STATION_MOVE_RESET.key + 100);
+ }
+
+ @Test
+ void watchCircleCommand_roundTripsThroughRedis() {
+ StationDispatchRuntimeStateSupport support = new StationDispatchRuntimeStateSupport();
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ ReflectionTestUtils.setField(support, "redisUtil", redisUtil);
+ AtomicReference<Object> storedValue = new AtomicReference<>();
+ when(redisUtil.get(RedisKeyType.WATCH_CIRCLE_STATION_.key + 100))
+ .thenAnswer(invocation -> storedValue.get());
+ when(redisUtil.set(eq(RedisKeyType.WATCH_CIRCLE_STATION_.key + 100), org.mockito.ArgumentMatchers.any(String.class), eq(60L * 60 * 24)))
+ .thenAnswer(invocation -> {
+ storedValue.set(invocation.getArgument(1));
+ return true;
+ });
+
+ StationCommand command = new StationCommand();
+ command.setTaskNo(100);
+ command.setTargetStaNo(145);
+
+ support.saveWatchCircleCommand(100, command);
+ StationCommand loaded = support.loadWatchCircleCommand(100);
+
+ assertNotNull(loaded);
+ assertEquals(145, loaded.getTargetStaNo());
+ support.clearWatchCircleCommand(100);
+ verify(redisUtil).del(RedisKeyType.WATCH_CIRCLE_STATION_.key + 100);
+ }
+}
diff --git a/src/test/java/com/zy/core/utils/station/StationOutboundDispatchProcessorTest.java b/src/test/java/com/zy/core/utils/station/StationOutboundDispatchProcessorTest.java
new file mode 100644
index 0000000..b678efb
--- /dev/null
+++ b/src/test/java/com/zy/core/utils/station/StationOutboundDispatchProcessorTest.java
@@ -0,0 +1,202 @@
+package com.zy.core.utils.station;
+
+import com.zy.asrs.domain.enums.NotifyMsgType;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.WrkAnalysisService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.asrs.utils.NotifyUtils;
+import com.zy.common.utils.RedisUtil;
+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.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.move.StationMoveCoordinator;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.model.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+import com.zy.core.utils.station.model.OutOrderDispatchDecision;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class StationOutboundDispatchProcessorTest {
+
+ @Test
+ void crnStationOutExecute_recordsDispatchSessionAndClearsStationCacheAfterIssuingMoveCommand() {
+ StationOutboundDispatchProcessor processor = new StationOutboundDispatchProcessor();
+ WrkMastService wrkMastService = mock(WrkMastService.class);
+ WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationCommandDispatcher dispatcher = mock(StationCommandDispatcher.class);
+ StationDispatchLoadSupport loadSupport = mock(StationDispatchLoadSupport.class);
+ StationOutboundDecisionSupport decisionSupport = mock(StationOutboundDecisionSupport.class);
+ StationThread stationThread = mock(StationThread.class);
+
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "wrkAnalysisService", wrkAnalysisService);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(processor, "stationCommandDispatcher", dispatcher);
+ ReflectionTestUtils.setField(processor, "stationDispatchLoadSupport", loadSupport);
+ ReflectionTestUtils.setField(processor, "stationOutboundDecisionSupport", decisionSupport);
+
+ WrkMast wrkMast = buildWrkMast(10001, 145);
+ wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN_COMPLETE.sts);
+ wrkMast.setCrnNo(4);
+ when(wrkMastService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(wrkMast));
+ when(wrkMastService.updateById(wrkMast)).thenReturn(true);
+
+ StationObjModel stationObjModel = new StationObjModel();
+ stationObjModel.setDeviceNo(1);
+ stationObjModel.setStationId(101);
+ when(redisUtil.get(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + 10001))
+ .thenReturn(com.alibaba.fastjson.JSON.toJSONString(stationObjModel));
+ when(redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + 101)).thenReturn(null);
+
+ StationProtocol stationProtocol = buildStationProtocol(101, 0, 101);
+ stationProtocol.setAutoing(true);
+ stationProtocol.setLoading(true);
+ when(stationThread.getStatusMap()).thenReturn(Map.of(101, stationProtocol));
+
+ DispatchLimitConfig limitConfig = new DispatchLimitConfig();
+ LoadGuardState loadGuardState = new LoadGuardState();
+ LoopHitResult noHit = LoopHitResult.noHit();
+ OutOrderDispatchDecision decision = OutOrderDispatchDecision.direct(145);
+ StationCommand command = buildCommand(10001, 101, 145);
+
+ when(loadSupport.getDispatchLimitConfig(null, null)).thenReturn(limitConfig);
+ when(loadSupport.countCurrentStationTask()).thenReturn(0);
+ when(loadSupport.buildLoadGuardState(limitConfig)).thenReturn(loadGuardState);
+ when(decisionSupport.getAllOutOrderList()).thenReturn(List.of(101, 121, 145));
+ when(decisionSupport.resolveOutboundPathLenFactor(wrkMast)).thenReturn(0.25d);
+ when(decisionSupport.resolveOutboundDispatchDecision(101, wrkMast, List.of(101, 121, 145), 0.25d)).thenReturn(decision);
+ when(loadSupport.getDispatchLimitConfig(101, 145)).thenReturn(limitConfig);
+ when(loadSupport.findPathLoopHit(limitConfig, 101, 145, loadGuardState, wrkMast, 0.25d)).thenReturn(noHit);
+ when(loadSupport.isDispatchBlocked(limitConfig, 0, loadGuardState, false)).thenReturn(false);
+ when(decisionSupport.buildOutboundMoveCommand(stationThread, wrkMast, 101, 145, 0.25d)).thenReturn(command);
+ when(dispatcher.dispatch(1, command, "station-operate-process", "crnStationOutExecute"))
+ .thenReturn(StationCommandDispatchResult.accepted("accepted", 1, "station-operate-process", "crnStationOutExecute"));
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ processor.crnStationOutExecute();
+
+ verify(wrkAnalysisService, times(1)).markOutboundStationStart(eq(wrkMast), any(Date.class));
+ verify(coordinator, times(1)).recordDispatch(eq(10001), eq(101), eq("crnStationOutExecute"), same(command), eq(false));
+ verify(redisUtil, times(1)).set(eq(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + 101), eq("lock"), eq(5L));
+ verify(redisUtil, times(1)).del(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + 10001);
+ verify(loadSupport, times(1)).saveLoopLoadReserve(10001, noHit);
+ assertEquals(WrkStsType.STATION_RUN.sts, wrkMast.getWrkSts());
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
+
+ @Test
+ void dualCrnStationOutExecute_recordsDispatchSessionAndClearsDualCacheAfterIssuingMoveCommand() {
+ StationOutboundDispatchProcessor processor = new StationOutboundDispatchProcessor();
+ WrkMastService wrkMastService = mock(WrkMastService.class);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationCommandDispatcher dispatcher = mock(StationCommandDispatcher.class);
+ NotifyUtils notifyUtils = mock(NotifyUtils.class);
+ StationOutboundDecisionSupport decisionSupport = mock(StationOutboundDecisionSupport.class);
+ StationThread stationThread = mock(StationThread.class);
+
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(processor, "stationCommandDispatcher", dispatcher);
+ ReflectionTestUtils.setField(processor, "notifyUtils", notifyUtils);
+ ReflectionTestUtils.setField(processor, "stationOutboundDecisionSupport", decisionSupport);
+
+ WrkMast wrkMast = buildWrkMast(10002, 188);
+ wrkMast.setWrkSts(WrkStsType.OUTBOUND_RUN_COMPLETE.sts);
+ wrkMast.setDualCrnNo(2);
+ when(wrkMastService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(wrkMast));
+ when(wrkMastService.updateById(wrkMast)).thenReturn(true);
+
+ StationObjModel stationObjModel = new StationObjModel();
+ stationObjModel.setDeviceNo(1);
+ stationObjModel.setStationId(121);
+ when(redisUtil.get(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + 10002))
+ .thenReturn(com.alibaba.fastjson.JSON.toJSONString(stationObjModel));
+ when(redisUtil.get(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + 121)).thenReturn(null);
+
+ StationProtocol stationProtocol = buildStationProtocol(121, 0, 121);
+ stationProtocol.setAutoing(true);
+ stationProtocol.setLoading(true);
+ when(stationThread.getStatusMap()).thenReturn(Map.of(121, stationProtocol));
+
+ StationCommand command = buildCommand(10002, 121, 188);
+ when(decisionSupport.resolveOutboundPathLenFactor(wrkMast)).thenReturn(0.5d);
+ when(decisionSupport.buildOutboundMoveCommand(stationThread, wrkMast, 121, 188, 0.5d)).thenReturn(command);
+ when(dispatcher.dispatch(1, command, "station-operate-process", "dualCrnStationOutExecute"))
+ .thenReturn(StationCommandDispatchResult.accepted("accepted", 1, "station-operate-process", "dualCrnStationOutExecute"));
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ processor.dualCrnStationOutExecute();
+
+ verify(coordinator, times(1)).recordDispatch(eq(10002), eq(121), eq("dualCrnStationOutExecute"), same(command), eq(false));
+ verify(notifyUtils, times(1)).notify(String.valueOf(SlaveType.Devp), 1, "10002", wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN, null);
+ verify(redisUtil, times(1)).set(eq(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + 121), eq("lock"), eq(5L));
+ verify(redisUtil, times(1)).del(RedisKeyType.DUAL_CRN_OUT_TASK_STATION_INFO.key + 10002);
+ assertEquals(WrkStsType.STATION_RUN.sts, wrkMast.getWrkSts());
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
+
+ private static WrkMast buildWrkMast(int wrkNo, int targetStationId) {
+ WrkMast wrkMast = new WrkMast();
+ wrkMast.setWrkNo(wrkNo);
+ wrkMast.setStaNo(targetStationId);
+ wrkMast.setWmsWrkNo("WMS-" + wrkNo);
+ wrkMast.setIoTime(new Date());
+ return wrkMast;
+ }
+
+ private static StationProtocol buildStationProtocol(int stationId,
+ int taskNo,
+ int targetStationId) {
+ StationProtocol stationProtocol = new StationProtocol();
+ stationProtocol.setStationId(stationId);
+ stationProtocol.setTaskNo(taskNo);
+ stationProtocol.setTargetStaNo(targetStationId);
+ return stationProtocol;
+ }
+
+ private static StationCommand buildCommand(int wrkNo,
+ int stationId,
+ int targetStationId) {
+ StationCommand command = new StationCommand();
+ command.setTaskNo(wrkNo);
+ command.setStationId(stationId);
+ command.setTargetStaNo(targetStationId);
+ return command;
+ }
+}
diff --git a/src/test/java/com/zy/core/utils/station/StationRegularDispatchProcessorTest.java b/src/test/java/com/zy/core/utils/station/StationRegularDispatchProcessorTest.java
new file mode 100644
index 0000000..a862ffd
--- /dev/null
+++ b/src/test/java/com/zy/core/utils/station/StationRegularDispatchProcessorTest.java
@@ -0,0 +1,240 @@
+package com.zy.core.utils.station;
+
+import com.zy.asrs.domain.enums.NotifyMsgType;
+import com.zy.asrs.entity.BasDevp;
+import com.zy.asrs.entity.BasStation;
+import com.zy.asrs.entity.WrkMast;
+import com.zy.asrs.service.BasDevpService;
+import com.zy.asrs.service.BasStationService;
+import com.zy.asrs.service.WrkAnalysisService;
+import com.zy.asrs.service.WrkMastService;
+import com.zy.asrs.utils.NotifyUtils;
+import com.zy.common.entity.FindCrnNoResult;
+import com.zy.common.service.CommonService;
+import com.zy.common.utils.RedisUtil;
+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.WrkStsType;
+import com.zy.core.model.command.StationCommand;
+import com.zy.core.model.protocol.StationProtocol;
+import com.zy.core.move.StationMoveCoordinator;
+import com.zy.core.thread.StationThread;
+import com.zy.core.utils.station.model.DispatchLimitConfig;
+import com.zy.core.utils.station.model.LoadGuardState;
+import com.zy.core.utils.station.model.LoopHitResult;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.same;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class StationRegularDispatchProcessorTest {
+
+ @Test
+ void stationInExecute_recordsDispatchSessionAfterIssuingMoveCommand() {
+ StationRegularDispatchProcessor processor = new StationRegularDispatchProcessor();
+ BasDevpService basDevpService = mock(BasDevpService.class);
+ WrkMastService wrkMastService = mock(WrkMastService.class);
+ CommonService commonService = mock(CommonService.class);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationCommandDispatcher dispatcher = mock(StationCommandDispatcher.class);
+ StationDispatchLoadSupport loadSupport = mock(StationDispatchLoadSupport.class);
+ StationThread stationThread = mock(StationThread.class);
+
+ ReflectionTestUtils.setField(processor, "basDevpService", basDevpService);
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "commonService", commonService);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "wrkAnalysisService", wrkAnalysisService);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(processor, "stationCommandDispatcher", dispatcher);
+ ReflectionTestUtils.setField(processor, "stationDispatchLoadSupport", loadSupport);
+
+ BasDevp basDevp = buildBasDevp(1);
+ basDevp.setBarcodeStationList("[{\"deviceNo\":1,\"stationId\":101}]");
+ when(basDevpService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(basDevp));
+
+ StationProtocol stationProtocol = buildStationProtocol(101, 500670, 101);
+ stationProtocol.setAutoing(true);
+ stationProtocol.setLoading(true);
+ stationProtocol.setBarcode("GSL110005");
+ when(stationThread.getStatusMap()).thenReturn(Map.of(101, stationProtocol));
+
+ WrkMast wrkMast = buildWrkMast(500670, 102);
+ wrkMast.setWrkSts(WrkStsType.NEW_INBOUND.sts);
+ wrkMast.setLocNo("8-4-1");
+ when(wrkMastService.getOne(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class))).thenReturn(wrkMast);
+ when(wrkMastService.updateById(wrkMast)).thenReturn(true);
+
+ FindCrnNoResult findCrnNoResult = new FindCrnNoResult();
+ findCrnNoResult.setCrnNo(4);
+ findCrnNoResult.setCrnType(SlaveType.Crn);
+ when(commonService.findCrnNoByLocNo("8-4-1")).thenReturn(findCrnNoResult);
+ when(commonService.findInStationId(findCrnNoResult, 101)).thenReturn(102);
+
+ StationCommand command = new StationCommand();
+ command.setTaskNo(500670);
+ command.setStationId(101);
+ command.setTargetStaNo(102);
+ when(stationThread.getCommand(com.zy.core.enums.StationCommandType.MOVE, 500670, 101, 102, 0))
+ .thenReturn(command);
+ when(redisUtil.get(anyString())).thenReturn(null);
+ when(dispatcher.dispatch(1, command, "station-operate-process", "stationInExecute"))
+ .thenReturn(StationCommandDispatchResult.accepted("accepted", 1, "station-operate-process", "stationInExecute"));
+
+ DispatchLimitConfig baseConfig = new DispatchLimitConfig();
+ LoadGuardState loadGuardState = new LoadGuardState();
+ LoopHitResult noHit = LoopHitResult.noHit();
+ when(loadSupport.getDispatchLimitConfig(null, null)).thenReturn(baseConfig);
+ when(loadSupport.countCurrentStationTask()).thenReturn(0);
+ when(loadSupport.buildLoadGuardState(baseConfig)).thenReturn(loadGuardState);
+ when(loadSupport.getDispatchLimitConfig(101, 102)).thenReturn(baseConfig);
+ when(loadSupport.findPathLoopHit(baseConfig, 101, 102, loadGuardState)).thenReturn(noHit);
+ when(loadSupport.isDispatchBlocked(baseConfig, 0, loadGuardState, false)).thenReturn(false);
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ processor.stationInExecute();
+
+ verify(coordinator, times(1)).recordDispatch(eq(500670), eq(101), eq("stationInExecute"), same(command), eq(false));
+ verify(redisUtil, times(1)).set(eq(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + 101), eq("lock"), eq(5L));
+ verify(loadSupport, times(1)).saveLoopLoadReserve(500670, noHit);
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
+
+ @Test
+ void stationOutExecuteFinish_attemptsClearPathBeforeCompletingTask() {
+ StationRegularDispatchProcessor processor = new StationRegularDispatchProcessor();
+ WrkMastService wrkMastService = mock(WrkMastService.class);
+ BasStationService basStationService = mock(BasStationService.class);
+ WrkAnalysisService wrkAnalysisService = mock(WrkAnalysisService.class);
+ NotifyUtils notifyUtils = mock(NotifyUtils.class);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ StationThread stationThread = mock(StationThread.class);
+
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "basStationService", basStationService);
+ ReflectionTestUtils.setField(processor, "wrkAnalysisService", wrkAnalysisService);
+ ReflectionTestUtils.setField(processor, "notifyUtils", notifyUtils);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+
+ WrkMast wrkMast = buildWrkMast(10335, 145);
+ wrkMast.setWrkSts(WrkStsType.STATION_RUN.sts);
+ when(wrkMastService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(wrkMast));
+ when(wrkMastService.updateById(wrkMast)).thenReturn(true);
+
+ BasStation basStation = new BasStation();
+ basStation.setStationId(145);
+ basStation.setDeviceNo(1);
+ when(basStationService.getOne(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(basStation);
+
+ StationProtocol stationProtocol = buildStationProtocol(145, 10335, 145);
+ when(stationThread.getStatusMap()).thenReturn(Map.of(145, stationProtocol));
+ when(stationThread.clearPath(10335)).thenReturn(true);
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ processor.stationOutExecuteFinish();
+
+ verify(stationThread, times(1)).clearPath(10335);
+ verify(coordinator, times(1)).finishSession(10335);
+ verify(wrkMastService, times(1)).updateById(wrkMast);
+ verify(notifyUtils, times(1)).notify(String.valueOf(SlaveType.Devp), 1, "10335", wrkMast.getWmsWrkNo(), NotifyMsgType.STATION_OUT_TASK_RUN_COMPLETE, null);
+ assertEquals(WrkStsType.STATION_RUN_COMPLETE.sts, wrkMast.getWrkSts());
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
+
+ @Test
+ void checkTaskToComplete_marksTaskCompleteAfterLeavingTargetStation() {
+ StationRegularDispatchProcessor processor = new StationRegularDispatchProcessor();
+ WrkMastService wrkMastService = mock(WrkMastService.class);
+ BasStationService basStationService = mock(BasStationService.class);
+ StationMoveCoordinator coordinator = mock(StationMoveCoordinator.class);
+ RedisUtil redisUtil = mock(RedisUtil.class);
+ StationThread stationThread = mock(StationThread.class);
+
+ ReflectionTestUtils.setField(processor, "wrkMastService", wrkMastService);
+ ReflectionTestUtils.setField(processor, "basStationService", basStationService);
+ ReflectionTestUtils.setField(processor, "stationMoveCoordinator", coordinator);
+ ReflectionTestUtils.setField(processor, "redisUtil", redisUtil);
+
+ WrkMast wrkMast = buildWrkMast(10335, 145);
+ wrkMast.setWrkSts(WrkStsType.STATION_RUN_COMPLETE.sts);
+ when(wrkMastService.list(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(Collections.singletonList(wrkMast));
+
+ BasStation basStation = new BasStation();
+ basStation.setStationId(145);
+ basStation.setDeviceNo(1);
+ when(basStationService.getOne(any(com.baomidou.mybatisplus.core.conditions.Wrapper.class)))
+ .thenReturn(basStation);
+
+ StationProtocol stationProtocol = buildStationProtocol(145, 0, 145);
+ when(stationThread.getStatusMap()).thenReturn(Map.of(145, stationProtocol));
+ when(redisUtil.get(anyString())).thenReturn(null);
+
+ SlaveConnection.put(SlaveType.Devp, 1, stationThread);
+ try {
+ processor.checkTaskToComplete();
+
+ verify(coordinator, times(1)).finishSession(10335);
+ verify(wrkMastService, times(1)).updateById(wrkMast);
+ assertEquals(WrkStsType.COMPLETE_OUTBOUND.sts, wrkMast.getWrkSts());
+ } finally {
+ SlaveConnection.remove(SlaveType.Devp, 1);
+ }
+ }
+
+ private static BasDevp buildBasDevp(int devpNo) {
+ BasDevp basDevp = new BasDevp();
+ basDevp.setDevpNo(devpNo);
+ return basDevp;
+ }
+
+ private static WrkMast buildWrkMast(int wrkNo, int targetStationId) {
+ WrkMast wrkMast = new WrkMast();
+ wrkMast.setWrkNo(wrkNo);
+ wrkMast.setStaNo(targetStationId);
+ wrkMast.setWmsWrkNo("WMS-" + wrkNo);
+ wrkMast.setIoTime(new Date());
+ return wrkMast;
+ }
+
+ private static StationProtocol buildStationProtocol(int stationId,
+ int taskNo,
+ int targetStationId) {
+ StationProtocol stationProtocol = new StationProtocol();
+ stationProtocol.setStationId(stationId);
+ stationProtocol.setTaskNo(taskNo);
+ stationProtocol.setTargetStaNo(targetStationId);
+ return stationProtocol;
+ }
+}
diff --git a/src/test/java/com/zy/core/utils/station/model/StationModelTypePlacementTest.java b/src/test/java/com/zy/core/utils/station/model/StationModelTypePlacementTest.java
new file mode 100644
index 0000000..5d472e8
--- /dev/null
+++ b/src/test/java/com/zy/core/utils/station/model/StationModelTypePlacementTest.java
@@ -0,0 +1,49 @@
+package com.zy.core.utils.station.model;
+
+import com.zy.core.utils.StationOperateProcessUtils;
+import com.zy.core.utils.station.StationDispatchLoadSupport;
+import com.zy.core.utils.station.StationOutboundDecisionSupport;
+import com.zy.core.utils.station.StationRerouteProcessor;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+
+class StationModelTypePlacementTest {
+
+ @Test
+ void rerouteTypes_areNoLongerNestedInsideStationOperateProcessUtils() {
+ assertFalse(hasDeclaredClass(StationOperateProcessUtils.class, "RerouteSceneType"));
+ assertFalse(hasDeclaredClass(StationOperateProcessUtils.class, "RerouteDecision"));
+ assertFalse(hasDeclaredClass(StationOperateProcessUtils.class, "RerouteContext"));
+ assertFalse(hasDeclaredClass(StationOperateProcessUtils.class, "RerouteCommandPlan"));
+ assertFalse(hasDeclaredClass(StationOperateProcessUtils.class, "RerouteExecutionResult"));
+ }
+
+ @Test
+ void dispatchLoadTypes_areNoLongerNestedInsideStationDispatchLoadSupport() {
+ assertFalse(hasDeclaredClass(StationDispatchLoadSupport.class, "DispatchLimitConfig"));
+ assertFalse(hasDeclaredClass(StationDispatchLoadSupport.class, "LoadGuardState"));
+ assertFalse(hasDeclaredClass(StationDispatchLoadSupport.class, "LoopHitResult"));
+ }
+
+ @Test
+ void outboundDecisionTypes_areNoLongerNestedInsideStationOutboundDecisionSupport() {
+ assertFalse(hasDeclaredClass(StationOutboundDecisionSupport.class, "OutOrderDispatchDecision"));
+ assertFalse(hasDeclaredClass(StationOutboundDecisionSupport.class, "CircleTargetCandidate"));
+ }
+
+ @Test
+ void rerouteProcessor_isLocatedUnderStationPackage() {
+ assertEquals("com.zy.core.utils.station", StationRerouteProcessor.class.getPackageName());
+ }
+
+ private static boolean hasDeclaredClass(Class<?> ownerType, String simpleName) {
+ for (Class<?> declaredClass : ownerType.getDeclaredClasses()) {
+ if (simpleName.equals(declaredClass.getSimpleName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
--
Gitblit v1.9.1