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.model.NavigateNode; 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.Map; import java.util.function.Supplier; public class ZyStationFakeSegConnect implements ZyStationConnectApi { private static int LOCK_STATION = 0; 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, "任务号为空"); } 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 { // 用于存储当前任务待执行的完整路径队列 BlockingQueue pathQueue = new LinkedBlockingQueue<>(); // 当前是否正在执行移动 boolean isMoving = false; // 当前移动到的路径索引 int currentPathIndex = 0; // 完整路径记录 List fullPath = new ArrayList<>(); StationCommand initialCommand = null; Integer finalTargetStationId = null; boolean generateBarcode = false; while (true) { 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 (initialCommand == null) { initialCommand = command; finalTargetStationId = command.getTargetStaNo(); if (checkTaskNoInArea(taskNo)) { generateBarcode = true; } } List newPath = command.getNavigatePath(); if (newPath != null && !newPath.isEmpty()) { // 如果是第一段路径 if (fullPath.isEmpty()) { fullPath.addAll(newPath); for (Integer stationId : newPath) { pathQueue.offer(stationId); } } else { // 追加路径,需要去重衔接点 Integer lastStationId = fullPath.get(fullPath.size() - 1); int startIndex = 0; if (!newPath.isEmpty() && newPath.get(0).equals(lastStationId)) { startIndex = 1; } for (int i = startIndex; i < newPath.size(); i++) { Integer stationId = newPath.get(i); fullPath.add(stationId); pathQueue.offer(stationId); } } } // 处理非移动命令 if (command.getCommandType() != StationCommandType.MOVE) { handleCommand(deviceNo, command); } } // 执行移动逻辑 if (!pathQueue.isEmpty()) { // 如果刚开始,先初始化当前位置 if (currentPathIndex == 0 && !fullPath.isEmpty()) { Integer startStationId = fullPath.get(0); Integer deviceId = getDeviceNoByStationId(startStationId); if (deviceId != null) { initStationMove(taskNo, startStationId, deviceId, taskNo, finalTargetStationId, true, null); } } // 取出下一个目标点 Integer nextStationId = pathQueue.peek(); // 这里的逻辑需要改为逐个行走 // 实际行走逻辑应该是在这里消费 pathQueue // 为了简化,我们将 pathQueue 转为 stationMoveByPathIds 的逻辑,但这里需要改造成步进式 // 重新设计:runTaskLoop 负责不断消费 pathQueue 并执行单步移动 if (nextStationId != null) { Integer currentStationId = fullPath.get(currentPathIndex); if (currentStationId.equals(nextStationId)) { pathQueue.poll(); // 移除已到达的点 continue; } // 执行从 currentStationId 到 nextStationId 的移动 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) { currentPathIndex++; pathQueue.poll(); sleep(1000); // 模拟耗时 } else { sleep(1000); // 失败重试等待 } } else { pathQueue.poll(); // 无法执行,跳过 } } } else { // 队列为空,检查是否超时 Long lastTime = taskLastUpdateTime.get(taskNo); if (lastTime != null && System.currentTimeMillis() - lastTime > 30000) { // 完成任务后清理 if (fullPath.size() > 0 && generateBarcode) { Integer lastStation = fullPath.get(fullPath.size()-1); Integer lastDevice = getDeviceNoByStationId(lastStation); if (lastDevice != null) { generateStationBarcode(taskNo, finalTargetStationId, lastDevice); } } break; } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { taskQueues.remove(taskNo); taskLastUpdateTime.remove(taskNo); taskRunning.remove(taskNo); } } @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); } List navigatePath = command.getNavigatePath(); if (navigatePath != null && !navigatePath.isEmpty()) { segmentedPathCommand(command, generateBarcode); return; } } 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(""); } } private void segmentedPathCommand(StationCommand command, boolean generateBarcode) { Integer taskNo = command.getTaskNo(); Integer finalTargetStationId = command.getTargetStaNo(); List path = command.getNavigatePath(); if (path == null || path.isEmpty()) { return; } Integer currentStationId = findCurrentStationIdByTask(taskNo); int startIdx = 0; if (currentStationId != null) { int idx = path.indexOf(currentStationId); if (idx >= 0) { startIdx = idx; } } Integer segmentTargetStationId = path.get(path.size() - 1); int endIdx = path.size() - 1; boolean isFinalSegment = segmentTargetStationId.equals(finalTargetStationId); boolean appendMode = currentStationId != null && path.indexOf(currentStationId) >= 0; boolean generateBarcodeFinal = generateBarcode && isFinalSegment; stationMoveByPathIds(path, taskNo, finalTargetStationId, false, generateBarcodeFinal, startIdx, endIdx, appendMode); } 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; } private boolean stationMoveByPathIds(List stationPath, Integer taskNo, Integer targetStationId, boolean clearData, boolean generateBarcode, int startIdx, int endIdx, boolean appendMode) { Integer lastStationId = null; Integer targetStationDeviceNo = getDeviceNoByStationId(targetStationId); long executeTime = System.currentTimeMillis(); int i = Math.max(0, startIdx); while (i < stationPath.size() && i <= endIdx) { if (Thread.currentThread().isInterrupted()) { return false; } Integer currentStationId = stationPath.get(i); Integer currentStationDeviceNo = getDeviceNoByStationId(currentStationId); if (currentStationDeviceNo == null) { return false; } Integer nextStationId = null; Integer nextStationDeviceNo = null; try { nextStationId = stationPath.get(i + 1); nextStationDeviceNo = getDeviceNoByStationId(nextStationId); } catch (Exception ignore) {} if (!checkTaskNoInArea(taskNo)) { boolean fakeAllowCheckBlock = true; Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); if (systemConfigMapObj != null) { HashMap systemConfigMap = (HashMap) systemConfigMapObj; if (!systemConfigMap.get("fakeAllowCheckBlock").equals("Y")) { fakeAllowCheckBlock = false; } } if (fakeAllowCheckBlock && System.currentTimeMillis() - executeTime > 1000 * 10) { boolean result = runBlockStation(taskNo, currentStationId, currentStationDeviceNo, taskNo, currentStationId); if(!result) { continue; } return false; } } if (i == startIdx && !appendMode) { boolean result = initStationMove(taskNo, currentStationId, currentStationDeviceNo, taskNo, targetStationId, true, null); if (!result) { continue; } sleep(1000); if (Thread.currentThread().isInterrupted()) { return false; } } if (nextStationId != null && nextStationDeviceNo != null) { boolean result = stationMoveToNext(taskNo, currentStationId, currentStationDeviceNo, nextStationId, nextStationDeviceNo, taskNo, targetStationId); if (!result) { continue; } lastStationId = currentStationId; } if (currentStationId.equals(targetStationId)) { break; } i++; executeTime = System.currentTimeMillis(); sleep(1000); if (Thread.currentThread().isInterrupted()) { return false; } } if (generateBarcode) { if (lastStationId != null && targetStationDeviceNo != null) { while (true) { if (Thread.currentThread().isInterrupted()) { break; } boolean result = generateStationBarcode(taskNo, targetStationId, targetStationDeviceNo); sleep(1000); if (!result) { continue; } break; } } } if (clearData) { sleep(10000); if (Thread.currentThread().isInterrupted()) { return true; } if (lastStationId != null && targetStationDeviceNo != null) { while (true) { if (Thread.currentThread().isInterrupted()) { break; } boolean result = clearStation(targetStationDeviceNo, taskNo, targetStationId); sleep(1000); if (!result) { continue; } break; } } } return true; } private void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public synchronized boolean setLockStation(Integer uuid) { if (LOCK_STATION == 0) { LOCK_STATION = uuid; return true; }else { if(LOCK_STATION == uuid) { return true; } } return false; } public synchronized boolean releaseLockStation(Integer uuid) { if (LOCK_STATION != uuid) { return false; } LOCK_STATION = 0; return true; } public synchronized boolean updateStationData(Integer lockTaskNo, Integer stationId, Integer deviceNo, Integer taskNo, Integer targetStaNo, Boolean isLoading, String barcode, Boolean runBlock) { if (LOCK_STATION != lockTaskNo) { return false; } 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 synchronized boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) { boolean executeResult = lockExecute(lockTaskNo, () -> { 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; } } boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, targetStationId, isLoading, barcode, false); if (!result) { return false; } return true; }); return executeResult; } public synchronized boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) { boolean executeResult = lockExecute(lockTaskNo, () -> { List statusList = deviceStatusMap.get(currentStationDeviceNo); if (statusList == null) { return false; } List nextStatusList = deviceStatusMap.get(nextStationDeviceNo); if (statusList == 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 = updateStationData(lockTaskNo, nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, null, false); if (!result) { return false; } boolean result2 = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, 0, 0, false, "", false); if (!result2) { return false; } return true; }); return executeResult; } public synchronized boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) { boolean executeResult = lockExecute(lockTaskNo, () -> { 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); boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, null, null, null, barcode, null); if (!result) { return false; } return true; }); return executeResult; } public synchronized boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { boolean executeResult = lockExecute(lockTaskNo, () -> { 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; } boolean result = updateStationData(deviceNo, lockTaskNo, currentStationId, 0, 0, false, "", false); if (!result) { return false; } return true; }); return executeResult; } public synchronized boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer blockStationId) { boolean executeResult = lockExecute(lockTaskNo, () -> { 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; } boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", true); if (!result) { return false; } return true; }); return executeResult; } public boolean lockExecute(Integer taskNo, Supplier function) { if (!setLockStation(taskNo)) { return false; } boolean result = function.get(); releaseLockStation(taskNo); return result; } 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; } }