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 deviceExecutors = new ConcurrentHashMap(); private final Map> taskQueues = new ConcurrentHashMap>(); private final Map taskLastUpdateTime = new ConcurrentHashMap(); private final Map taskRunning = new ConcurrentHashMap(); private final Map taskContexts = new ConcurrentHashMap(); private final Map taskLoopGenerations = new ConcurrentHashMap(); private final Map taskLifecycleLocks = new ConcurrentHashMap(); private volatile Set legalClearStationIds = new HashSet(); private volatile Set barcodeStationIds = new HashSet(); 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 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 queue = taskQueues.computeIfAbsent(taskNo, key -> new LinkedBlockingQueue()); 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 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 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 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 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 appendedPath = new ArrayList(); List replacedFuturePath = new ArrayList(); 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 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 appendedPath, List 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 normalizePath(List path) { List result = new ArrayList(); if (path == null) { return result; } for (Integer stationId : path) { if (stationId != null) { result.add(stationId); } } return result; } private void trimPendingPathToCurrent(LinkedBlockingQueue queue, Integer currentStationId) { if (queue == null || currentStationId == null || queue.isEmpty()) { return; } List snapshot = new ArrayList(queue); int index = snapshot.indexOf(currentStationId); if (index <= 0) { return; } for (int i = 0; i < index; i++) { queue.poll(); } } private List rebuildPendingPathFromCurrent(TaskRuntimeContext context, List newPath) { List rebuilt = new ArrayList(); 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 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 queue) { Integer last = null; for (Integer item : queue) { last = item; } return last; } private int getPathAppendStartIndex(List 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 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 stationIds = barcodeStationIds; if (stationIds == null || stationIds.isEmpty()) { refreshBarcodeStationIds(); stationIds = barcodeStationIds; } return stationIds != null && stationIds.contains(stationId); } private void refreshLegalClearStationIds() { try { Set stationIds = new HashSet(); com.zy.asrs.service.BasCrnpService basCrnpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasCrnpService.class); List 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 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 stationIds = new HashSet(); com.zy.asrs.service.BasDevpService basDevpService = com.core.common.SpringUtils.getBean(com.zy.asrs.service.BasDevpService.class); List 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 stationIds, List 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 buildDetails(Object... keyValues) { Map details = new LinkedHashMap(); 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 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 safeDetails = details == null ? new LinkedHashMap() : new LinkedHashMap(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 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 pendingPathQueue = new LinkedBlockingQueue(); private final List stitchedPathStationIds = new ArrayList(); private final List passedStationIds = new ArrayList(); private final List latestAppendedPath = new ArrayList(); 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 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 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 getPendingStationIds() { return new ArrayList(pendingPathQueue); } private List getPassedStationIds() { return new ArrayList(passedStationIds); } private List getStitchedPathStationIds() { return new ArrayList(stitchedPathStationIds); } private List getLatestAppendedPath() { return new ArrayList(latestAppendedPath); } } }