package com.zy.core.network.fake;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.zy.asrs.entity.DeviceConfig;
|
import com.zy.common.utils.RedisUtil;
|
import com.zy.core.News;
|
import com.zy.core.enums.RedisKeyType;
|
import com.zy.core.enums.StationCommandType;
|
import com.zy.core.model.CommandResponse;
|
import com.zy.core.model.command.StationCommand;
|
import com.zy.core.network.api.ZyStationConnectApi;
|
import com.zy.core.network.entity.ZyStationStatusEntity;
|
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.HashSet;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Set;
|
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.Executors;
|
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ThreadFactory;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.atomic.AtomicInteger;
|
|
public class ZyStationFakeSegConnect implements ZyStationConnectApi {
|
|
private long getWaitSegmentTimeoutMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_SEGMENT_WAIT_TIMEOUT_MS, 30000L);
|
}
|
|
private long getMoveStepDurationMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_MOVE_STEP_DURATION_MS, 500L);
|
}
|
|
private long getIdleLoopDelayMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_IDLE_LOOP_DELAY_MS, 200L);
|
}
|
|
private long getBlockedLoopDelayMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_BLOCKED_LOOP_DELAY_MS, 1000L);
|
}
|
|
private long getInitializeDelayMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_INITIALIZE_DELAY_MS, 0L);
|
}
|
|
private long getFinishDelayMs() {
|
return FakeConfigSupport.getLong(FakeConfigKeys.FAKE_STATION_FINISH_DELAY_MS, 0L);
|
}
|
|
private static final String STATUS_WAITING = "WAITING";
|
private static final String STATUS_RUNNING = "RUNNING";
|
private static final String STATUS_BLOCKED = "BLOCKED";
|
private static final String STATUS_CANCELLED = "CANCELLED";
|
private static final String STATUS_TIMEOUT = "TIMEOUT";
|
private static final String STATUS_FINISHED = "FINISHED";
|
|
private static final String SEGMENT_MERGE_APPEND = "APPEND";
|
private static final String SEGMENT_MERGE_REPLACE_TARGET_CHANGED = "REPLACE_TARGET_CHANGED";
|
private static final String SEGMENT_MERGE_REPLACE_REROUTE = "REPLACE_REROUTE";
|
private static final String SEGMENT_MERGE_IGNORE_DISCONNECTED = "IGNORE_DISCONNECTED";
|
private static final String SEGMENT_MERGE_IGNORE_CURRENT_MISSING = "IGNORE_CURRENT_MISSING";
|
|
private final FakeStationStateManager stateManager = new FakeStationStateManager();
|
private final FakeStationMoveEngine moveEngine = new FakeStationMoveEngine(stateManager);
|
private final FakeStationBlockManager blockManager = new FakeStationBlockManager(stateManager);
|
private static final AtomicInteger DEVICE_EXECUTOR_THREAD_SEQ = new AtomicInteger(1);
|
private volatile ScheduledExecutorService loopScheduler = createLoopScheduler();
|
private RedisUtil redisUtil;
|
private final Map<Integer, ExecutorService> deviceExecutors = new ConcurrentHashMap<Integer, ExecutorService>();
|
|
private final Map<Integer, BlockingQueue<StationCommand>> taskQueues = new ConcurrentHashMap<Integer, BlockingQueue<StationCommand>>();
|
private final Map<Integer, Long> taskLastUpdateTime = new ConcurrentHashMap<Integer, Long>();
|
private final Map<Integer, Boolean> taskRunning = new ConcurrentHashMap<Integer, Boolean>();
|
private final Map<Integer, TaskRuntimeContext> taskContexts = new ConcurrentHashMap<Integer, TaskRuntimeContext>();
|
private final Map<Integer, AtomicInteger> taskLoopGenerations = new ConcurrentHashMap<Integer, AtomicInteger>();
|
private final Map<Integer, Object> taskLifecycleLocks = new ConcurrentHashMap<Integer, Object>();
|
private volatile Set<Integer> legalClearStationIds = new HashSet<Integer>();
|
private volatile Set<Integer> barcodeStationIds = new HashSet<Integer>();
|
|
public void addFakeConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) {
|
this.redisUtil = redisUtil;
|
refreshLegalClearStationIds();
|
refreshBarcodeStationIds();
|
stateManager.registerDevice(deviceConfig);
|
}
|
|
@Override
|
public boolean connect() {
|
if (loopScheduler == null || loopScheduler.isShutdown() || loopScheduler.isTerminated()) {
|
loopScheduler = createLoopScheduler();
|
}
|
return true;
|
}
|
|
@Override
|
public boolean disconnect() {
|
ScheduledExecutorService scheduler = loopScheduler;
|
if (scheduler != null) {
|
scheduler.shutdownNow();
|
}
|
for (ExecutorService executor : deviceExecutors.values()) {
|
executor.shutdownNow();
|
}
|
deviceExecutors.clear();
|
taskQueues.clear();
|
taskLastUpdateTime.clear();
|
taskRunning.clear();
|
taskContexts.clear();
|
taskLoopGenerations.clear();
|
taskLifecycleLocks.clear();
|
return true;
|
}
|
|
@Override
|
public List<ZyStationStatusEntity> getStatus(Integer deviceNo) {
|
return stateManager.getStatus(deviceNo);
|
}
|
|
@Override
|
public CommandResponse sendCommand(Integer deviceNo, StationCommand command) {
|
Integer taskNo = command.getTaskNo();
|
if (taskNo == null) {
|
return new CommandResponse(false, "任务号为空");
|
}
|
|
if (command.getCommandType() != StationCommandType.MOVE) {
|
News.info("[WCS Debug] fake sendCommand收到非MOVE命令,deviceNo={},taskNo={},stationId={},targetStaNo={},commandType={}",
|
deviceNo, taskNo, command.getStationId(), command.getTargetStaNo(), command.getCommandType());
|
getDeviceExecutor(deviceNo).submit(new Runnable() {
|
@Override
|
public void run() {
|
handleCommand(deviceNo, command);
|
}
|
});
|
return new CommandResponse(true, "命令已受理(异步执行)");
|
}
|
|
int loopGeneration = 0;
|
boolean shouldSchedule = false;
|
Object lifecycleLock = getTaskLifecycleLock(taskNo);
|
synchronized (lifecycleLock) {
|
BlockingQueue<StationCommand> queue = taskQueues.computeIfAbsent(taskNo, key -> new LinkedBlockingQueue<StationCommand>());
|
queue.offer(command);
|
taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
|
News.info("[WCS Debug] fake sendCommand入队,deviceNo={},taskNo={},stationId={},targetStaNo={},segmentNo={},segmentCount={},queueSize={},running={}",
|
deviceNo, taskNo, command.getStationId(), command.getTargetStaNo(), command.getSegmentNo(), command.getSegmentCount(),
|
queue.size(), taskRunning.containsKey(taskNo));
|
|
if (taskRunning.putIfAbsent(taskNo, true) == null) {
|
TaskRuntimeContext context = new TaskRuntimeContext(taskNo, deviceNo, stateManager.getThreadImpl(deviceNo));
|
taskContexts.put(taskNo, context);
|
AtomicInteger generation = taskLoopGenerations.computeIfAbsent(taskNo, key -> new AtomicInteger(0));
|
loopGeneration = generation.incrementAndGet();
|
context.loopGeneration = loopGeneration;
|
shouldSchedule = true;
|
News.info("[WCS Debug] fake task准备启动执行线程,deviceNo={},taskNo={},queueSize={},loopGeneration={}",
|
deviceNo, taskNo, queue.size(), loopGeneration);
|
} else {
|
News.info("[WCS Debug] fake task复用已存在执行线程,deviceNo={},taskNo={},queueSize={}",
|
deviceNo, taskNo, queue.size());
|
}
|
}
|
if (shouldSchedule) {
|
scheduleTaskLoop(deviceNo, taskNo, loopGeneration);
|
}
|
|
return new CommandResponse(true, "命令已受理(异步执行)");
|
}
|
|
@Override
|
public CommandResponse sendOriginCommand(String address, short[] data) {
|
return new CommandResponse(true, "原始命令已受理(异步执行)");
|
}
|
|
@Override
|
public CommandResponse clearTaskBufferSlot(Integer deviceNo, Integer stationId, Integer slotIdx) {
|
return stateManager.clearTaskBufferSlot(deviceNo, stationId, slotIdx);
|
}
|
|
@Override
|
public byte[] readOriginCommand(String address, int length) {
|
return new byte[0];
|
}
|
|
|
private ScheduledExecutorService createLoopScheduler() {
|
return Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
|
@Override
|
public Thread newThread(Runnable runnable) {
|
Thread thread = new Thread(runnable, "fake-station-loop-scheduler");
|
thread.setDaemon(true);
|
return thread;
|
}
|
});
|
}
|
|
private ExecutorService getDeviceExecutor(Integer deviceNo) {
|
return deviceExecutors.computeIfAbsent(deviceNo, key -> Executors.newSingleThreadExecutor(new ThreadFactory() {
|
@Override
|
public Thread newThread(Runnable runnable) {
|
Thread thread = new Thread(runnable,
|
"fake-station-device-" + key + "-" + DEVICE_EXECUTOR_THREAD_SEQ.getAndIncrement());
|
thread.setDaemon(true);
|
return thread;
|
}
|
}));
|
}
|
|
private void scheduleTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration) {
|
scheduleTaskLoop(deviceNo, taskNo, loopGeneration, 0L);
|
}
|
|
private void scheduleTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration, long delayMs) {
|
Runnable task = new Runnable() {
|
@Override
|
public void run() {
|
getDeviceExecutor(deviceNo).submit(new Runnable() {
|
@Override
|
public void run() {
|
runTaskLoop(deviceNo, taskNo, loopGeneration);
|
}
|
});
|
}
|
};
|
if (delayMs <= 0L) {
|
task.run();
|
return;
|
}
|
loopScheduler.schedule(task, delayMs, TimeUnit.MILLISECONDS);
|
}
|
|
private void scheduleTaskLoopByDelay(Integer deviceNo, Integer taskNo, int loopGeneration, long delayMs) {
|
scheduleTaskLoop(deviceNo, taskNo, loopGeneration, Math.max(delayMs, 1L));
|
}
|
|
private void runTaskLoop(Integer deviceNo, Integer taskNo, int loopGeneration) {
|
TaskRuntimeContext context = taskContexts.get(taskNo);
|
if (context == null) {
|
News.info("[WCS Debug] fake task忽略无上下文续跑片段,deviceNo={},taskNo={},loopGeneration={}",
|
deviceNo, taskNo, loopGeneration);
|
return;
|
}
|
if (!deviceNo.equals(context.deviceNo)) {
|
News.info("[WCS Debug] fake task忽略跨设备续跑片段,taskNo={},expectedDeviceNo={},actualDeviceNo={},loopGeneration={}",
|
taskNo, context.deviceNo, deviceNo, loopGeneration);
|
return;
|
}
|
if (context.loopGeneration != loopGeneration || !isCurrentLoopGeneration(taskNo, loopGeneration)) {
|
News.info("[WCS Debug] fake task忽略过期续跑片段,deviceNo={},taskNo={},expectedLoopGeneration={},actualLoopGeneration={}",
|
deviceNo, taskNo, loopGeneration, context.loopGeneration);
|
return;
|
}
|
News.info("[WCS Debug] fake task进入runTaskLoop,deviceNo={},taskNo={},threadImpl={},queueExists={},queueSize={},loopGeneration={},status={},initialized={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}",
|
deviceNo, taskNo, context.threadImpl, taskQueues.containsKey(taskNo),
|
taskQueues.containsKey(taskNo) && taskQueues.get(taskNo) != null ? taskQueues.get(taskNo).size() : null, loopGeneration,
|
context.status, context.initialized, context.currentStationId, context.finalTargetStationId,
|
context.blockedStationId, context.getPendingStationIds());
|
long nextDelayMs = 0L;
|
boolean shouldContinue = false;
|
try {
|
if (Thread.currentThread().isInterrupted()) {
|
if (!isTerminalStatus(context.status)) {
|
context.status = STATUS_CANCELLED;
|
}
|
} else if (hasTaskReset(taskNo)) {
|
context.status = STATUS_CANCELLED;
|
} else {
|
BlockingQueue<StationCommand> commandQueue = taskQueues.get(taskNo);
|
if (commandQueue == null) {
|
context.status = STATUS_FINISHED;
|
} else {
|
StationCommand command = commandQueue.poll();
|
if (command != null) {
|
taskLastUpdateTime.put(taskNo, System.currentTimeMillis());
|
context.lastCommandAt = System.currentTimeMillis();
|
handleIncomingSegment(deviceNo, context, command);
|
}
|
|
if (!isTerminalStatus(context.status)) {
|
if (!context.pendingPathQueue.isEmpty()) {
|
News.info("[WCS Debug] fake task准备推进,deviceNo={},taskNo={},initialized={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}",
|
deviceNo, taskNo, context.initialized, context.currentStationId,
|
context.finalTargetStationId, context.blockedStationId, context.getPendingStationIds());
|
if (!context.initialized || context.currentStationId == null) {
|
nextDelayMs = initializeTaskPosition(deviceNo, context);
|
} else {
|
MoveStepResult moveResult = executeNextMove(deviceNo, context);
|
if (!moveResult.shouldContinue()) {
|
context.status = STATUS_FINISHED;
|
}
|
nextDelayMs = moveResult.getNextDelayMs();
|
}
|
} else {
|
News.info("[WCS Debug] fake task进入空闲态,deviceNo={},taskNo={},status={},currentStationId={},targetStationId={},blockedStationId={},pendingPath={}",
|
deviceNo, taskNo, context.status, context.currentStationId,
|
context.finalTargetStationId, context.blockedStationId, context.getPendingStationIds());
|
IdleStepResult idleResult = handleIdleState(deviceNo, context);
|
if (idleResult.isFinished() && !isTerminalStatus(context.status)) {
|
context.status = STATUS_FINISHED;
|
}
|
nextDelayMs = idleResult.getNextDelayMs();
|
}
|
}
|
shouldContinue = !isTerminalStatus(context.status)
|
&& taskQueues.containsKey(taskNo)
|
&& (commandQueue != null);
|
}
|
}
|
} catch (Exception e) {
|
context.status = STATUS_CANCELLED;
|
if (e instanceof InterruptedException) {
|
Thread.currentThread().interrupt();
|
}
|
News.info("[WCS Debug] 任务{}执行异常,当前站点={},目标站={},待执行路径={},异常类型={},异常信息={}",
|
taskNo, context.currentStationId, context.finalTargetStationId, context.getPendingStationIds(),
|
e.getClass().getSimpleName(), e.getMessage());
|
}
|
|
if (shouldContinue) {
|
if (nextDelayMs > 0L) {
|
scheduleTaskLoopByDelay(deviceNo, taskNo, loopGeneration, nextDelayMs);
|
} else {
|
scheduleTaskLoop(deviceNo, taskNo, loopGeneration);
|
}
|
return;
|
}
|
finishTaskLoop(deviceNo, taskNo, context, loopGeneration);
|
}
|
|
private void finishTaskLoop(Integer deviceNo, Integer taskNo, TaskRuntimeContext context, int loopGeneration) {
|
Object lifecycleLock = getTaskLifecycleLock(taskNo);
|
synchronized (lifecycleLock) {
|
if (context.loopGeneration != loopGeneration || !isCurrentLoopGeneration(taskNo, loopGeneration)) {
|
News.info("[WCS Debug] fake task忽略过期结束清理,deviceNo={},taskNo={},expectedLoopGeneration={},actualLoopGeneration={}",
|
deviceNo, taskNo, loopGeneration, context.loopGeneration);
|
return;
|
}
|
BlockingQueue<StationCommand> queue = taskQueues.get(taskNo);
|
if (queue != null && !queue.isEmpty()) {
|
News.info("[WCS Debug] fake task结束清理前发现新命令,恢复续跑,deviceNo={},taskNo={},queueSize={},loopGeneration={}",
|
deviceNo, taskNo, queue.size(), loopGeneration);
|
scheduleTaskLoop(deviceNo, taskNo, loopGeneration);
|
return;
|
}
|
News.info("[WCS Debug] fake task即将退出runTaskLoop,deviceNo={},taskNo={},status={},queueSizeBeforeCleanup={},lastCurrentStationId={},targetStationId={},loopGeneration={}",
|
deviceNo, taskNo, context.status, queue == null ? null : queue.size(), context.currentStationId, context.finalTargetStationId, loopGeneration);
|
taskQueues.remove(taskNo);
|
taskLastUpdateTime.remove(taskNo);
|
taskRunning.remove(taskNo);
|
taskContexts.remove(taskNo);
|
taskLoopGenerations.remove(taskNo);
|
taskLifecycleLocks.remove(taskNo, lifecycleLock);
|
}
|
|
if (!isTerminalStatus(context.status)) {
|
context.status = STATUS_FINISHED;
|
}
|
traceEvent(deviceNo, context, "TASK_END", "任务执行结束并清理资源",
|
buildDetails("reason", context.status, "loopGeneration", loopGeneration), true);
|
News.info("[WCS Debug] 任务{}执行结束并清理资源,状态={},loopGeneration={}", taskNo, context.status, loopGeneration);
|
}
|
|
private void handleIncomingSegment(Integer deviceNo, TaskRuntimeContext context, StationCommand command) {
|
if (!deviceNo.equals(context.deviceNo)) {
|
traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段来自不同设备车道,已忽略",
|
buildDetails("expectedDeviceNo", context.deviceNo, "actualDeviceNo", deviceNo,
|
"segmentNo", command.getSegmentNo(), "segmentCount", command.getSegmentCount()), false);
|
return;
|
}
|
List<Integer> newPath = normalizePath(command.getNavigatePath());
|
Integer lastInQueue = getLastInQueue(context.pendingPathQueue);
|
int startIndex = getPathAppendStartIndex(newPath, context.currentStationId, lastInQueue);
|
Integer previousTargetStationId = context.finalTargetStationId;
|
Integer commandTargetStationId = command.getTargetStaNo();
|
boolean targetChanged = commandTargetStationId != null
|
&& !commandTargetStationId.equals(previousTargetStationId);
|
boolean queueEmpty = context.pendingPathQueue.isEmpty();
|
boolean newPathContainsCurrent = context.currentStationId != null && newPath.contains(context.currentStationId);
|
boolean pathConnectedToTail = startIndex >= 0;
|
List<Integer> oldPendingStations = context.getPendingStationIds();
|
boolean shouldClearBarcodeSourceOnReroute = context.currentStationId != null
|
&& context.currentStationId.equals(command.getStationId())
|
&& isBarcodeStation(context.currentStationId)
|
&& previousTargetStationId != null
|
&& previousTargetStationId.equals(context.currentStationId)
|
&& commandTargetStationId != null
|
&& !commandTargetStationId.equals(context.currentStationId);
|
|
context.setStartStationIdIfAbsent(command.getStationId());
|
if (!context.generateBarcode && checkTaskNoInArea(context.taskNo)) {
|
context.generateBarcode = true;
|
}
|
|
traceEvent(deviceNo, context, "SEGMENT_RECEIVED", "收到新的路径分段命令",
|
buildDetails("segmentPath", newPath, "appendStartIndex", startIndex, "currentStationId",
|
context.currentStationId, "queueTailStationId", lastInQueue, "commandStationId",
|
command.getStationId(), "commandTargetStationId", commandTargetStationId,
|
"previousTargetStationId", previousTargetStationId),
|
false);
|
|
if (commandTargetStationId != null) {
|
if (targetChanged) {
|
traceEvent(deviceNo, context, "TARGET_SWITCHED",
|
"任务目标站发生切换: " + previousTargetStationId + " -> " + commandTargetStationId,
|
buildDetails("fromTargetStationId", previousTargetStationId, "toTargetStationId",
|
commandTargetStationId),
|
false);
|
context.arrivalHandled = false;
|
}
|
context.finalTargetStationId = commandTargetStationId;
|
syncCurrentStationTarget(context.taskNo, context.currentStationId, context.finalTargetStationId);
|
}
|
context.segmentNo = command.getSegmentNo();
|
context.segmentCount = command.getSegmentCount();
|
|
boolean tailConnectedAppend = !queueEmpty && pathConnectedToTail;
|
boolean shouldReplace = !tailConnectedAppend && !queueEmpty
|
&& (targetChanged || (!pathConnectedToTail && newPathContainsCurrent));
|
String ignoreReason = null;
|
if (!shouldReplace && !tailConnectedAppend && !newPath.isEmpty() && !pathConnectedToTail) {
|
ignoreReason = newPathContainsCurrent ? SEGMENT_MERGE_IGNORE_DISCONNECTED : SEGMENT_MERGE_IGNORE_CURRENT_MISSING;
|
if (queueEmpty && context.currentStationId == null) {
|
startIndex = 0;
|
} else {
|
traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法接入当前运行上下文,已忽略",
|
buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId,
|
"queueTailStationId", lastInQueue, "ignoreReason", ignoreReason,
|
"queueEmpty", queueEmpty, "tailConnectedAppend", tailConnectedAppend,
|
"targetChanged", targetChanged),
|
false);
|
context.latestAppendedPath.clear();
|
return;
|
}
|
}
|
|
if (tailConnectedAppend && startIndex < 0) {
|
startIndex = 0;
|
}
|
|
List<Integer> appendedPath = new ArrayList<Integer>();
|
List<Integer> replacedFuturePath = new ArrayList<Integer>();
|
Integer duplicateHeadStationId = null;
|
String mergeMode = SEGMENT_MERGE_APPEND;
|
if (shouldReplace) {
|
replacedFuturePath = rebuildPendingPathFromCurrent(context, newPath);
|
boolean atTargetAlready = context.currentStationId != null && context.currentStationId.equals(commandTargetStationId);
|
if (context.currentStationId != null && replacedFuturePath.isEmpty() && !atTargetAlready) {
|
traceEvent(deviceNo, context, "SEGMENT_IGNORED", "路径分段无法接入当前运行上下文,已忽略",
|
buildDetails("segmentPath", newPath, "currentStationId", context.currentStationId,
|
"queueTailStationId", lastInQueue, "ignoreReason", SEGMENT_MERGE_IGNORE_CURRENT_MISSING),
|
false);
|
context.latestAppendedPath.clear();
|
return;
|
}
|
replacePendingPathQueue(context, replacedFuturePath);
|
context.replaceLatestPath(replacedFuturePath);
|
mergeMode = targetChanged ? SEGMENT_MERGE_REPLACE_TARGET_CHANGED : SEGMENT_MERGE_REPLACE_REROUTE;
|
traceEvent(deviceNo, context, "SEGMENT_REPLACED",
|
"新的 reroute 路径已覆盖旧 future queue",
|
buildDetails("mergeMode", mergeMode, "currentStationId", context.currentStationId,
|
"previousTargetStationId", previousTargetStationId, "newTargetStationId", commandTargetStationId,
|
"oldQueue", oldPendingStations, "replacedFuturePath", replacedFuturePath,
|
"queueSize", context.pendingPathQueue.size()),
|
false);
|
} else {
|
for (int i = startIndex; i < newPath.size(); i++) {
|
Integer stationId = newPath.get(i);
|
context.pendingPathQueue.offer(stationId);
|
appendedPath.add(stationId);
|
}
|
if (context.currentStationId != null && context.currentStationId.equals(context.pendingPathQueue.peek())) {
|
duplicateHeadStationId = context.pendingPathQueue.poll();
|
if (!appendedPath.isEmpty() && duplicateHeadStationId.equals(appendedPath.get(0))) {
|
appendedPath.remove(0);
|
}
|
}
|
context.appendStitchedPath(appendedPath);
|
traceEvent(deviceNo, context, "SEGMENT_APPENDED",
|
"路径分段已追加到待执行队列,队列长度=" + context.pendingPathQueue.size(),
|
buildDetails("mergeMode", mergeMode, "segmentPath", newPath, "appendedPath", appendedPath,
|
"appendStartIndex", startIndex, "queueSize", context.pendingPathQueue.size(),
|
"tailConnectedAppend", tailConnectedAppend, "targetChanged", targetChanged,
|
"duplicateHeadStationId", duplicateHeadStationId),
|
false);
|
}
|
|
if (duplicateHeadStationId != null) {
|
traceEvent(deviceNo, context, "SEGMENT_TRIMMED", "待执行队列头部重复当前站点,已移除",
|
buildDetails("currentStationId", context.currentStationId,
|
"duplicateHeadStationId", duplicateHeadStationId,
|
"queueSize", context.pendingPathQueue.size(),
|
"remainingPendingPath", context.getPendingStationIds()),
|
false);
|
}
|
|
if (context.currentStationId != null && context.currentStationId.equals(context.pendingPathQueue.peek())) {
|
Integer trimmedHead = context.pendingPathQueue.poll();
|
traceEvent(deviceNo, context, "SEGMENT_TRIMMED", "待执行队列头部仍与当前站点重复,已再次移除",
|
buildDetails("currentStationId", context.currentStationId,
|
"duplicateHeadStationId", trimmedHead,
|
"queueSize", context.pendingPathQueue.size(),
|
"remainingPendingPath", context.getPendingStationIds()),
|
false);
|
}
|
|
context.lastCommandAt = System.currentTimeMillis();
|
|
boolean segmentAccepted = !appendedPath.isEmpty() || !replacedFuturePath.isEmpty()
|
|| (context.currentStationId != null && context.currentStationId.equals(commandTargetStationId))
|
|| (context.currentStationId != null && context.currentStationId.equals(command.getStationId())
|
&& commandTargetStationId != null && commandTargetStationId.equals(context.finalTargetStationId));
|
if (segmentAccepted) {
|
if (shouldClearBarcodeSourceOnReroute && context.currentStationId != null) {
|
Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId);
|
if (currentDeviceNo != null) {
|
guardedClearStationForDispatch(currentDeviceNo, context.currentStationId, context.taskNo, "barcodeSourceRerouteAccepted");
|
traceEvent(deviceNo, context, "SOURCE_STATION_CLEARED", "条码源站已完成任务交接,源站状态已清除",
|
buildDetails("stationId", context.currentStationId,
|
"previousTargetStationId", previousTargetStationId,
|
"newTargetStationId", commandTargetStationId,
|
"mergeMode", mergeMode),
|
false);
|
}
|
}
|
resumeFromCurrentStation(deviceNo, context, mergeMode, appendedPath, replacedFuturePath,
|
shouldClearBarcodeSourceOnReroute);
|
}
|
}
|
|
private long initializeTaskPosition(Integer deviceNo, TaskRuntimeContext context) {
|
Integer nextStationId = context.pendingPathQueue.peek();
|
if (nextStationId == null) {
|
return 0L;
|
}
|
|
if (context.currentStationId == null) {
|
Integer actualCurrentStationId = stateManager.findCurrentStationIdByTask(context.taskNo);
|
if (actualCurrentStationId != null) {
|
context.currentStationId = actualCurrentStationId;
|
context.initialized = true;
|
context.status = STATUS_RUNNING;
|
context.blockedStationId = null;
|
|
Integer actualDeviceNo = stateManager.getDeviceNoByStationId(actualCurrentStationId);
|
if (actualDeviceNo != null) {
|
guardedClearRunBlock(context.taskNo, actualCurrentStationId, actualDeviceNo);
|
}
|
|
trimPendingPathToCurrent(context.pendingPathQueue, actualCurrentStationId);
|
if (actualCurrentStationId.equals(context.pendingPathQueue.peek())) {
|
context.pendingPathQueue.poll();
|
}
|
|
context.addPassedStation(actualCurrentStationId);
|
context.lastStepAt = System.currentTimeMillis();
|
context.lastProgressAt = context.lastStepAt;
|
context.lastProgressStationId = context.currentStationId;
|
guardedPublishTaskLocation(context.taskNo, actualDeviceNo, actualCurrentStationId, true, false);
|
traceEvent(deviceNo, context, "MOVE_INIT", "任务从当前实际站点恢复执行",
|
buildDetails("stationId", actualCurrentStationId, "recovered", true), false);
|
return 0L;
|
}
|
}
|
|
context.currentStationId = nextStationId;
|
Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId);
|
if (currentDeviceNo == null) {
|
context.pendingPathQueue.poll();
|
return 0L;
|
}
|
|
boolean result = moveEngine.initStationMove(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo,
|
context.finalTargetStationId, true, null);
|
if (!result) {
|
return getIdleLoopDelayMs();
|
}
|
|
context.initialized = true;
|
context.status = STATUS_RUNNING;
|
context.pendingPathQueue.poll();
|
context.addPassedStation(context.currentStationId);
|
context.lastStepAt = System.currentTimeMillis();
|
context.lastProgressAt = context.lastStepAt;
|
context.lastProgressStationId = context.currentStationId;
|
guardedPublishTaskLocation(context.taskNo, currentDeviceNo, context.currentStationId, true, false);
|
traceEvent(deviceNo, context, "MOVE_INIT", "任务初始化起点站点",
|
buildDetails("stationId", context.currentStationId, "recovered", false), false);
|
return Math.max(getInitializeDelayMs(), getMoveStepDurationMs());
|
}
|
|
private MoveStepResult executeNextMove(Integer deviceNo, TaskRuntimeContext context) {
|
Integer nextStationId = context.pendingPathQueue.peek();
|
if (nextStationId == null || context.currentStationId == null) {
|
return MoveStepResult.continueNow();
|
}
|
|
Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId);
|
Integer nextDeviceNo = stateManager.getDeviceNoByStationId(nextStationId);
|
if (currentDeviceNo == null || nextDeviceNo == null) {
|
context.pendingPathQueue.poll();
|
return MoveStepResult.continueNow();
|
}
|
|
boolean moveSuccess = moveEngine.stationMoveToNext(context.taskNo, context.currentStationId, currentDeviceNo,
|
nextStationId, nextDeviceNo, context.taskNo, context.finalTargetStationId);
|
if (moveSuccess) {
|
Integer previousStationId = context.currentStationId;
|
context.currentStationId = nextStationId;
|
context.pendingPathQueue.poll();
|
context.addPassedStation(nextStationId);
|
context.arrivalHandled = false;
|
context.blockedStationId = null;
|
context.status = STATUS_RUNNING;
|
context.lastStepAt = System.currentTimeMillis();
|
context.lastProgressAt = context.lastStepAt;
|
context.lastProgressStationId = context.currentStationId;
|
blockManager.clearBlocked(previousStationId);
|
blockManager.clearBlocked(nextStationId);
|
traceEvent(deviceNo, context, "MOVE_STEP_OK", "任务完成一步站点移动",
|
buildDetails("fromStationId", previousStationId, "toStationId", nextStationId,
|
"remainingPendingPath", context.getPendingStationIds()),
|
false);
|
return MoveStepResult.continueAfter(getBlockedLoopDelayMs());
|
}
|
|
long now = System.currentTimeMillis();
|
boolean sameStationNoProgress = context.currentStationId != null
|
&& context.currentStationId.equals(context.lastProgressStationId);
|
long timeoutMs = blockManager.getFakeRunBlockTimeoutMs(redisUtil);
|
long dwellMs = now - context.lastProgressAt;
|
if (!checkTaskNoInArea(context.taskNo) && blockManager.getFakeAllowCheckBlock(redisUtil)
|
&& !blockManager.isSpecialStation(context.currentStationId)
|
&& !blockManager.isBlocked(context.currentStationId)
|
&& sameStationNoProgress
|
&& dwellMs > timeoutMs) {
|
List<Integer> clearedPendingPath = context.getPendingStationIds();
|
News.info("[WCS Debug] fake task清空待执行路径,原因=RUN_BLOCKED,deviceNo={},taskNo={},currentStationId={},targetStationId={},blockedStationId={},pendingBeforeClear={}",
|
deviceNo, context.taskNo, context.currentStationId, context.finalTargetStationId,
|
context.blockedStationId, clearedPendingPath);
|
context.clearPendingPathWithLog(deviceNo, context.taskNo, context.currentStationId,
|
context.finalTargetStationId, context.blockedStationId, "RUN_BLOCKED");
|
guardedRunBlockStation(context.taskNo, context.currentStationId, currentDeviceNo, context.taskNo,
|
context.currentStationId);
|
context.blockedStationId = context.currentStationId;
|
context.status = STATUS_BLOCKED;
|
traceEvent(deviceNo, context, "RUN_BLOCKED", "任务在当前站点停留超时,被标记为堵塞,待执行路径已清空",
|
buildDetails("blockedStationId", context.currentStationId,
|
"lastProgressStationId", context.lastProgressStationId,
|
"dwellMs", dwellMs,
|
"timeoutMs", timeoutMs,
|
"clearedPendingPath", clearedPendingPath), false);
|
return MoveStepResult.continueAfter(getMoveStepDurationMs());
|
}
|
|
context.status = STATUS_WAITING;
|
traceEvent(deviceNo, context, "MOVE_STEP_WAIT", "当前站已更新目标但尚未完成落站,保持当前位置等待下一次推进",
|
buildDetails("currentStationId", context.currentStationId,
|
"nextStationId", nextStationId,
|
"targetStationId", context.finalTargetStationId,
|
"blockedStationId", context.blockedStationId,
|
"pendingPath", context.getPendingStationIds()), false);
|
return MoveStepResult.continueAfter(500L);
|
}
|
|
private void resumeFromCurrentStation(Integer deviceNo, TaskRuntimeContext context, String mergeMode,
|
List<Integer> appendedPath, List<Integer> replacedFuturePath, boolean sourceCleared) {
|
if (context.currentStationId != null) {
|
Integer currentDeviceNo = stateManager.getDeviceNoByStationId(context.currentStationId);
|
if (currentDeviceNo != null) {
|
guardedClearRunBlock(context.taskNo, context.currentStationId, currentDeviceNo);
|
guardedPublishTaskLocation(context.taskNo, currentDeviceNo, context.currentStationId, true, false);
|
}
|
}
|
context.blockedStationId = null;
|
context.status = STATUS_RUNNING;
|
context.lastProgressAt = System.currentTimeMillis();
|
context.lastProgressStationId = context.currentStationId;
|
traceEvent(deviceNo, context, "BLOCK_RESET", "收到新的路径分段,已清除堵塞并重新计时",
|
buildDetails("mergeMode", mergeMode, "currentStationId", context.currentStationId,
|
"targetStationId", context.finalTargetStationId, "queueSize", context.pendingPathQueue.size(),
|
"appendedPath", appendedPath, "replacedFuturePath", replacedFuturePath,
|
"sourceCleared", sourceCleared),
|
false);
|
}
|
|
private boolean shouldAdvanceFromCurrentState(TaskRuntimeContext context) {
|
return context != null
|
&& context.initialized
|
&& context.currentStationId != null
|
&& !context.pendingPathQueue.isEmpty()
|
&& !STATUS_BLOCKED.equals(context.status)
|
&& !STATUS_CANCELLED.equals(context.status)
|
&& !STATUS_TIMEOUT.equals(context.status)
|
&& !STATUS_FINISHED.equals(context.status);
|
}
|
|
private IdleStepResult handleIdleState(Integer deviceNo, TaskRuntimeContext context) {
|
if (shouldAdvanceFromCurrentState(context)) {
|
traceEvent(deviceNo, context, "IDLE_RESUME", "当前站点存在未完成路径,立即从当前站恢复推进",
|
buildDetails("currentStationId", context.currentStationId,
|
"targetStationId", context.finalTargetStationId,
|
"pendingPath", context.getPendingStationIds()), false);
|
context.status = STATUS_RUNNING;
|
return IdleStepResult.waitNext(1L);
|
}
|
|
if (context.currentStationId != null && context.finalTargetStationId != null
|
&& context.currentStationId.equals(context.finalTargetStationId)) {
|
if (!context.arrivalHandled) {
|
boolean barcodeGenerated = false;
|
if (context.generateBarcode) {
|
Integer targetDeviceNo = stateManager.getDeviceNoByStationId(context.finalTargetStationId);
|
if (targetDeviceNo != null) {
|
barcodeGenerated = guardedGenerateStationBarcode(context.taskNo, context.finalTargetStationId,
|
targetDeviceNo);
|
}
|
}
|
context.arrivalHandled = true;
|
context.lastProgressAt = System.currentTimeMillis();
|
context.lastProgressStationId = context.currentStationId;
|
traceEvent(deviceNo, context, "ARRIVED", "任务到达最终目标站点",
|
buildDetails("stationId", context.currentStationId, "barcodeGenerated", barcodeGenerated),
|
false);
|
}
|
|
Integer targetDeviceNo = stateManager.getDeviceNoByStationId(context.finalTargetStationId);
|
if (targetDeviceNo != null) {
|
boolean ownerConflict = stateManager.isFinalStationOwnerConflict(targetDeviceNo, context.finalTargetStationId,
|
context.taskNo);
|
if (ownerConflict) {
|
logFinalStationOwnershipLost(deviceNo, context);
|
context.status = STATUS_BLOCKED;
|
return IdleStepResult.waitNext(getMoveStepDurationMs());
|
}
|
boolean stationCleared = stateManager.isStationClearedForTask(targetDeviceNo, context.finalTargetStationId,
|
context.taskNo);
|
boolean crnTaken = isCrnTakenByTask(context.taskNo);
|
if (stationCleared || crnTaken) {
|
context.status = STATUS_FINISHED;
|
traceEvent(deviceNo, context, "TASK_COMPLETE", "堆垛机已取走货物,任务完成",
|
buildDetails("stationId", context.finalTargetStationId, "stationCleared", stationCleared, "crnTaken", crnTaken), false);
|
return IdleStepResult.finish();
|
}
|
}
|
if (targetDeviceNo != null && !guardedArrivalCompletion(targetDeviceNo, context)) {
|
context.status = STATUS_BLOCKED;
|
return IdleStepResult.waitNext(getMoveStepDurationMs());
|
}
|
|
long dwellMs = System.currentTimeMillis() - context.lastProgressAt;
|
long timeoutMs = blockManager.getFakeRunBlockTimeoutMs(redisUtil);
|
if (!checkTaskNoInArea(context.taskNo) && blockManager.getFakeAllowCheckBlock(redisUtil)
|
&& !blockManager.isSpecialStation(context.currentStationId)
|
&& !blockManager.isBlocked(context.currentStationId)
|
&& dwellMs > timeoutMs) {
|
context.status = STATUS_BLOCKED;
|
context.clearPendingPathWithLog(deviceNo, context.taskNo, context.currentStationId,
|
context.finalTargetStationId, context.blockedStationId, "TARGET_RUN_BLOCKED");
|
guardedRunBlockStation(context.taskNo, context.currentStationId, targetDeviceNo, context.taskNo,
|
context.currentStationId);
|
context.blockedStationId = context.currentStationId;
|
traceEvent(deviceNo, context, "RUN_BLOCKED", "任务到达终点站后停留超时,被标记为堵塞,等待堆垛机取货",
|
buildDetails("blockedStationId", context.currentStationId,
|
"lastProgressStationId", context.lastProgressStationId,
|
"dwellMs", dwellMs,
|
"timeoutMs", timeoutMs), false);
|
return IdleStepResult.waitNext(getMoveStepDurationMs());
|
}
|
|
return IdleStepResult.waitNext(getFinishDelayMs());
|
}
|
|
Long lastTime = taskLastUpdateTime.get(context.taskNo);
|
long waitSegmentTimeoutMs = getWaitSegmentTimeoutMs();
|
if (lastTime != null && System.currentTimeMillis() - lastTime > waitSegmentTimeoutMs) {
|
traceEvent(deviceNo, context, "WAIT_TIMEOUT", "等待新的路径分段超时",
|
buildDetails("timeoutMs", waitSegmentTimeoutMs, "currentStationId", context.currentStationId,
|
"targetStationId", context.finalTargetStationId),
|
false);
|
taskLastUpdateTime.put(context.taskNo, System.currentTimeMillis());
|
}
|
return IdleStepResult.waitNext(getIdleLoopDelayMs());
|
}
|
|
private List<Integer> normalizePath(List<Integer> path) {
|
List<Integer> result = new ArrayList<Integer>();
|
if (path == null) {
|
return result;
|
}
|
for (Integer stationId : path) {
|
if (stationId != null) {
|
result.add(stationId);
|
}
|
}
|
return result;
|
}
|
|
private void trimPendingPathToCurrent(LinkedBlockingQueue<Integer> queue, Integer currentStationId) {
|
if (queue == null || currentStationId == null || queue.isEmpty()) {
|
return;
|
}
|
List<Integer> snapshot = new ArrayList<Integer>(queue);
|
int index = snapshot.indexOf(currentStationId);
|
if (index <= 0) {
|
return;
|
}
|
for (int i = 0; i < index; i++) {
|
queue.poll();
|
}
|
}
|
|
private List<Integer> rebuildPendingPathFromCurrent(TaskRuntimeContext context, List<Integer> newPath) {
|
List<Integer> rebuilt = new ArrayList<Integer>();
|
if (newPath == null || newPath.isEmpty()) {
|
return rebuilt;
|
}
|
Integer currentStationId = context.currentStationId;
|
int startIndex = 0;
|
if (currentStationId != null) {
|
int currentIndex = newPath.indexOf(currentStationId);
|
if (currentIndex < 0) {
|
return rebuilt;
|
}
|
startIndex = currentIndex + 1;
|
}
|
for (int i = startIndex; i < newPath.size(); i++) {
|
rebuilt.add(newPath.get(i));
|
}
|
return rebuilt;
|
}
|
|
private void replacePendingPathQueue(TaskRuntimeContext context, List<Integer> futurePath) {
|
context.pendingPathQueue.clear();
|
if (futurePath == null) {
|
return;
|
}
|
for (Integer stationId : futurePath) {
|
context.pendingPathQueue.offer(stationId);
|
}
|
}
|
|
private boolean hasTaskReset(Integer taskNo) {
|
// 仿真系统不响应外部取消信号(如堵塞重路由触发的 signalSegmentReset),
|
// 避免任务在站点行走过程中被意外清除
|
return false;
|
}
|
|
private Integer getLastInQueue(LinkedBlockingQueue<Integer> queue) {
|
Integer last = null;
|
for (Integer item : queue) {
|
last = item;
|
}
|
return last;
|
}
|
|
private int getPathAppendStartIndex(List<Integer> newPath, Integer currentStationId, Integer lastInQueue) {
|
if (newPath == null || newPath.isEmpty()) {
|
return 0;
|
}
|
|
if (lastInQueue != null) {
|
int idx = newPath.lastIndexOf(lastInQueue);
|
if (idx >= 0) {
|
return idx + 1;
|
}
|
}
|
|
if (currentStationId != null) {
|
int idx = newPath.lastIndexOf(currentStationId);
|
if (idx >= 0) {
|
return idx + 1;
|
}
|
return -1;
|
}
|
|
if (lastInQueue != null) {
|
return -1;
|
}
|
|
return 0;
|
}
|
|
private void syncCurrentStationTarget(Integer taskNo, Integer currentStationId, Integer targetStationId) {
|
if (currentStationId == null || targetStationId == null) {
|
return;
|
}
|
Integer currentDeviceNo = stateManager.getDeviceNoByStationId(currentStationId);
|
if (currentDeviceNo == null) {
|
return;
|
}
|
|
moveEngine.lockStations(currentStationId);
|
try {
|
ZyStationStatusEntity currentStatus = stateManager.findStationStatus(currentDeviceNo, currentStationId);
|
if (currentStatus == null) {
|
return;
|
}
|
|
if (hasOwnerConflict(currentStatus, taskNo)) {
|
logOwnerConflict("syncCurrentStationTarget", currentStationId, currentStatus, taskNo, targetStationId, false,
|
"owner_conflict");
|
return;
|
}
|
|
stateManager.updateStationDataInternal(currentStationId, currentDeviceNo, taskNo, targetStationId, null, null, null);
|
} finally {
|
moveEngine.unlockStations(currentStationId);
|
}
|
}
|
|
private boolean hasOwnerConflict(ZyStationStatusEntity currentStatus, Integer incomingTaskNo) {
|
return currentStatus != null
|
&& currentStatus.isLoading()
|
&& currentStatus.getTaskNo() != null
|
&& currentStatus.getTaskNo() > 0
|
&& (incomingTaskNo == null || !currentStatus.getTaskNo().equals(incomingTaskNo));
|
}
|
|
private void logOwnerConflict(String operation, Integer stationId, ZyStationStatusEntity currentStatus,
|
Integer incomingTaskNo, Integer incomingTargetStationId, boolean finalStation, String reason) {
|
News.info("[WCS Debug] fake station owner冲突,operation={},stationId={},currentTaskNo={},currentLoading={},currentTargetStaNo={},incomingTaskNo={},incomingTargetStaNo={},finalStation={},reason={}",
|
operation,
|
stationId,
|
currentStatus == null ? null : currentStatus.getTaskNo(),
|
currentStatus == null ? null : currentStatus.isLoading(),
|
currentStatus == null ? null : currentStatus.getTargetStaNo(),
|
incomingTaskNo,
|
incomingTargetStationId,
|
finalStation,
|
reason);
|
}
|
|
private boolean ensureStationWritable(String operation, Integer deviceNo, Integer stationId,
|
Integer incomingTaskNo, Integer incomingTargetStationId, boolean finalStation) {
|
if (deviceNo == null || stationId == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = stateManager.findStationStatus(deviceNo, stationId);
|
if (currentStatus == null) {
|
return false;
|
}
|
if (!hasOwnerConflict(currentStatus, incomingTaskNo)) {
|
return true;
|
}
|
logOwnerConflict(operation, stationId, currentStatus, incomingTaskNo, incomingTargetStationId, finalStation,
|
finalStation ? "final_station_owner_conflict" : "owner_conflict");
|
return false;
|
}
|
|
private boolean ensureStationClearable(String operation, Integer deviceNo, Integer stationId, Integer expectedTaskNo,
|
boolean finalStation) {
|
if (deviceNo == null || stationId == null) {
|
return false;
|
}
|
ZyStationStatusEntity currentStatus = stateManager.findStationStatus(deviceNo, stationId);
|
if (currentStatus == null) {
|
return false;
|
}
|
if (!hasOwnerConflict(currentStatus, expectedTaskNo)) {
|
return true;
|
}
|
if (isLegalClearStation(stationId)) {
|
return true;
|
}
|
logOwnerConflict(operation, stationId, currentStatus, expectedTaskNo, currentStatus.getTargetStaNo(), finalStation,
|
"illegal_clear_attempt");
|
return false;
|
}
|
|
private boolean isLegalClearStation(Integer stationId) {
|
if (stationId == null) {
|
return false;
|
}
|
Set<Integer> stationIds = legalClearStationIds;
|
if (stationIds == null || stationIds.isEmpty()) {
|
refreshLegalClearStationIds();
|
stationIds = legalClearStationIds;
|
}
|
return stationIds != null && stationIds.contains(stationId);
|
}
|
|
private boolean isBarcodeStation(Integer stationId) {
|
if (stationId == null) {
|
return false;
|
}
|
Set<Integer> stationIds = barcodeStationIds;
|
if (stationIds == null || stationIds.isEmpty()) {
|
refreshBarcodeStationIds();
|
stationIds = barcodeStationIds;
|
}
|
return stationIds != null && stationIds.contains(stationId);
|
}
|
|
private void refreshLegalClearStationIds() {
|
try {
|
Set<Integer> stationIds = new HashSet<Integer>();
|
|
com.zy.asrs.service.BasCrnpService basCrnpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasCrnpService.class);
|
List<com.zy.asrs.entity.BasCrnp> basCrnps = basCrnpService.list();
|
for (com.zy.asrs.entity.BasCrnp basCrnp : basCrnps) {
|
if (basCrnp == null) {
|
continue;
|
}
|
collectStationIds(stationIds, basCrnp.getInStationList$());
|
collectStationIds(stationIds, basCrnp.getOutStationList$());
|
}
|
|
com.zy.asrs.service.BasDevpService basDevpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasDevpService.class);
|
List<com.zy.asrs.entity.BasDevp> basDevps = basDevpService.list();
|
for (com.zy.asrs.entity.BasDevp basDevp : basDevps) {
|
if (basDevp == null) {
|
continue;
|
}
|
collectStationIds(stationIds, basDevp.getBarcodeStationList$());
|
collectStationIds(stationIds, basDevp.getInStationList$());
|
collectStationIds(stationIds, basDevp.getOutStationList$());
|
}
|
|
legalClearStationIds = stationIds;
|
} catch (Exception e) {
|
News.info("[WCS Debug] fake 合法清站白名单刷新失败,异常类型={},异常信息={}",
|
e.getClass().getSimpleName(), e.getMessage());
|
}
|
}
|
|
private void refreshBarcodeStationIds() {
|
try {
|
Set<Integer> stationIds = new HashSet<Integer>();
|
com.zy.asrs.service.BasDevpService basDevpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasDevpService.class);
|
List<com.zy.asrs.entity.BasDevp> basDevps = basDevpService.list();
|
for (com.zy.asrs.entity.BasDevp basDevp : basDevps) {
|
if (basDevp == null) {
|
continue;
|
}
|
collectStationIds(stationIds, basDevp.getBarcodeStationList$());
|
}
|
barcodeStationIds = stationIds;
|
} catch (Exception e) {
|
News.info("[WCS Debug] fake 条码站白名单刷新失败,异常类型={},异常信息={}",
|
e.getClass().getSimpleName(), e.getMessage());
|
}
|
}
|
|
private void collectStationIds(Set<Integer> stationIds, List<com.zy.core.model.StationObjModel> stations) {
|
if (stationIds == null || stations == null || stations.isEmpty()) {
|
return;
|
}
|
for (com.zy.core.model.StationObjModel station : stations) {
|
if (station != null && station.getStationId() != null) {
|
stationIds.add(station.getStationId());
|
}
|
}
|
}
|
|
private boolean isFinalStation(Integer taskNo, Integer stationId) {
|
TaskRuntimeContext context = taskNo == null ? null : taskContexts.get(taskNo);
|
return context != null && stationId != null && stationId.equals(context.finalTargetStationId);
|
}
|
|
private void logFinalStationOwnershipLost(Integer deviceNo, TaskRuntimeContext context) {
|
Integer stationId = context == null ? null : context.finalTargetStationId;
|
Integer targetDeviceNo = stateManager.getDeviceNoByStationId(stationId);
|
ZyStationStatusEntity currentStatus = targetDeviceNo == null ? null : stateManager.snapshotStation(targetDeviceNo, stationId);
|
News.info("[WCS Debug] fake 最终站所有权丢失,taskNo={},stationId={},currentTaskNo={},currentLoading={},currentTargetStaNo={},reason={}",
|
context == null ? null : context.taskNo,
|
stationId,
|
currentStatus == null ? null : currentStatus.getTaskNo(),
|
currentStatus == null ? null : currentStatus.isLoading(),
|
currentStatus == null ? null : currentStatus.getTargetStaNo(),
|
"final_station_owner_conflict");
|
traceEvent(deviceNo, context, "FINAL_STATION_OWNER_CONFLICT", "最终站仍有物但所有者已被其他任务覆盖",
|
buildDetails("stationId", stationId,
|
"currentTaskNo", currentStatus == null ? null : currentStatus.getTaskNo(),
|
"currentLoading", currentStatus == null ? null : currentStatus.isLoading(),
|
"currentTargetStaNo", currentStatus == null ? null : currentStatus.getTargetStaNo()), false);
|
}
|
|
private boolean guardedClearStationForDispatch(Integer deviceNo, Integer stationId, Integer expectedTaskNo, String reason) {
|
if (!ensureStationClearable("clearStationForDispatch", deviceNo, stationId, expectedTaskNo,
|
isFinalStation(expectedTaskNo, stationId))) {
|
return false;
|
}
|
stateManager.clearStationForDispatch(deviceNo, stationId, reason);
|
return true;
|
}
|
|
private boolean guardedResetStation(Integer deviceNo, Integer stationId, Integer expectedTaskNo) {
|
if (!ensureStationClearable("resetStation", deviceNo, stationId, expectedTaskNo,
|
isFinalStation(expectedTaskNo, stationId))) {
|
return false;
|
}
|
stateManager.resetStation(deviceNo, stationId);
|
return true;
|
}
|
|
private boolean guardedUpdateStationBarcode(Integer deviceNo, Integer stationId, Integer taskNo, String barcode) {
|
if (!ensureStationWritable("updateStationBarcode", deviceNo, stationId, taskNo, null,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
stateManager.updateStationBarcode(deviceNo, stationId, barcode);
|
return true;
|
}
|
|
private boolean guardedGenerateFakeOutStationData(Integer deviceNo, Integer stationId, Integer taskNo) {
|
if (!ensureStationWritable("generateFakeOutStationData", deviceNo, stationId, taskNo, null,
|
isFinalStation(taskNo, stationId))) {
|
News.error("fake 出库站点有物写入被拒绝。deviceNo={},stationId={},taskNo={},reason=station-not-writable",
|
deviceNo, stationId, taskNo);
|
return false;
|
}
|
stateManager.generateFakeOutStationData(deviceNo, stationId);
|
News.info("fake 出库站点有物写入成功。deviceNo={},stationId={},taskNo={},loading=true",
|
deviceNo, stationId, taskNo);
|
return true;
|
}
|
|
private boolean guardedHandoffBarcodeStation(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) {
|
if (!ensureStationWritable("handoffBarcodeStation", deviceNo, stationId, taskNo, targetStationId,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
stateManager.handoffBarcodeStation(deviceNo, taskNo, stationId, targetStationId);
|
return true;
|
}
|
|
private boolean guardedRunBlockStation(Integer taskNo, Integer stationId, Integer deviceNo, Integer ownerTaskNo,
|
Integer targetStationId) {
|
if (!ensureStationWritable("runBlockStation", deviceNo, stationId, ownerTaskNo, targetStationId,
|
isFinalStation(ownerTaskNo, stationId))) {
|
return false;
|
}
|
blockManager.runBlockStation(taskNo, stationId, deviceNo, ownerTaskNo, targetStationId);
|
return true;
|
}
|
|
private boolean guardedClearRunBlock(Integer taskNo, Integer stationId, Integer deviceNo) {
|
if (!ensureStationWritable("clearRunBlock", deviceNo, stationId, taskNo, null,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
blockManager.clearRunBlock(stationId, deviceNo);
|
return true;
|
}
|
|
private boolean guardedPublishTaskLocation(Integer taskNo, Integer deviceNo, Integer stationId, boolean loading, boolean runBlock) {
|
if (loading && !ensureStationWritable("publishTaskLocation", deviceNo, stationId, taskNo, null,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
stateManager.publishTaskLocation(taskNo, deviceNo, stationId, loading, runBlock);
|
return true;
|
}
|
|
private boolean guardedGenerateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) {
|
if (!ensureStationWritable("generateStationData", deviceNo, stationId, taskNo, targetStationId,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
stateManager.generateStationData(deviceNo, taskNo, stationId, targetStationId);
|
return true;
|
}
|
|
private boolean guardedSyncCurrentStationTarget(Integer taskNo, Integer currentStationId, Integer targetStationId) {
|
syncCurrentStationTarget(taskNo, currentStationId, targetStationId);
|
return true;
|
}
|
|
private boolean guardedClearTaskLocationIfMatches(Integer taskNo, Integer deviceNo, Integer stationId) {
|
stateManager.clearTaskLocationIfMatches(taskNo, deviceNo, stationId);
|
return true;
|
}
|
|
private boolean guardedPublishCurrentLocation(Integer taskNo, Integer deviceNo, Integer stationId) {
|
return guardedPublishTaskLocation(taskNo, deviceNo, stationId, true, false);
|
}
|
|
private boolean guardedGenerateStationBarcode(Integer taskNo, Integer stationId, Integer deviceNo) {
|
if (!ensureStationWritable("generateStationBarcode", deviceNo, stationId, taskNo, null,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
return stateManager.generateStationBarcode(taskNo, stationId, deviceNo);
|
}
|
|
private boolean guardedUpdateStationData(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStationId,
|
Boolean loading, String barcode, Boolean runBlock) {
|
if ((taskNo != null || Boolean.TRUE.equals(loading))
|
&& !ensureStationWritable("updateStationData", deviceNo, stationId, taskNo, targetStationId,
|
isFinalStation(taskNo, stationId))) {
|
return false;
|
}
|
return stateManager.updateStationDataInternal(stationId, deviceNo, taskNo, targetStationId, loading, barcode, runBlock);
|
}
|
|
private boolean isFinalStationConflict(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return isFinalStation(taskNo, stationId) && stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedResetOrClearBlockedStation(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return ensureStationClearable("blockedStationClear", deviceNo, stationId, taskNo, isFinalStation(taskNo, stationId));
|
}
|
|
private boolean guardedStationOccupied(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedFinalStationOwnership(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return !stateManager.isFinalStationOwnerConflict(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedFinalStationWritable(Integer taskNo, Integer stationId, Integer deviceNo, String operation) {
|
if (!guardedFinalStationOwnership(taskNo, stationId, deviceNo)) {
|
ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId);
|
logOwnerConflict(operation, stationId, currentStatus, taskNo,
|
currentStatus == null ? null : currentStatus.getTargetStaNo(), true,
|
"final_station_owner_conflict");
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedFinalStationClearable(Integer taskNo, Integer stationId, Integer deviceNo, String operation) {
|
if (!guardedFinalStationOwnership(taskNo, stationId, deviceNo)) {
|
ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId);
|
logOwnerConflict(operation, stationId, currentStatus, taskNo,
|
currentStatus == null ? null : currentStatus.getTargetStaNo(), true,
|
"illegal_clear_attempt");
|
return false;
|
}
|
return true;
|
}
|
|
private boolean shouldTreatAsFinalStationConflict(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return isFinalStation(taskNo, stationId) && stateManager.isFinalStationOwnerConflict(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedFinalStationMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) {
|
if (shouldTreatAsFinalStationConflict(taskNo, stationId, deviceNo)) {
|
ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId);
|
logOwnerConflict(operation, stationId, currentStatus, taskNo, targetStationId, true,
|
"final_station_owner_conflict");
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedFinalStationClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo) {
|
if (shouldTreatAsFinalStationConflict(taskNo, stationId, deviceNo)) {
|
ZyStationStatusEntity currentStatus = stateManager.snapshotStation(deviceNo, stationId);
|
logOwnerConflict(operation, stationId, currentStatus, taskNo,
|
currentStatus == null ? null : currentStatus.getTargetStaNo(), true,
|
"illegal_clear_attempt");
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedStationWrite(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) {
|
if (!guardedFinalStationMutation(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
return ensureStationWritable(operation, deviceNo, stationId, taskNo, targetStationId, isFinalStation(taskNo, stationId));
|
}
|
|
private boolean guardedStationClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo) {
|
if (!guardedFinalStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
return ensureStationClearable(operation, deviceNo, stationId, taskNo, isFinalStation(taskNo, stationId));
|
}
|
|
private boolean guardedStateMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId,
|
Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedStateClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedFinalStationCompletion(Integer deviceNo, TaskRuntimeContext context) {
|
if (context == null || context.finalTargetStationId == null || context.taskNo == null) {
|
return false;
|
}
|
return !stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo);
|
}
|
|
private boolean guardedCurrentStationOwnership(Integer taskNo, Integer currentStationId, Integer currentDeviceNo) {
|
return !stateManager.isOccupiedByOtherLoadingTask(currentDeviceNo, currentStationId, taskNo);
|
}
|
|
private boolean guardedTargetStationOwnership(Integer taskNo, Integer stationId, Integer deviceNo) {
|
return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedCommandWrite(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedCommandClear(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedRunBlockMutation(Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId) {
|
return guardedStationWrite("runBlockStation", taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedClearMutation(Integer taskNo, Integer stationId, Integer deviceNo, String operation) {
|
return guardedStationClear(operation, taskNo, stationId, deviceNo);
|
}
|
|
private boolean guardedArrivalOwnership(Integer deviceNo, TaskRuntimeContext context) {
|
return context != null && context.finalTargetStationId != null && context.taskNo != null
|
&& !stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo);
|
}
|
|
private boolean guardedArrivalStationWritable(Integer deviceNo, TaskRuntimeContext context, String operation) {
|
if (!guardedArrivalOwnership(deviceNo, context)) {
|
logFinalStationOwnershipLost(deviceNo, context);
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedArrivalStationClearable(Integer deviceNo, TaskRuntimeContext context, String operation) {
|
if (!guardedArrivalOwnership(deviceNo, context)) {
|
logFinalStationOwnershipLost(deviceNo, context);
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedStationMutation(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId,
|
Runnable mutation, boolean clearOperation) {
|
boolean allowed = clearOperation
|
? guardedStationClear(operation, taskNo, stationId, deviceNo)
|
: guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
if (!allowed) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedFinalArrivalState(Integer deviceNo, TaskRuntimeContext context) {
|
if (context == null || context.finalTargetStationId == null || context.taskNo == null) {
|
return false;
|
}
|
if (stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo)) {
|
logFinalStationOwnershipLost(deviceNo, context);
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedArrivalCompletion(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedStationOwnership(String operation, Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId,
|
boolean clearOperation) {
|
return clearOperation
|
? guardedStationClear(operation, taskNo, stationId, deviceNo)
|
: guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedStationMutationIfAllowed(String operation, Integer taskNo, Integer stationId, Integer deviceNo,
|
Integer targetStationId, boolean clearOperation, Runnable mutation) {
|
if (!guardedStationOwnership(operation, taskNo, stationId, deviceNo, targetStationId, clearOperation)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedArrivalStationState(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedMutateFinalStation(String operation, Integer taskNo, Integer stationId, Integer deviceNo,
|
Integer targetStationId, Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedClearFinalStation(String operation, Integer taskNo, Integer stationId, Integer deviceNo,
|
Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedArrivalState(Integer deviceNo, TaskRuntimeContext context) {
|
if (!guardedFinalArrivalState(deviceNo, context)) {
|
return false;
|
}
|
return true;
|
}
|
|
private boolean guardedCurrentStationWrite(Integer taskNo, Integer currentStationId, Integer currentDeviceNo, Integer targetStationId,
|
String operation) {
|
return guardedStationWrite(operation, taskNo, currentStationId, currentDeviceNo, targetStationId);
|
}
|
|
private boolean guardedCurrentStationClear(Integer taskNo, Integer currentStationId, Integer currentDeviceNo, String operation) {
|
return guardedStationClear(operation, taskNo, currentStationId, currentDeviceNo);
|
}
|
|
private boolean guardedFinalStationWrite(Integer taskNo, Integer stationId, Integer deviceNo, Integer targetStationId, String operation) {
|
return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedFinalStationClear(Integer taskNo, Integer stationId, Integer deviceNo, String operation) {
|
return guardedStationClear(operation, taskNo, stationId, deviceNo);
|
}
|
|
private boolean guardedStationBarcodeWrite(Integer deviceNo, Integer stationId, Integer taskNo, String barcode) {
|
return guardedUpdateStationBarcode(deviceNo, stationId, taskNo, barcode);
|
}
|
|
private boolean guardedStationOccupancyWrite(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId,
|
Runnable mutation, String operation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedStationOccupancyClear(Integer deviceNo, Integer stationId, Integer taskNo,
|
Runnable mutation, String operation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedStationWriteForTask(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId,
|
String operation) {
|
return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedStationClearForTask(Integer deviceNo, Integer stationId, Integer taskNo, String operation) {
|
return guardedStationClear(operation, taskNo, stationId, deviceNo);
|
}
|
|
private boolean guardedMutateStationIfOwned(Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId,
|
String operation, Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedClearStationIfOwned(Integer deviceNo, Integer stationId, Integer taskNo, String operation,
|
Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedArrivalVisibility(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedFinalStationNotOverwritten(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedMutateOccupiedStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo,
|
Integer targetStationId, Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedClearOccupiedStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo,
|
Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedFinalStationStillOwned(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedStateWrite(String operation, Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId) {
|
return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedStateClear(String operation, Integer deviceNo, Integer stationId, Integer taskNo) {
|
return guardedStationClear(operation, taskNo, stationId, deviceNo);
|
}
|
|
private boolean guardedArrivalOwnerCheck(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedArrivalConflict(Integer deviceNo, TaskRuntimeContext context) {
|
return stateManager.isFinalStationOwnerConflict(deviceNo, context.finalTargetStationId, context.taskNo);
|
}
|
|
private boolean guardedCurrentOwnerCheck(Integer deviceNo, Integer stationId, Integer taskNo) {
|
return !stateManager.isOccupiedByOtherLoadingTask(deviceNo, stationId, taskNo);
|
}
|
|
private boolean guardedMutateStationState(String operation, Integer deviceNo, Integer stationId, Integer taskNo,
|
Integer targetStationId, Runnable mutation) {
|
if (!guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedClearStationState(String operation, Integer deviceNo, Integer stationId, Integer taskNo,
|
Runnable mutation) {
|
if (!guardedStationClear(operation, taskNo, stationId, deviceNo)) {
|
return false;
|
}
|
mutation.run();
|
return true;
|
}
|
|
private boolean guardedArrivalCompletionState(Integer deviceNo, TaskRuntimeContext context) {
|
return guardedFinalArrivalState(deviceNo, context);
|
}
|
|
private boolean guardedWriteToStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo, Integer targetStationId) {
|
return guardedStationWrite(operation, taskNo, stationId, deviceNo, targetStationId);
|
}
|
|
private boolean guardedClearFromStation(String operation, Integer deviceNo, Integer stationId, Integer taskNo) {
|
return guardedStationClear(operation, taskNo, stationId, deviceNo);
|
}
|
|
private void handleCommand(Integer deviceNo, StationCommand command) {
|
News.info("[WCS Debug] fake 非MOVE命令进入device串行执行,deviceNo={},stationId={},targetStaNo={},commandType={},命令数据={}",
|
deviceNo, command.getStationId(), command.getTargetStaNo(), command.getCommandType(), JSON.toJSONString(command));
|
Integer taskNo = command.getTaskNo();
|
Integer stationId = command.getStationId();
|
Integer targetStationId = command.getTargetStaNo();
|
|
if (command.getCommandType() == StationCommandType.RESET) {
|
boolean reset = guardedResetStation(deviceNo, stationId, taskNo);
|
News.info("[WCS Debug] fake RESET已通过统一站点锁执行,deviceNo={},stationId={},accepted={}", deviceNo, stationId, reset);
|
return;
|
}
|
|
if (command.getCommandType() == StationCommandType.WRITE_INFO) {
|
if (command.getBarcode() != null) {
|
boolean updated = guardedUpdateStationBarcode(deviceNo, stationId, taskNo, command.getBarcode());
|
News.info("[WCS Debug] fake WRITE_INFO条码写入已通过统一站点锁执行,deviceNo={},stationId={},barcode={},accepted={}",
|
deviceNo, stationId, command.getBarcode(), updated);
|
return;
|
}
|
if (taskNo == 9998 && targetStationId == 0) {
|
boolean generated = guardedGenerateFakeOutStationData(deviceNo, stationId, taskNo);
|
News.info("[WCS Debug] fake WRITE_INFO有物写入已通过统一站点锁执行,deviceNo={},stationId={},accepted={}", deviceNo, stationId, generated);
|
return;
|
}
|
}
|
|
if (taskNo != null && taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId != null
|
&& stationId.equals(targetStationId)) {
|
boolean handedOff = guardedHandoffBarcodeStation(deviceNo, taskNo, stationId, targetStationId);
|
News.info("[WCS Debug] fake 条码站任务交接完成,deviceNo={},stationId={},taskNo={},targetStationId={},动作=barcodeTaskHandoff,accepted={}",
|
deviceNo, stationId, taskNo, targetStationId, handedOff);
|
}
|
}
|
|
private boolean checkTaskNoInArea(Integer taskNo) {
|
if (taskNo == null || redisUtil == null) {
|
return false;
|
}
|
|
Object fakeTaskNoAreaObj = redisUtil.get(RedisKeyType.FAKE_TASK_NO_AREA.key);
|
if (fakeTaskNoAreaObj == null) {
|
return false;
|
}
|
|
JSONObject data = JSON.parseObject(String.valueOf(fakeTaskNoAreaObj));
|
Integer start = data.getInteger("start");
|
Integer end = data.getInteger("end");
|
if (start == null || end == null) {
|
return false;
|
}
|
return taskNo >= start && taskNo <= end;
|
}
|
|
private Map<String, Object> buildDetails(Object... keyValues) {
|
Map<String, Object> details = new LinkedHashMap<String, Object>();
|
if (keyValues == null) {
|
return details;
|
}
|
for (int i = 0; i + 1 < keyValues.length; i += 2) {
|
Object key = keyValues[i];
|
if (key != null) {
|
details.put(String.valueOf(key), keyValues[i + 1]);
|
}
|
}
|
return details;
|
}
|
|
private void traceEvent(Integer deviceNo, TaskRuntimeContext context, String eventType, String message,
|
Map<String, Object> details, boolean terminal) {
|
if (context != null && context.deviceNo != null && deviceNo != null && !context.deviceNo.equals(deviceNo)) {
|
deviceNo = context.deviceNo;
|
}
|
if (context == null || context.taskNo == null) {
|
return;
|
}
|
Map<String, Object> safeDetails = details == null ? new LinkedHashMap<String, Object>() : new LinkedHashMap<String, Object>(details);
|
if (!safeDetails.containsKey("segmentNo") && context.segmentNo != null) {
|
safeDetails.put("segmentNo", context.segmentNo);
|
}
|
if (!safeDetails.containsKey("segmentCount") && context.segmentCount != null) {
|
safeDetails.put("segmentCount", context.segmentCount);
|
}
|
try {
|
FakeTaskTraceRegistry registry = com.core.common.SpringUtils.getBean(FakeTaskTraceRegistry.class);
|
if (registry != null) {
|
registry.record(context.taskNo, context.threadImpl != null ? context.threadImpl : stateManager.getThreadImpl(deviceNo),
|
context.status, context.startStationId, context.currentStationId, context.finalTargetStationId,
|
context.blockedStationId, context.getStitchedPathStationIds(), context.getPassedStationIds(),
|
context.getPendingStationIds(), context.getLatestAppendedPath(), eventType, message, safeDetails,
|
terminal);
|
}
|
} catch (Exception e) {
|
News.info("[WCS Debug] fake trace记录失败,taskNo={},eventType={},异常类型={},异常信息={}",
|
context.taskNo, eventType, e.getClass().getSimpleName(), e.getMessage());
|
}
|
News.info("[WCS Debug] fake task event,taskNo={},eventType={},status={},currentStationId={},targetStationId={},pending={},details={}",
|
context.taskNo, eventType, context.status, context.currentStationId, context.finalTargetStationId,
|
context.getPendingStationIds(), JSON.toJSONString(safeDetails));
|
}
|
|
private boolean isTerminalStatus(String status) {
|
return STATUS_CANCELLED.equals(status) || STATUS_TIMEOUT.equals(status)
|
|| STATUS_FINISHED.equals(status);
|
}
|
|
private boolean isCurrentLoopGeneration(Integer taskNo, int loopGeneration) {
|
AtomicInteger generation = taskLoopGenerations.get(taskNo);
|
return generation != null && generation.get() == loopGeneration;
|
}
|
|
private Object getTaskLifecycleLock(Integer taskNo) {
|
return taskLifecycleLocks.computeIfAbsent(taskNo, key -> new Object());
|
}
|
|
/**
|
* 检查堆垛机是否已取走指定任务的货物
|
* 通过遍历所有堆垛机设备,检查是否有堆垛机正在执行该任务且已 loaded
|
*/
|
private boolean isCrnTakenByTask(Integer taskNo) {
|
if (taskNo == null || taskNo <= 0) {
|
return false;
|
}
|
try {
|
com.zy.asrs.service.BasCrnpService basCrnpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasCrnpService.class);
|
List<com.zy.asrs.entity.BasCrnp> basCrnps = basCrnpService.list();
|
for (com.zy.asrs.entity.BasCrnp basCrnp : basCrnps) {
|
com.zy.core.thread.CrnThread ct = (com.zy.core.thread.CrnThread) com.zy.core.cache.SlaveConnection.get(
|
com.zy.core.enums.SlaveType.Crn, basCrnp.getCrnNo());
|
if (ct == null) {
|
continue;
|
}
|
com.zy.core.model.protocol.CrnProtocol protocol = ct.getStatus();
|
if (protocol != null && taskNo.equals(protocol.getTaskNo())
|
&& protocol.getLoaded() == 1) {
|
return true;
|
}
|
}
|
} catch (Exception ignore) {
|
}
|
return false;
|
}
|
|
|
private static class MoveStepResult {
|
|
private final boolean shouldContinue;
|
private final long nextDelayMs;
|
|
private MoveStepResult(boolean shouldContinue, long nextDelayMs) {
|
this.shouldContinue = shouldContinue;
|
this.nextDelayMs = nextDelayMs;
|
}
|
|
private static MoveStepResult continueNow() {
|
return new MoveStepResult(true, 0L);
|
}
|
|
private static MoveStepResult continueAfter(long nextDelayMs) {
|
return new MoveStepResult(true, nextDelayMs);
|
}
|
|
private boolean shouldContinue() {
|
return shouldContinue;
|
}
|
|
private long getNextDelayMs() {
|
return nextDelayMs;
|
}
|
}
|
|
private static class IdleStepResult {
|
|
private final boolean finished;
|
private final long nextDelayMs;
|
|
private IdleStepResult(boolean finished, long nextDelayMs) {
|
this.finished = finished;
|
this.nextDelayMs = nextDelayMs;
|
}
|
|
private static IdleStepResult finish() {
|
return new IdleStepResult(true, 0L);
|
}
|
|
private static IdleStepResult waitNext(long nextDelayMs) {
|
return new IdleStepResult(false, nextDelayMs);
|
}
|
|
private boolean isFinished() {
|
return finished;
|
}
|
|
private long getNextDelayMs() {
|
return nextDelayMs;
|
}
|
}
|
|
private static class TaskRuntimeContext {
|
|
private final Integer taskNo;
|
private final Integer deviceNo;
|
private final String threadImpl;
|
private final LinkedBlockingQueue<Integer> pendingPathQueue = new LinkedBlockingQueue<Integer>();
|
private final List<Integer> stitchedPathStationIds = new ArrayList<Integer>();
|
private final List<Integer> passedStationIds = new ArrayList<Integer>();
|
private final List<Integer> latestAppendedPath = new ArrayList<Integer>();
|
|
private Integer startStationId;
|
private Integer currentStationId;
|
private Integer finalTargetStationId;
|
private Integer blockedStationId;
|
private boolean generateBarcode;
|
private boolean initialized;
|
private boolean arrivalHandled;
|
private Integer segmentNo;
|
private Integer segmentCount;
|
private long lastStepAt = System.currentTimeMillis();
|
private long lastCommandAt = System.currentTimeMillis();
|
private long lastProgressAt = System.currentTimeMillis();
|
private Integer lastProgressStationId;
|
private int loopGeneration;
|
private String status = STATUS_WAITING;
|
|
private TaskRuntimeContext(Integer taskNo, Integer deviceNo, String threadImpl) {
|
this.taskNo = taskNo;
|
this.deviceNo = deviceNo;
|
this.threadImpl = threadImpl;
|
}
|
|
private void setStartStationIdIfAbsent(Integer stationId) {
|
if (startStationId == null && stationId != null) {
|
startStationId = stationId;
|
}
|
}
|
|
private void appendStitchedPath(List<Integer> path) {
|
latestAppendedPath.clear();
|
if (path == null) {
|
return;
|
}
|
for (Integer stationId : path) {
|
if (stationId == null) {
|
continue;
|
}
|
latestAppendedPath.add(stationId);
|
if (stitchedPathStationIds.isEmpty()
|
|| !stationId.equals(stitchedPathStationIds.get(stitchedPathStationIds.size() - 1))) {
|
stitchedPathStationIds.add(stationId);
|
}
|
}
|
}
|
|
private void replaceLatestPath(List<Integer> path) {
|
latestAppendedPath.clear();
|
if (path == null) {
|
return;
|
}
|
for (Integer stationId : path) {
|
if (stationId != null) {
|
latestAppendedPath.add(stationId);
|
}
|
}
|
}
|
|
private void clearPendingPath() {
|
pendingPathQueue.clear();
|
latestAppendedPath.clear();
|
}
|
|
private void clearPendingPathWithLog(Integer deviceNo, Integer taskNo, Integer currentStationId,
|
Integer targetStationId, Integer blockedStationId, String reason) {
|
News.info("[WCS Debug] fake task清空待执行路径,原因={},deviceNo={},taskNo={},currentStationId={},targetStationId={},blockedStationId={},pendingBeforeClear={}",
|
reason, deviceNo, taskNo, currentStationId, targetStationId, blockedStationId, getPendingStationIds());
|
clearPendingPath();
|
}
|
|
private void addPassedStation(Integer stationId) {
|
if (stationId == null) {
|
return;
|
}
|
if (passedStationIds.isEmpty()
|
|| !stationId.equals(passedStationIds.get(passedStationIds.size() - 1))) {
|
passedStationIds.add(stationId);
|
}
|
}
|
|
private List<Integer> getPendingStationIds() {
|
return new ArrayList<Integer>(pendingPathQueue);
|
}
|
|
private List<Integer> getPassedStationIds() {
|
return new ArrayList<Integer>(passedStationIds);
|
}
|
|
private List<Integer> getStitchedPathStationIds() {
|
return new ArrayList<Integer>(stitchedPathStationIds);
|
}
|
|
private List<Integer> getLatestAppendedPath() {
|
return new ArrayList<Integer>(latestAppendedPath);
|
}
|
}
|
}
|