package com.zy.core.thread.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.core.common.SpringUtils; import com.zy.asrs.entity.BasStationOpt; import com.zy.asrs.entity.DeviceConfig; import com.zy.asrs.service.BasStationOptService; import com.zy.common.model.NavigateNode; import com.zy.common.utils.NavigateUtils; import com.zy.common.utils.RedisUtil; import com.zy.core.cache.MessageQueue; import com.zy.core.enums.SlaveType; import com.zy.core.enums.StationCommandType; import com.zy.core.model.CommandResponse; import com.zy.core.model.Task; import com.zy.core.model.command.StationCommand; import com.zy.core.model.protocol.StationProtocol; import com.zy.core.model.protocol.StationTaskBufferItem; import com.zy.core.network.DeviceConnectPool; import com.zy.core.network.ZyStationConnectDriver; import com.zy.core.network.entity.ZyStationStatusEntity; import com.zy.core.service.StationTaskLoopService; import com.zy.core.thread.impl.v5.StationV5RunBlockReroutePlanner; import com.zy.core.thread.impl.v5.StationV5SegmentExecutor; import com.zy.core.thread.impl.v5.StationV5StatusReader; import com.zy.core.thread.support.RecentStationArrivalTracker; import lombok.Data; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @Data @Slf4j public class ZyStationV5Thread implements Runnable, com.zy.core.thread.StationThread { private static final int SEGMENT_EXECUTOR_POOL_SIZE = 64; private DeviceConfig deviceConfig; private RedisUtil redisUtil; private ZyStationConnectDriver zyStationConnectDriver; private final ExecutorService executor = Executors.newFixedThreadPool(SEGMENT_EXECUTOR_POOL_SIZE); private StationV5SegmentExecutor segmentExecutor; private final RecentStationArrivalTracker recentArrivalTracker; private final StationV5StatusReader statusReader; private final StationV5RunBlockReroutePlanner runBlockReroutePlanner; public ZyStationV5Thread(DeviceConfig deviceConfig, RedisUtil redisUtil) { this.deviceConfig = deviceConfig; this.redisUtil = redisUtil; this.recentArrivalTracker = new RecentStationArrivalTracker(redisUtil); this.segmentExecutor = new StationV5SegmentExecutor(deviceConfig, redisUtil, this::sendCommand); this.statusReader = new StationV5StatusReader(deviceConfig, redisUtil, recentArrivalTracker); this.runBlockReroutePlanner = new StationV5RunBlockReroutePlanner(redisUtil); } @Override @SuppressWarnings("InfiniteLoopStatement") public void run() { this.connect(); Thread readThread = new Thread(() -> { while (true) { try { statusReader.readStatus(zyStationConnectDriver); Thread.sleep(100); } catch (Exception e) { log.error("StationV5Thread Fail", e); } } }, "DevpRead-" + deviceConfig.getDeviceNo()); readThread.start(); Thread processThread = new Thread(() -> { while (true) { try { pollAndDispatchQueuedCommand(); Thread.sleep(100); } catch (Exception e) { log.error("StationV5Process Fail", e); } } }, "DevpProcess-" + deviceConfig.getDeviceNo()); processThread.start(); } @Override public boolean connect() { zyStationConnectDriver = new ZyStationConnectDriver(deviceConfig, redisUtil); zyStationConnectDriver.start(); DeviceConnectPool.put(SlaveType.Devp, deviceConfig.getDeviceNo(), zyStationConnectDriver); return true; } @Override public void close() { if (zyStationConnectDriver != null) { zyStationConnectDriver.close(); } if (executor != null) { try { executor.shutdownNow(); } catch (Exception ignore) { } } } @Override public List getStatus() { return statusReader.getStatusList(); } @Override public Map getStatusMap() { Map map = new HashMap<>(); for (StationProtocol stationProtocol : statusReader.getStatusList()) { map.put(stationProtocol.getStationId(), stationProtocol); } return map; } private void pollAndDispatchQueuedCommand() { Task task = MessageQueue.poll(SlaveType.Devp, deviceConfig.getDeviceNo()); if (task == null || task.getStep() == null || task.getStep() != 2) { return; } submitSegmentCommand((StationCommand) task.getData()); } private void submitSegmentCommand(StationCommand command) { if (command == null || executor == null || segmentExecutor == null) { return; } executor.submit(() -> segmentExecutor.execute(command)); } @Override public boolean hasRecentArrival(Integer stationId, Integer taskNo) { return recentArrivalTracker.hasRecentArrival(stationId, taskNo); } @Override public StationCommand getCommand(StationCommandType commandType, Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize) { return getCommand(commandType, taskNo, stationId, targetStationId, palletSize, null); } @Override public StationCommand getCommand(StationCommandType commandType, Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize, Double pathLenFactor) { StationCommand stationCommand = new StationCommand(); stationCommand.setTaskNo(taskNo); stationCommand.setStationId(stationId); stationCommand.setTargetStaNo(targetStationId); stationCommand.setPalletSize(palletSize); stationCommand.setCommandType(commandType); if (commandType == StationCommandType.MOVE && !stationId.equals(targetStationId)) { List nodes = calcPathNavigateNodes(taskNo, stationId, targetStationId, pathLenFactor); return fillMoveCommandPath(stationCommand, nodes, taskNo, stationId, targetStationId); } return stationCommand; } @Override public synchronized StationCommand getRunBlockRerouteCommand(Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize) { return getRunBlockRerouteCommand(taskNo, stationId, targetStationId, palletSize, null); } @Override public synchronized StationCommand getRunBlockRerouteCommand(Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize, Double pathLenFactor) { if (taskNo == null || taskNo <= 0 || stationId == null || targetStationId == null) { return null; } if (Objects.equals(stationId, targetStationId)) { return getCommand(StationCommandType.MOVE, taskNo, stationId, targetStationId, palletSize, pathLenFactor); } StationTaskLoopService taskLoopService = loadStationTaskLoopService(); StationTaskLoopService.LoopEvaluation loopEvaluation = taskLoopService == null ? new StationTaskLoopService.LoopEvaluation(taskNo, stationId, StationTaskLoopService.LoopIdentitySnapshot.empty(), 0, 0, false) : taskLoopService.evaluateLoop(taskNo, stationId, true); log.info("输送线堵塞重规划环线识别,taskNo={}, stationId={}, scopeType={}, localStationCount={}, sourceLoopStationCount={}", taskNo, stationId, loopEvaluation.getLoopIdentity().getScopeType(), loopEvaluation.getLoopIdentity().getLocalStationCount(), loopEvaluation.getLoopIdentity().getSourceLoopStationCount()); List> candidatePathList = calcCandidatePathNavigateNodes(taskNo, stationId, targetStationId, pathLenFactor); List candidateCommandList = new ArrayList<>(); for (List candidatePath : candidatePathList) { StationCommand rerouteCommand = buildMoveCommand(taskNo, stationId, targetStationId, palletSize, candidatePath); if (rerouteCommand == null || rerouteCommand.getNavigatePath() == null || rerouteCommand.getNavigatePath().isEmpty()) { continue; } candidateCommandList.add(rerouteCommand); } StationV5RunBlockReroutePlanner.PlanResult planResult = runBlockReroutePlanner.plan( taskNo, stationId, loopEvaluation, candidateCommandList ); if (candidateCommandList.isEmpty()) { log.warn("输送线堵塞重规划失败,候选路径为空,taskNo={}, planCount={}, stationId={}, targetStationId={}", taskNo, planResult.getPlanCount(), stationId, targetStationId); return null; } StationCommand rerouteCommand = planResult.getCommand(); if (rerouteCommand != null) { if (taskLoopService != null) { taskLoopService.recordLoopIssue(loopEvaluation, "RUN_BLOCK_REROUTE"); } log.info("输送线堵塞重规划选中候选路线,taskNo={}, planCount={}, stationId={}, targetStationId={}, route={}", taskNo, planResult.getPlanCount(), stationId, targetStationId, JSON.toJSONString(rerouteCommand.getNavigatePath())); return rerouteCommand; } log.warn("输送线堵塞重规划未找到可下发路线,taskNo={}, planCount={}, stationId={}, targetStationId={}, triedRoutes={}", taskNo, planResult.getPlanCount(), stationId, targetStationId, JSON.toJSONString(planResult.getIssuedRoutePathList())); return null; } @Override public synchronized boolean clearPath(Integer taskNo) { if (taskNo == null || taskNo <= 0) { return false; } if (zyStationConnectDriver == null) { return false; } List status = getStatus(); if (status == null || status.isEmpty()) { return false; } boolean found = false; boolean success = true; for (StationProtocol stationProtocol : status) { List taskBufferItems = stationProtocol == null ? null : stationProtocol.getTaskBufferItems(); if (taskBufferItems == null || taskBufferItems.isEmpty()) { continue; } Integer stationId = stationProtocol.getStationId(); for (StationTaskBufferItem item : taskBufferItems) { if (item == null || !Objects.equals(taskNo, item.getTaskNo())) { continue; } found = true; if (!zyStationConnectDriver.clearTaskBufferSlot(stationId, item.getSlotIdx())) { success = false; log.warn("输送站缓存区残留路径清理失败。stationId={}, slotIdx={}, taskNo={}", stationId, item.getSlotIdx(), item.getTaskNo()); continue; }else { item.setTaskNo(0); item.setTargetStaNo(0); success = true; log.warn("输送站缓存区残留路径清理成功。stationId={}, slotIdx={}, taskNo={}", stationId, item.getSlotIdx(), item.getTaskNo()); } } } return found && success; } @Override public CommandResponse sendCommand(StationCommand command) { CommandResponse commandResponse = null; try { commandResponse = zyStationConnectDriver.sendCommand(command); } catch (Exception e) { e.printStackTrace(); } finally { BasStationOptService optService = SpringUtils.getBean(BasStationOptService.class); if (optService == null) { return commandResponse; } List statusListEntity = zyStationConnectDriver.getStatus(); ZyStationStatusEntity matched = null; if (statusListEntity != null) { for (ZyStationStatusEntity entity : statusListEntity) { if (entity.getStationId() != null && entity.getStationId().equals(command.getStationId())) { matched = entity; break; } } } BasStationOpt basStationOpt = new BasStationOpt( command.getTaskNo(), command.getStationId(), new Date(), String.valueOf(command.getCommandType()), command.getStationId(), command.getTargetStaNo(), null, null, null, JSON.toJSONString(command), JSON.toJSONString(matched), commandResponse != null && Boolean.TRUE.equals(commandResponse.getResult()) ? 1 : 0, JSON.toJSONString(commandResponse) ); optService.save(basStationOpt); } return commandResponse; } @Override public CommandResponse sendOriginCommand(String address, short[] data) { return zyStationConnectDriver.sendOriginCommand(address, data); } @Override public byte[] readOriginCommand(String address, int length) { return zyStationConnectDriver.readOriginCommand(address, length); } private List calcPathNavigateNodes(Integer taskNo, Integer startStationId, Integer targetStationId, Double pathLenFactor) { NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class); if (navigateUtils == null) { return new ArrayList<>(); } return navigateUtils.calcByStationId(startStationId, targetStationId, taskNo, pathLenFactor); } private List> calcCandidatePathNavigateNodes(Integer taskNo, Integer startStationId, Integer targetStationId, Double pathLenFactor) { NavigateUtils navigateUtils = SpringUtils.getBean(NavigateUtils.class); if (navigateUtils == null) { return new ArrayList<>(); } return navigateUtils.calcCandidatePathByStationId(startStationId, targetStationId, taskNo, pathLenFactor); } private StationCommand buildMoveCommand(Integer taskNo, Integer stationId, Integer targetStationId, Integer palletSize, List nodes) { StationCommand stationCommand = new StationCommand(); stationCommand.setTaskNo(taskNo); stationCommand.setStationId(stationId); stationCommand.setTargetStaNo(targetStationId); stationCommand.setPalletSize(palletSize); stationCommand.setCommandType(StationCommandType.MOVE); return fillMoveCommandPath(stationCommand, nodes, taskNo, stationId, targetStationId); } private StationCommand fillMoveCommandPath(StationCommand stationCommand, List nodes, Integer taskNo, Integer stationId, Integer targetStationId) { List path = new ArrayList<>(); List liftTransferPath = new ArrayList<>(); for (NavigateNode node : nodes) { JSONObject valueObject; try { valueObject = JSONObject.parseObject(node.getNodeValue()); } catch (Exception ignore) { continue; } if (valueObject == null) { continue; } Integer stationNo = valueObject.getInteger("stationId"); if (stationNo == null) { continue; } path.add(stationNo); if (Boolean.TRUE.equals(node.getIsLiftTransferPoint())) { liftTransferPath.add(stationNo); } } if (path.isEmpty()) { log.warn("输送线命令生成失败,路径为空,taskNo={}, stationId={}, targetStationId={}", taskNo, stationId, targetStationId); return null; } stationCommand.setNavigatePath(path); stationCommand.setLiftTransferPath(liftTransferPath); stationCommand.setTargetStaNo(path.get(path.size() - 1)); return stationCommand; } private StationTaskLoopService loadStationTaskLoopService() { try { return SpringUtils.getBean(StationTaskLoopService.class); } catch (Exception ignore) { return null; } } }