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.HashMap; import java.util.List; import java.util.Random; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; import java.util.Map; import java.util.Arrays; public class ZyStationFakeSegConnect implements ZyStationConnectApi { // 站点级锁:每个站点独立一把锁,提升并发性能 private final Map stationLocks = new ConcurrentHashMap<>(); private HashMap> deviceStatusMap = new HashMap<>(); private HashMap deviceConfigMap = new HashMap<>(); private RedisUtil redisUtil; private final Map> taskQueues = new ConcurrentHashMap<>(); private final Map taskLastUpdateTime = new ConcurrentHashMap<>(); private final Map taskRunning = new ConcurrentHashMap<>(); private final ExecutorService executor = Executors.newCachedThreadPool(); public void addFakeConnect(DeviceConfig deviceConfig, RedisUtil redisUtil) { this.redisUtil = redisUtil; if (deviceConfigMap.containsKey(deviceConfig.getDeviceNo())) { return; } deviceConfigMap.put(deviceConfig.getDeviceNo(), deviceConfig); deviceStatusMap.put(deviceConfig.getDeviceNo(), new CopyOnWriteArrayList<>()); } @Override public boolean connect() { return true; } @Override public boolean disconnect() { executor.shutdownNow(); return true; } @Override public List getStatus(Integer deviceNo) { List statusList = deviceStatusMap.get(deviceNo); if (statusList == null) { return new ArrayList<>(); } DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); if (statusList.isEmpty()) { List init = JSON.parseArray(deviceConfig.getFakeInitStatus(), ZyStationStatusEntity.class); if (init != null) { statusList.addAll(init); for (ZyStationStatusEntity status : statusList) { status.setAutoing(true); status.setLoading(false); status.setInEnable(true); status.setOutEnable(true); status.setEmptyMk(false); status.setFullPlt(false); status.setRunBlock(false); status.setPalletHeight(0); status.setError(0); status.setBarcode(""); } } } return statusList; } @Override public CommandResponse sendCommand(Integer deviceNo, StationCommand command) { Integer taskNo = command.getTaskNo(); if (taskNo == null) { return new CommandResponse(false, "任务号为空"); } // 处理非移动命令 if (command.getCommandType() != StationCommandType.MOVE) { handleCommand(deviceNo, command); } else { // 将移动命令追加到任务队列(支持分段下发) taskQueues.computeIfAbsent(taskNo, k -> new LinkedBlockingQueue<>()).offer(command); taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); // 只有任务未启动时才启动执行器,后续分段命令仅追加到队列 if (taskRunning.putIfAbsent(taskNo, true) == null) { executor.submit(() -> runTaskLoop(deviceNo, taskNo)); } // 后续分段命令不再返回错误,正常追加到队列 } return new CommandResponse(true, "命令已受理(异步执行)"); } private void runTaskLoop(Integer deviceNo, Integer taskNo) { try { // 待执行的路径队列(存储站点ID序列) LinkedBlockingQueue pendingPathQueue = new LinkedBlockingQueue<>(); // 当前所在站点ID Integer currentStationId = null; // 最终目标站点ID Integer finalTargetStationId = null; // 是否需要生成条码 boolean generateBarcode = false; // 是否已初始化起点 boolean initialized = false; // 上一步执行时间(用于堵塞检测) long stepExecuteTime = System.currentTimeMillis(); while (true) { if (Thread.currentThread().isInterrupted()) { break; } BlockingQueue commandQueue = taskQueues.get(taskNo); if (commandQueue == null) { break; } // 尝试获取新的分段命令 StationCommand command = commandQueue.poll(100, TimeUnit.MILLISECONDS); if (command != null) { taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); // 首次接收命令时初始化 if (finalTargetStationId == null) { finalTargetStationId = command.getTargetStaNo(); if (checkTaskNoInArea(taskNo)) { generateBarcode = true; } } // 将新路径追加到待执行队列 List newPath = command.getNavigatePath(); if (newPath != null && !newPath.isEmpty()) { // 获取队列中最后一个站点(用于衔接点去重) Integer lastInQueue = getLastInQueue(pendingPathQueue); if (lastInQueue == null) { lastInQueue = currentStationId; } int startIndex = 0; // 如果新路径的起点与当前位置或队列末尾重复,则跳过 if (lastInQueue != null && !newPath.isEmpty() && newPath.get(0).equals(lastInQueue)) { startIndex = 1; } for (int i = startIndex; i < newPath.size(); i++) { pendingPathQueue.offer(newPath.get(i)); } News.info("[WCS Debug] 任务{}追加路径段: {} -> 队列大小: {}", taskNo, newPath, pendingPathQueue.size()); } } // 执行移动逻辑 if (!pendingPathQueue.isEmpty()) { Integer nextStationId = pendingPathQueue.peek(); // 如果尚未初始化起点 if (!initialized && currentStationId == null) { // 优先查找托盘当前实际位置(支持堵塞后重路由场景) Integer actualCurrentStationId = findCurrentStationIdByTask(taskNo); if (actualCurrentStationId != null) { // 找到了当前托盘位置,使用实际位置作为起点 currentStationId = actualCurrentStationId; initialized = true; // 清除该站点的 runBlock 标记(堵塞恢复) Integer deviceId = getDeviceNoByStationId(currentStationId); if (deviceId != null) { clearRunBlock(currentStationId, deviceId); } // 如果路径起点与当前位置相同,移除起点避免重复 if (nextStationId.equals(currentStationId)) { pendingPathQueue.poll(); } stepExecuteTime = System.currentTimeMillis(); News.info("[WCS Debug] 任务{}恢复执行,当前位置: {}", taskNo, currentStationId); continue; } // 未找到当前位置(首次执行),首个站点就是起点 currentStationId = nextStationId; Integer deviceId = getDeviceNoByStationId(currentStationId); if (deviceId != null) { boolean result = initStationMove(taskNo, currentStationId, deviceId, taskNo, finalTargetStationId, true, null); if (result) { initialized = true; pendingPathQueue.poll(); // 移除起点 stepExecuteTime = System.currentTimeMillis(); News.info("[WCS Debug] 任务{}初始化起点: {}", taskNo, currentStationId); } } sleep(500); continue; } // 执行从当前站点到下一站点的移动 Integer currentDeviceNo = getDeviceNoByStationId(currentStationId); Integer nextDeviceNo = getDeviceNoByStationId(nextStationId); if (currentDeviceNo != null && nextDeviceNo != null) { boolean moveSuccess = stationMoveToNext(taskNo, currentStationId, currentDeviceNo, nextStationId, nextDeviceNo, taskNo, finalTargetStationId); if (moveSuccess) { currentStationId = nextStationId; pendingPathQueue.poll(); stepExecuteTime = System.currentTimeMillis(); News.info("[WCS Debug] 任务{}移动到站点: {}, 剩余队列: {}", taskNo, currentStationId, pendingPathQueue.size()); sleep(1000); // 模拟移动耗时 } else { // 移动失败,检查是否堵塞 if (!checkTaskNoInArea(taskNo)) { boolean fakeAllowCheckBlock = getFakeAllowCheckBlock(); if (fakeAllowCheckBlock && System.currentTimeMillis() - stepExecuteTime > 10000) { // 认定堵塞 boolean result = runBlockStation(taskNo, currentStationId, currentDeviceNo, taskNo, currentStationId); if (result) { News.info("[WCS Debug] 任务{}在站点{}被标记为堵塞", taskNo, currentStationId); pendingPathQueue.clear(); break; } } } sleep(500); // 失败重试等待 } } else { // 无法获取设备号,跳过该站点 pendingPathQueue.poll(); } } else { // 路径队列为空,等待新的分段命令 if (currentStationId != null && finalTargetStationId != null && currentStationId.equals(finalTargetStationId)) { // 已到达最终目标,正常结束 if (generateBarcode) { Integer targetDeviceNo = getDeviceNoByStationId(finalTargetStationId); if (targetDeviceNo != null) { generateStationBarcode(taskNo, finalTargetStationId, targetDeviceNo); News.info("[WCS Debug] 任务{}到达目标{}并生成条码", taskNo, finalTargetStationId); } } break; } // 未到达最终目标,等待新的分段命令 Long lastTime = taskLastUpdateTime.get(taskNo); if (lastTime != null && System.currentTimeMillis() - lastTime > 30000) { // 超时:30秒内没有收到新分段命令 News.info("[WCS Debug] 任务{}等待分段超时,当前位置: {}, 目标: {}", taskNo, currentStationId, finalTargetStationId); break; } // 继续等待新分段命令(不做任何事情,下一轮循环会尝试获取新命令) } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { taskQueues.remove(taskNo); taskLastUpdateTime.remove(taskNo); taskRunning.remove(taskNo); News.info("[WCS Debug] 任务{}执行结束并清理资源", taskNo); } } /** * 获取队列中最后一个元素(不移除) */ private Integer getLastInQueue(LinkedBlockingQueue queue) { Integer last = null; for (Integer item : queue) { last = item; } return last; } /** * 获取是否允许检查堵塞的配置 */ private boolean getFakeAllowCheckBlock() { boolean fakeAllowCheckBlock = true; Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); if (systemConfigMapObj != null) { HashMap systemConfigMap = (HashMap) systemConfigMapObj; String value = systemConfigMap.get("fakeAllowCheckBlock"); if (value != null && !value.equals("Y")) { fakeAllowCheckBlock = false; } } return fakeAllowCheckBlock; } @Override public CommandResponse sendOriginCommand(String address, short[] data) { return new CommandResponse(true, "原始命令已受理(异步执行)"); } @Override public byte[] readOriginCommand(String address, int length) { return new byte[0]; } private void handleCommand(Integer deviceNo, StationCommand command) { News.info("[WCS Debug] 站点仿真模拟(V3)已启动,命令数据={}", JSON.toJSONString(command)); Integer taskNo = command.getTaskNo(); Integer stationId = command.getStationId(); Integer targetStationId = command.getTargetStaNo(); boolean generateBarcode = false; if (command.getCommandType() == StationCommandType.RESET) { resetStation(deviceNo, stationId); return; } if (checkTaskNoInArea(taskNo)) { generateBarcode = true; } if (command.getCommandType() == StationCommandType.WRITE_INFO) { if (taskNo == 9998 && targetStationId == 0) { // 生成出库站点仿真数据 generateFakeOutStationData(deviceNo, stationId); return; } } if (taskNo > 0 && taskNo != 9999 && taskNo != 9998 && stationId == targetStationId) { generateStationData(deviceNo, taskNo, stationId, targetStationId); } // 注意:MOVE 类型的命令现已在 sendCommand 中处理,handleCommand 仅处理非 MOVE 命令 } private void generateFakeOutStationData(Integer deviceNo, Integer stationId) { List statusList = deviceStatusMap.get(deviceNo); ZyStationStatusEntity status = statusList.stream() .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); if (status == null) { return; } synchronized (status) { status.setLoading(true); } } private void generateStationData(Integer deviceNo, Integer taskNo, Integer stationId, Integer targetStationId) { List statusList = deviceStatusMap.get(deviceNo); ZyStationStatusEntity status = statusList.stream() .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); if (status == null) { return; } synchronized (status) { status.setTaskNo(taskNo); status.setTargetStaNo(targetStationId); } } private void resetStation(Integer deviceNo, Integer stationId) { List statusList = deviceStatusMap.get(deviceNo); ZyStationStatusEntity status = statusList.stream() .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); if (status == null) { return; } synchronized (status) { status.setTaskNo(0); status.setLoading(false); status.setBarcode(""); } } // segmentedPathCommand 方法已删除,功能已整合到 runTaskLoop private Integer getDeviceNoByStationId(Integer stationId) { for (Integer devNo : deviceStatusMap.keySet()) { List list = deviceStatusMap.get(devNo); if (list == null) { continue; } for (ZyStationStatusEntity e : list) { if (e.getStationId() != null && e.getStationId().equals(stationId)) { return devNo; } } } return null; } private Integer findCurrentStationIdByTask(Integer taskNo) { for (Integer devNo : deviceStatusMap.keySet()) { List list = deviceStatusMap.get(devNo); if (list == null) { continue; } for (ZyStationStatusEntity e : list) { if (e.getTaskNo() != null && e.getTaskNo().equals(taskNo) && e.isLoading()) { return e.getStationId(); } } } return null; } // stationMoveByPathIds 方法已删除,功能已整合到 runTaskLoop private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * 获取站点锁,如果不存在则创建 */ private ReentrantLock getStationLock(Integer stationId) { return stationLocks.computeIfAbsent(stationId, k -> new ReentrantLock()); } /** * 按顺序锁定多个站点(避免死锁) */ private void lockStations(Integer... stationIds) { Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); Arrays.sort(sorted); for (Integer stationId : sorted) { getStationLock(stationId).lock(); } } /** * 按逆序解锁多个站点 */ private void unlockStations(Integer... stationIds) { Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); Arrays.sort(sorted); for (int i = sorted.length - 1; i >= 0; i--) { getStationLock(sorted[i]).unlock(); } } /** * 更新站点数据(调用前必须已持有该站点的锁) */ private boolean updateStationDataInternal(Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, Boolean isLoading, String barcode, Boolean runBlock) { List statusList = deviceStatusMap.get(deviceNo); if (statusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); if (currentStatus == null) { return false; } if (taskNo != null) { currentStatus.setTaskNo(taskNo); } if (targetStaNo != null) { currentStatus.setTargetStaNo(targetStaNo); } if (isLoading != null) { currentStatus.setLoading(isLoading); } if (barcode != null) { currentStatus.setBarcode(barcode); } if (runBlock != null) { currentStatus.setRunBlock(runBlock); } return true; } /** * 初始化站点移动(使用站点级锁) */ public boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) { lockStations(currentStationId); try { List statusList = deviceStatusMap.get(currentStationDeviceNo); if (statusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); if (currentStatus == null) { return false; } if (currentStatus.getTaskNo() > 0) { if (!currentStatus.getTaskNo().equals(taskNo) && currentStatus.isLoading()) { return false; } } return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, targetStationId, isLoading, barcode, false); } finally { unlockStations(currentStationId); } } /** * 站点移动到下一个位置(使用站点级锁,按ID顺序获取锁避免死锁) */ public boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) { // 同时锁定当前站点和下一个站点(按ID顺序,避免死锁) lockStations(currentStationId, nextStationId); try { List statusList = deviceStatusMap.get(currentStationDeviceNo); if (statusList == null) { return false; } List nextStatusList = deviceStatusMap.get(nextStationDeviceNo); if (nextStatusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); ZyStationStatusEntity nextStatus = nextStatusList.stream() .filter(item -> item.getStationId().equals(nextStationId)).findFirst().orElse(null); if (currentStatus == null || nextStatus == null) { return false; } if (nextStatus.getTaskNo() > 0 || nextStatus.isLoading()) { return false; } boolean result = updateStationDataInternal(nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, null, false); if (!result) { return false; } boolean result2 = updateStationDataInternal(currentStationId, currentStationDeviceNo, 0, 0, false, "", false); if (!result2) { return false; } return true; } finally { unlockStations(currentStationId, nextStationId); } } /** * 生成站点条码(使用站点级锁) */ public boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) { lockStations(currentStationId); try { List statusList = deviceStatusMap.get(currentStationDeviceNo); if (statusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); if (currentStatus == null) { return false; } Random random = new Random(); String barcodeTime = String.valueOf(System.currentTimeMillis()); String barcode = String.valueOf(random.nextInt(10)) + String.valueOf(random.nextInt(10)) + barcodeTime.substring(7); return updateStationDataInternal(currentStationId, currentStationDeviceNo, null, null, null, barcode, null); } finally { unlockStations(currentStationId); } } /** * 清除站点数据(使用站点级锁) */ public boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { lockStations(currentStationId); try { List statusList = deviceStatusMap.get(deviceNo); if (statusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); if (currentStatus == null) { return false; } return updateStationDataInternal(currentStationId, deviceNo, 0, 0, false, "", false); } finally { unlockStations(currentStationId); } } /** * 标记站点堵塞(使用站点级锁) */ public boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer blockStationId) { lockStations(currentStationId); try { List statusList = deviceStatusMap.get(currentStationDeviceNo); if (statusList == null) { return false; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(currentStationId)).findFirst().orElse(null); if (currentStatus == null) { return false; } return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", true); } finally { unlockStations(currentStationId); } } /** * 清除站点堵塞标记(堵塞恢复时使用) */ public void clearRunBlock(Integer stationId, Integer deviceNo) { lockStations(stationId); try { List statusList = deviceStatusMap.get(deviceNo); if (statusList == null) { return; } ZyStationStatusEntity currentStatus = statusList.stream() .filter(item -> item.getStationId().equals(stationId)).findFirst().orElse(null); if (currentStatus == null) { return; } if (currentStatus.isRunBlock()) { currentStatus.setRunBlock(false); News.info("[WCS Debug] 站点{}堵塞标记已清除", stationId); } } finally { unlockStations(stationId); } } private boolean checkTaskNoInArea(Integer taskNo) { 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 (taskNo >= start && taskNo <= end) { return true; } return false; } }