| | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.zy.asrs.entity.DeviceConfig; |
| | | import com.zy.asrs.utils.Utils; |
| | | import com.zy.common.model.NavigateNode; |
| | | import com.zy.common.utils.RedisUtil; |
| | | import com.zy.core.News; |
| | | import com.zy.core.enums.RedisKeyType; |
| | |
| | | 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.function.Supplier; |
| | | import java.util.Arrays; |
| | | |
| | | public class ZyStationFakeSegConnect implements ZyStationConnectApi { |
| | | |
| | | private static int LOCK_STATION = 0; |
| | | // 站点级锁:每个站点独立一把锁,提升并发性能 |
| | | private final Map<Integer, ReentrantLock> stationLocks = new ConcurrentHashMap<>(); |
| | | private HashMap<Integer, List<ZyStationStatusEntity>> deviceStatusMap = new HashMap<>(); |
| | | private HashMap<Integer, DeviceConfig> deviceConfigMap = new HashMap<>(); |
| | | private RedisUtil redisUtil; |
| | |
| | | |
| | | @Override |
| | | public boolean connect() { |
| | | Thread checkThread = new Thread(() -> { |
| | | while (true) { |
| | | try { |
| | | for (Map.Entry<Integer, List<ZyStationStatusEntity>> entry : deviceStatusMap.entrySet()) { |
| | | List<ZyStationStatusEntity> stationList = entry.getValue(); |
| | | for (ZyStationStatusEntity statusEntity : stationList) { |
| | | if (statusEntity.isAutoing() |
| | | && statusEntity.isLoading() |
| | | && statusEntity.getTaskNo() > 0 |
| | | && !statusEntity.isRunBlock() |
| | | && !statusEntity.getStationId().equals(statusEntity.getTargetStaNo()) |
| | | ) { |
| | | BlockingQueue<StationCommand> commands = taskQueues.get(statusEntity.getTaskNo()); |
| | | if (commands == null) { |
| | | statusEntity.setRunBlock(true); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | Thread.sleep(100); |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | } |
| | | } |
| | | }); |
| | | checkThread.start(); |
| | | return true; |
| | | } |
| | | |
| | |
| | | } |
| | | DeviceConfig deviceConfig = deviceConfigMap.get(deviceNo); |
| | | if (statusList.isEmpty()) { |
| | | List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(), ZyStationStatusEntity.class); |
| | | List<ZyStationStatusEntity> init = JSON.parseArray(deviceConfig.getFakeInitStatus(), |
| | | ZyStationStatusEntity.class); |
| | | if (init != null) { |
| | | statusList.addAll(init); |
| | | for (ZyStationStatusEntity status : statusList) { |
| | |
| | | // 处理非移动命令 |
| | | if (command.getCommandType() != StationCommandType.MOVE) { |
| | | handleCommand(deviceNo, command); |
| | | }else { |
| | | } 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 { |
| | | // 用于存储当前任务待执行的完整路径队列 |
| | | BlockingQueue<Integer> pathQueue = new LinkedBlockingQueue<>(); |
| | | // 当前是否正在执行移动 |
| | | boolean isMoving = false; |
| | | // 当前移动到的路径索引 |
| | | int currentPathIndex = 0; |
| | | // 完整路径记录 |
| | | List<Integer> fullPath = new ArrayList<>(); |
| | | |
| | | StationCommand initialCommand = null; |
| | | // 待执行的路径队列(存储站点ID序列) |
| | | LinkedBlockingQueue<Integer> 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<StationCommand> commandQueue = taskQueues.get(taskNo); |
| | | if (commandQueue == null) { |
| | | break; |
| | | } |
| | | |
| | | // 尝试获取新命令,如果没有新命令则继续执行现有路径 |
| | | // 尝试获取新的分段命令 |
| | | StationCommand command = commandQueue.poll(100, TimeUnit.MILLISECONDS); |
| | | if (command != null) { |
| | | stepExecuteTime = System.currentTimeMillis(); |
| | | taskLastUpdateTime.put(taskNo, System.currentTimeMillis()); |
| | | |
| | | if (initialCommand == null) { |
| | | initialCommand = command; |
| | | |
| | | // 首次接收命令时初始化 |
| | | if (finalTargetStationId == null) { |
| | | finalTargetStationId = command.getTargetStaNo(); |
| | | if (checkTaskNoInArea(taskNo)) { |
| | | generateBarcode = true; |
| | | } |
| | | } |
| | | |
| | | // 将新路径追加到待执行队列 |
| | | List<Integer> 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); |
| | | } |
| | | // 获取队列中最后一个站点(用于衔接点去重) |
| | | 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 (!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); |
| | | } |
| | | } |
| | | if (!pendingPathQueue.isEmpty()) { |
| | | Integer nextStationId = pendingPathQueue.peek(); |
| | | |
| | | // 取出下一个目标点 |
| | | Integer nextStationId = pathQueue.peek(); // 这里的逻辑需要改为逐个行走 |
| | | // 实际行走逻辑应该是在这里消费 pathQueue |
| | | // 为了简化,我们将 pathQueue 转为 stationMoveByPathIds 的逻辑,但这里需要改造成步进式 |
| | | |
| | | // 重新设计:runTaskLoop 负责不断消费 pathQueue 并执行单步移动 |
| | | if (nextStationId != null) { |
| | | Integer currentStationId = fullPath.get(currentPathIndex); |
| | | if (currentStationId.equals(nextStationId)) { |
| | | pathQueue.poll(); // 移除已到达的点 |
| | | // 如果尚未初始化起点 |
| | | 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 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(); |
| | | stepExecuteTime = System.currentTimeMillis(); |
| | | sleep(1000); // 模拟耗时 |
| | | } else { |
| | | if (!checkTaskNoInArea(taskNo)) { |
| | | boolean fakeAllowCheckBlock = true; |
| | | Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); |
| | | if (systemConfigMapObj != null) { |
| | | HashMap<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj; |
| | | if (systemConfigMap.get("fakeAllowCheckBlock") != null && !systemConfigMap.get("fakeAllowCheckBlock").equals("Y")) { |
| | | fakeAllowCheckBlock = false; |
| | | } |
| | | } |
| | | |
| | | if (fakeAllowCheckBlock && System.currentTimeMillis() - stepExecuteTime > 1000 * 10) { |
| | | //认定堵塞 |
| | | boolean result = runBlockStation(taskNo, currentStationId, currentDeviceNo, taskNo, currentStationId); |
| | | if(result) { |
| | | break; |
| | | } |
| | | // 未找到当前位置(首次执行),首个站点就是起点 |
| | | 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(1000); // 失败重试等待 |
| | | } |
| | | } else { |
| | | pathQueue.poll(); // 无法执行,跳过 |
| | | sleep(500); // 失败重试等待 |
| | | } |
| | | } else { |
| | | // 无法获取设备号,跳过该站点 |
| | | pendingPathQueue.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); |
| | | } |
| | | // 路径队列为空,等待新的分段命令 |
| | | 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) { |
| | |
| | | taskQueues.remove(taskNo); |
| | | taskLastUpdateTime.remove(taskNo); |
| | | taskRunning.remove(taskNo); |
| | | News.info("[WCS Debug] 任务{}执行结束并清理资源", taskNo); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 获取队列中最后一个元素(不移除) |
| | | */ |
| | | private Integer getLastInQueue(LinkedBlockingQueue<Integer> 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<String, String> systemConfigMap = (HashMap<String, String>) systemConfigMapObj; |
| | | String value = systemConfigMap.get("fakeAllowCheckBlock"); |
| | | if (value != null && !value.equals("Y")) { |
| | | fakeAllowCheckBlock = false; |
| | | } |
| | | } |
| | | return fakeAllowCheckBlock; |
| | | } |
| | | |
| | | @Override |
| | |
| | | |
| | | 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<Integer> navigatePath = command.getNavigatePath(); |
| | | if (navigatePath != null && !navigatePath.isEmpty()) { |
| | | segmentedPathCommand(command, generateBarcode); |
| | | return; |
| | | } |
| | | // 注意:MOVE 类型的命令现已在 sendCommand 中处理,handleCommand 仅处理非 MOVE 命令 |
| | | } |
| | | |
| | | private void generateFakeOutStationData(Integer deviceNo, Integer stationId) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void segmentedPathCommand(StationCommand command, boolean generateBarcode) { |
| | | Integer taskNo = command.getTaskNo(); |
| | | Integer finalTargetStationId = command.getTargetStaNo(); |
| | | List<Integer> 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); |
| | | } |
| | | // segmentedPathCommand 方法已删除,功能已整合到 runTaskLoop |
| | | |
| | | private Integer getDeviceNoByStationId(Integer stationId) { |
| | | for (Integer devNo : deviceStatusMap.keySet()) { |
| | |
| | | return null; |
| | | } |
| | | |
| | | private boolean stationMoveByPathIds(List<Integer> 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<String, String> systemConfigMap = (HashMap<String, String>) 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; |
| | | } |
| | | // stationMoveByPathIds 方法已删除,功能已整合到 runTaskLoop |
| | | |
| | | private void sleep(long ms) { |
| | | try { |
| | |
| | | } |
| | | } |
| | | |
| | | public synchronized boolean setLockStation(Integer uuid) { |
| | | if (LOCK_STATION == 0) { |
| | | LOCK_STATION = uuid; |
| | | return true; |
| | | }else { |
| | | if(LOCK_STATION == uuid) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | /** |
| | | * 获取站点锁,如果不存在则创建 |
| | | */ |
| | | private ReentrantLock getStationLock(Integer stationId) { |
| | | return stationLocks.computeIfAbsent(stationId, k -> new ReentrantLock()); |
| | | } |
| | | |
| | | public synchronized boolean releaseLockStation(Integer uuid) { |
| | | if (LOCK_STATION != uuid) { |
| | | return false; |
| | | /** |
| | | * 按顺序锁定多个站点(避免死锁) |
| | | */ |
| | | private void lockStations(Integer... stationIds) { |
| | | Integer[] sorted = Arrays.copyOf(stationIds, stationIds.length); |
| | | Arrays.sort(sorted); |
| | | for (Integer stationId : sorted) { |
| | | getStationLock(stationId).lock(); |
| | | } |
| | | |
| | | 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; |
| | | /** |
| | | * 按逆序解锁多个站点 |
| | | */ |
| | | 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<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | |
| | | return true; |
| | | } |
| | | |
| | | public synchronized boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) { |
| | | boolean executeResult = lockExecute(lockTaskNo, () -> { |
| | | /** |
| | | * 初始化站点移动(使用站点级锁) |
| | | */ |
| | | public boolean initStationMove(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer taskNo, Integer targetStationId, Boolean isLoading, String barcode) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | |
| | | } |
| | | } |
| | | |
| | | boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, targetStationId, isLoading, barcode, false); |
| | | if (!result) { |
| | | return false; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | return executeResult; |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, targetStationId, |
| | | isLoading, barcode, false); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public synchronized boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) { |
| | | boolean executeResult = lockExecute(lockTaskNo, () -> { |
| | | /** |
| | | * 站点移动到下一个位置(使用站点级锁,按ID顺序获取锁避免死锁) |
| | | */ |
| | | public boolean stationMoveToNext(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer nextStationId, Integer nextStationDeviceNo, Integer taskNo, Integer targetStaNo) { |
| | | // 同时锁定当前站点和下一个站点(按ID顺序,避免死锁) |
| | | lockStations(currentStationId, nextStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | | } |
| | | |
| | | List<ZyStationStatusEntity> nextStatusList = deviceStatusMap.get(nextStationDeviceNo); |
| | | if (statusList == null) { |
| | | if (nextStatusList == null) { |
| | | return false; |
| | | } |
| | | |
| | |
| | | return false; |
| | | } |
| | | |
| | | boolean result = updateStationData(lockTaskNo, nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, null, false); |
| | | boolean result = updateStationDataInternal(nextStationId, nextStationDeviceNo, taskNo, targetStaNo, true, |
| | | null, false); |
| | | if (!result) { |
| | | return false; |
| | | } |
| | | |
| | | boolean result2 = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, 0, 0, false, "", false); |
| | | boolean result2 = updateStationDataInternal(currentStationId, currentStationDeviceNo, 0, 0, false, "", |
| | | false); |
| | | if (!result2) { |
| | | return false; |
| | | } |
| | | |
| | | return true; |
| | | }); |
| | | return executeResult; |
| | | } finally { |
| | | unlockStations(currentStationId, nextStationId); |
| | | } |
| | | } |
| | | |
| | | public synchronized boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo) { |
| | | boolean executeResult = lockExecute(lockTaskNo, () -> { |
| | | /** |
| | | * 生成站点条码(使用站点级锁) |
| | | */ |
| | | public boolean generateStationBarcode(Integer lockTaskNo, Integer currentStationId, |
| | | Integer currentStationDeviceNo) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == 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); |
| | | 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; |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, null, null, null, barcode, null); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public synchronized boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { |
| | | boolean executeResult = lockExecute(lockTaskNo, () -> { |
| | | /** |
| | | * 清除站点数据(使用站点级锁) |
| | | */ |
| | | public boolean clearStation(Integer deviceNo, Integer lockTaskNo, Integer currentStationId) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | |
| | | return false; |
| | | } |
| | | |
| | | boolean result = updateStationData(deviceNo, lockTaskNo, currentStationId, 0, 0, false, "", false); |
| | | if (!result) { |
| | | return false; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | return executeResult; |
| | | return updateStationDataInternal(currentStationId, deviceNo, 0, 0, false, "", false); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public synchronized boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, Integer taskNo, Integer blockStationId) { |
| | | boolean executeResult = lockExecute(lockTaskNo, () -> { |
| | | /** |
| | | * 标记站点堵塞(使用站点级锁) |
| | | */ |
| | | public boolean runBlockStation(Integer lockTaskNo, Integer currentStationId, Integer currentStationDeviceNo, |
| | | Integer taskNo, Integer blockStationId) { |
| | | lockStations(currentStationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(currentStationDeviceNo); |
| | | if (statusList == null) { |
| | | return false; |
| | |
| | | return false; |
| | | } |
| | | |
| | | boolean result = updateStationData(lockTaskNo, currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", true); |
| | | if (!result) { |
| | | return false; |
| | | } |
| | | return true; |
| | | }); |
| | | |
| | | return executeResult; |
| | | return updateStationDataInternal(currentStationId, currentStationDeviceNo, taskNo, blockStationId, true, "", |
| | | true); |
| | | } finally { |
| | | unlockStations(currentStationId); |
| | | } |
| | | } |
| | | |
| | | public boolean lockExecute(Integer taskNo, Supplier<Boolean> function) { |
| | | if (!setLockStation(taskNo)) { |
| | | return false; |
| | | } |
| | | /** |
| | | * 清除站点堵塞标记(堵塞恢复时使用) |
| | | */ |
| | | public void clearRunBlock(Integer stationId, Integer deviceNo) { |
| | | lockStations(stationId); |
| | | try { |
| | | List<ZyStationStatusEntity> statusList = deviceStatusMap.get(deviceNo); |
| | | if (statusList == null) { |
| | | return; |
| | | } |
| | | |
| | | boolean result = function.get(); |
| | | releaseLockStation(taskNo); |
| | | return result; |
| | | 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) { |
| | |
| | | Integer start = data.getInteger("start"); |
| | | Integer end = data.getInteger("end"); |
| | | |
| | | if(taskNo >= start && taskNo <= end) { |
| | | if (taskNo >= start && taskNo <= end) { |
| | | return true; |
| | | } |
| | | |