package com.zy.ai.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.zy.ai.domain.autotune.AutoTuneControlModeSnapshot; import com.zy.ai.domain.autotune.AutoTuneTaskDetailItem; import com.zy.ai.domain.autotune.AutoTuneParameterSnapshot; import com.zy.ai.domain.autotune.AutoTuneRoutePressureSnapshot; import com.zy.ai.domain.autotune.AutoTuneRuleDefinition; import com.zy.ai.domain.autotune.AutoTuneRuleSnapshotItem; import com.zy.ai.domain.autotune.AutoTuneSnapshot; import com.zy.ai.domain.autotune.AutoTuneStationRuntimeItem; import com.zy.ai.domain.autotune.AutoTuneTaskSnapshot; import com.zy.ai.service.AutoTuneControlModeService; import com.zy.ai.service.AutoTuneSnapshotService; import com.zy.ai.service.FlowTopologySnapshotService; import com.zy.ai.service.RoutePressureSnapshotService; import com.zy.asrs.domain.vo.StationCycleCapacityVo; import com.zy.asrs.domain.vo.StationCycleLoopVo; import com.zy.asrs.entity.BasCrnp; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.BasDualCrnp; import com.zy.asrs.entity.BasStation; import com.zy.asrs.entity.DeviceConfig; import com.zy.asrs.entity.WrkMast; import com.zy.asrs.service.BasCrnpService; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.BasDualCrnpService; import com.zy.asrs.service.BasStationService; import com.zy.asrs.service.DeviceConfigService; import com.zy.asrs.service.StationCycleCapacityService; import com.zy.asrs.service.WrkMastService; import com.zy.core.cache.SlaveConnection; import com.zy.core.enums.SlaveType; import com.zy.core.enums.WrkIoType; import com.zy.core.enums.WrkStsType; import com.zy.core.model.StationObjModel; import com.zy.core.model.protocol.StationProtocol; import com.zy.core.thread.StationThread; import com.zy.system.service.ConfigService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.LinkedHashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; @Service("autoTuneSnapshotService") public class AutoTuneSnapshotServiceImpl implements AutoTuneSnapshotService { private static final Logger LOGGER = LoggerFactory.getLogger(AutoTuneSnapshotServiceImpl.class); private static final int DEFAULT_CRN_OUT_BATCH_RUNNING_LIMIT = 5; private static final int DEFAULT_CONVEYOR_STATION_TASK_LIMIT = 30; private static final int DEFAULT_AI_AUTO_TUNE_INTERVAL_MINUTES = 10; private static final int OUTBOUND_TASK_SAMPLE_LIMIT = 50; private static final int STATION_LIMIT_BLOCKED_TASK_LIMIT = 50; private static final int SYSTEM_MESSAGE_LIMIT = 160; @Autowired private WrkMastService wrkMastService; @Autowired private DeviceConfigService deviceConfigService; @Autowired private StationCycleCapacityService stationCycleCapacityService; @Autowired private FlowTopologySnapshotService flowTopologySnapshotService; @Autowired private RoutePressureSnapshotService routePressureSnapshotService; @Autowired private AutoTuneControlModeService autoTuneControlModeService; @Autowired private ConfigService configService; @Autowired private BasStationService basStationService; @Autowired private BasDevpService basDevpService; @Autowired private BasCrnpService basCrnpService; @Autowired private BasDualCrnpService basDualCrnpService; @Override public AutoTuneSnapshot buildSnapshot() { List activeTasks = loadActiveTasks(); AutoTuneTaskSnapshot taskSnapshot = buildTaskSnapshot(activeTasks); List stationRuntimeSnapshot = buildStationRuntimeSnapshot(); AutoTuneRoutePressureSnapshot routePressureSnapshot = buildRoutePressureSnapshot( activeTasks, taskSnapshot, stationRuntimeSnapshot ); AutoTuneSnapshot snapshot = new AutoTuneSnapshot(); snapshot.setTaskSnapshot(taskSnapshot); snapshot.setStationRuntimeSnapshot(stationRuntimeSnapshot); snapshot.setRoutePressureSnapshot(routePressureSnapshot); snapshot.setCycleLoadSnapshot(buildCycleLoadSnapshot()); snapshot.setFlowTopologySnapshot(flowTopologySnapshotService.buildSnapshot(stationRuntimeSnapshot)); snapshot.setCurrentParameterSnapshot(buildCurrentParameterSnapshot()); snapshot.setRuleSnapshot(buildRuleSnapshot()); snapshot.setControlModeSnapshot(buildControlModeSnapshot()); snapshot.setSnapshotTime(new Date()); return snapshot; } private AutoTuneControlModeSnapshot buildControlModeSnapshot() { return autoTuneControlModeService.currentMode(); } List buildRuleSnapshot() { List result = new ArrayList<>(); for (AutoTuneRuleDefinition.Rule rule : AutoTuneRuleDefinition.rules().values()) { AutoTuneRuleSnapshotItem item = new AutoTuneRuleSnapshotItem(); item.setTargetType(rule.getTargetType().getCode()); item.setTargetKey(rule.getTargetKey()); item.setMinValue(rule.getMinValue()); item.setMaxValue(rule.getMaxValue()); item.setMaxStep(rule.getMaxStep()); item.setCooldownMinutes(rule.getCooldownMinutes()); item.setDynamicMaxValue(rule.isDynamicMaxValue()); item.setDynamicMaxSource(rule.getDynamicMaxSource()); item.setNote(rule.getNote()); result.add(item); } return result; } private AutoTuneTaskSnapshot buildTaskSnapshot() { return buildTaskSnapshot(loadActiveTasks()); } private AutoTuneTaskSnapshot buildTaskSnapshot(List activeTasks) { AutoTuneTaskSnapshot snapshot = new AutoTuneTaskSnapshot(); snapshot.setActiveTaskCount(activeTasks.size()); snapshot.setStationLimitBlockedTasks(buildStationLimitBlockedTasks(activeTasks)); snapshot.setOutboundTaskSamples(buildOutboundTaskSamples(activeTasks)); snapshot.setByTargetStation(countByTargetStation(activeTasks)); snapshot.setByBatch(countByBatch(activeTasks)); snapshot.setByStatus(countByStatus(activeTasks)); snapshot.setByCrn(countByCrn(activeTasks)); snapshot.setByDualCrn(countByDualCrn(activeTasks)); snapshot.setByIoType(countByIoType(activeTasks)); return snapshot; } private AutoTuneRoutePressureSnapshot buildRoutePressureSnapshot(List activeTasks, AutoTuneTaskSnapshot taskSnapshot, List stationRuntimeSnapshot) { if (routePressureSnapshotService == null) { return new AutoTuneRoutePressureSnapshot(); } try { AutoTuneRoutePressureSnapshot snapshot = routePressureSnapshotService.buildSnapshot( activeTasks, taskSnapshot, stationRuntimeSnapshot ); return snapshot == null ? new AutoTuneRoutePressureSnapshot() : snapshot; } catch (Exception exception) { LOGGER.warn( "Build auto tune route pressure snapshot failed, fallback to empty snapshot. activeTaskCount={}, stationRuntimeCount={}", safeList(activeTasks).size(), safeList(stationRuntimeSnapshot).size(), exception ); return new AutoTuneRoutePressureSnapshot(); } } private List buildStationLimitBlockedTasks(List activeTasks) { List result = new ArrayList<>(); for (WrkMast task : sortOutboundTasks(activeTasks)) { if (!hasStationLimitBlockedMessage(task)) { continue; } result.add(toTaskDetailItem(task)); if (result.size() >= STATION_LIMIT_BLOCKED_TASK_LIMIT) { break; } } return result; } private List buildOutboundTaskSamples(List activeTasks) { List result = new ArrayList<>(); for (WrkMast task : sortOutboundTasks(activeTasks)) { result.add(toTaskDetailItem(task)); if (result.size() >= OUTBOUND_TASK_SAMPLE_LIMIT) { break; } } return result; } private List sortOutboundTasks(List activeTasks) { List outboundTasks = new ArrayList<>(); for (WrkMast task : safeList(activeTasks)) { if (isOutboundTask(task)) { outboundTasks.add(task); } } outboundTasks.sort(Comparator .comparing(this::taskBatch, Comparator.nullsLast(String::compareTo)) .thenComparing(this::taskBatchSeq, Comparator.nullsLast(Integer::compareTo)) .thenComparing(this::taskWrkNo, Comparator.nullsLast(Integer::compareTo))); return outboundTasks; } private boolean isOutboundTask(WrkMast task) { return task != null && Integer.valueOf(WrkIoType.OUT.id).equals(task.getIoType()); } private boolean hasStationLimitBlockedMessage(WrkMast task) { if (task == null || task.getSystemMsg() == null) { return false; } return task.getSystemMsg().contains("出库任务上限"); } private AutoTuneTaskDetailItem toTaskDetailItem(WrkMast task) { AutoTuneTaskDetailItem item = new AutoTuneTaskDetailItem(); item.setWrkNo(task.getWrkNo()); item.setWrkSts(task.getWrkSts()); item.setWrkStsDesc(wrkStsDesc(task.getWrkSts())); item.setIoType(task.getIoType()); item.setIoTypeDesc(ioTypeDesc(task.getIoType())); item.setStaNo(task.getStaNo()); item.setSourceStaNo(task.getSourceStaNo()); item.setSourceLocNo(task.getSourceLocNo()); item.setCrnNo(task.getCrnNo()); item.setDualCrnNo(task.getDualCrnNo()); item.setBatch(task.getBatch()); item.setBatchSeq(task.getBatchSeq()); item.setIoTime(task.getIoTime()); item.setSystemMsg(limitText(task.getSystemMsg(), SYSTEM_MESSAGE_LIMIT)); return item; } private String wrkStsDesc(Long wrkSts) { if (wrkSts == null) { return null; } try { return WrkStsType.query(wrkSts).desc; } catch (Exception ignore) { return null; } } private String ioTypeDesc(Integer ioType) { if (ioType == null) { return null; } WrkIoType wrkIoType = WrkIoType.get(ioType); return wrkIoType == null ? null : wrkIoType.desc; } private String limitText(String value, int maxLength) { if (value == null || value.length() <= maxLength) { return value; } return value.substring(0, maxLength); } private String taskBatch(WrkMast task) { return task == null ? null : task.getBatch(); } private Integer taskBatchSeq(WrkMast task) { return task == null ? null : task.getBatchSeq(); } private Integer taskWrkNo(WrkMast task) { return task == null ? null : task.getWrkNo(); } private List loadActiveTasks() { if (wrkMastService == null) { return Collections.emptyList(); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.and(query -> query.notIn("wrk_sts", Arrays.asList( WrkStsType.COMPLETE_INBOUND.sts, WrkStsType.SETTLE_INBOUND.sts, WrkStsType.COMPLETE_OUTBOUND.sts, WrkStsType.SETTLE_OUTBOUND.sts, WrkStsType.COMPLETE_LOC_MOVE.sts, WrkStsType.COMPLETE_CRN_MOVE.sts )) .or() .isNull("wrk_sts")); return safeList(wrkMastService.list(wrapper)); } private List buildStationRuntimeSnapshot() { if (deviceConfigService == null) { return Collections.emptyList(); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("device_type", String.valueOf(SlaveType.Devp)); wrapper.orderByAsc("device_no"); List deviceConfigList = safeList(deviceConfigService.list(wrapper)); Map runtimeMap = new LinkedHashMap<>(); for (DeviceConfig deviceConfig : deviceConfigList) { appendStationRuntime(runtimeMap, deviceConfig); } return new ArrayList<>(runtimeMap.values()); } private void appendStationRuntime(Map runtimeMap, DeviceConfig deviceConfig) { if (deviceConfig == null || deviceConfig.getDeviceNo() == null) { return; } StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, deviceConfig.getDeviceNo()); if (stationThread == null || stationThread.getStatusMap() == null) { return; } for (StationProtocol protocol : stationThread.getStatusMap().values()) { AutoTuneStationRuntimeItem runtimeItem = toRuntimeItem(protocol); if (runtimeItem != null) { runtimeMap.put(runtimeItem.getStationId(), runtimeItem); } } } private AutoTuneStationRuntimeItem toRuntimeItem(StationProtocol protocol) { if (protocol == null || protocol.getStationId() == null) { return null; } AutoTuneStationRuntimeItem item = new AutoTuneStationRuntimeItem(); item.setStationId(protocol.getStationId()); item.setAutoing(protocol.isAutoing() ? 1 : 0); item.setLoading(protocol.isLoading() ? 1 : 0); item.setTaskNo(protocol.getTaskNo() == null ? 0 : protocol.getTaskNo()); item.setRunBlock(protocol.isRunBlock() ? 1 : 0); item.setIoMode(protocol.getIoMode() == null ? null : String.valueOf(protocol.getIoMode())); return item; } private Map buildCycleLoadSnapshot() { if (stationCycleCapacityService == null) { return new LinkedHashMap<>(); } return toCycleLoadMap(stationCycleCapacityService.getLatestSnapshot()); } private Map toCycleLoadMap(StationCycleCapacityVo cycleCapacityVo) { Map result = new LinkedHashMap<>(); if (cycleCapacityVo == null) { return result; } result.put("loopList", toLoopMapList(cycleCapacityVo.getLoopList())); result.put("loopCount", cycleCapacityVo.getLoopCount()); result.put("totalStationCount", cycleCapacityVo.getTotalStationCount()); result.put("taskStationCount", cycleCapacityVo.getTaskStationCount()); result.put("manualStationCount", cycleCapacityVo.getManualStationCount()); result.put("occupiedStationCount", cycleCapacityVo.getOccupiedStationCount()); result.put("currentLoad", cycleCapacityVo.getCurrentLoad()); result.put("refreshTime", cycleCapacityVo.getRefreshTime()); return result; } private List> toLoopMapList(List loopList) { List> result = new ArrayList<>(); for (StationCycleLoopVo loopVo : safeList(loopList)) { if (loopVo == null) { continue; } Map loopMap = new LinkedHashMap<>(); loopMap.put("loopNo", loopVo.getLoopNo()); loopMap.put("stationIdList", loopVo.getStationIdList()); loopMap.put("workNoList", loopVo.getWorkNoList()); loopMap.put("stationCount", loopVo.getStationCount()); loopMap.put("taskCount", loopVo.getTaskCount()); loopMap.put("manualStationCount", loopVo.getManualStationCount()); loopMap.put("occupiedStationCount", loopVo.getOccupiedStationCount()); loopMap.put("currentLoad", loopVo.getCurrentLoad()); result.add(loopMap); } return result; } private AutoTuneParameterSnapshot buildCurrentParameterSnapshot() { AutoTuneParameterSnapshot snapshot = new AutoTuneParameterSnapshot(); snapshot.setCrnOutBatchRunningLimit(readConfigInt( "crnOutBatchRunningLimit", DEFAULT_CRN_OUT_BATCH_RUNNING_LIMIT )); snapshot.setConveyorStationTaskLimit(readConfigInt( "conveyorStationTaskLimit", DEFAULT_CONVEYOR_STATION_TASK_LIMIT )); snapshot.setAiAutoTuneIntervalMinutes(readConfigInt( "aiAutoTuneIntervalMinutes", DEFAULT_AI_AUTO_TUNE_INTERVAL_MINUTES )); List crnList = loadCrnList(); List dualCrnList = loadDualCrnList(); List outStationList = loadOutStationList(); snapshot.setStationOutTaskLimits(buildStationOutTaskLimitMap(outStationList)); snapshot.setStationOutBufferCapacities(buildStationOutBufferCapacityMap(outStationList)); snapshot.setCrnMaxOutTask(buildCrnMaxOutTask(crnList)); snapshot.setCrnMaxInTask(buildCrnMaxInTask(crnList)); snapshot.setDualCrnMaxOutTask(buildDualCrnMaxOutTask(dualCrnList)); snapshot.setDualCrnMaxInTask(buildDualCrnMaxInTask(dualCrnList)); return snapshot; } private int readConfigInt(String code, int defaultValue) { if (configService == null) { return defaultValue; } String value = configService.getConfigValue(code, String.valueOf(defaultValue)); if (value == null || value.trim().isEmpty()) { return defaultValue; } try { return Integer.parseInt(value.trim()); } catch (NumberFormatException exception) { return defaultValue; } } private List loadOutStationList() { if (basStationService == null) { return Collections.emptyList(); } Set outStationIds = loadOutStationIds(); if (outStationIds.isEmpty()) { return Collections.emptyList(); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.in("station_id", outStationIds); wrapper.orderByAsc("station_id"); return safeList(basStationService.list(wrapper)); } private Set loadOutStationIds() { LinkedHashSet stationIds = new LinkedHashSet<>(); if (basDevpService == null) { return stationIds; } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.eq("status", 1); wrapper.orderByAsc("devp_no"); List basDevpList = safeList(basDevpService.list(wrapper)); for (BasDevp basDevp : basDevpList) { if (basDevp == null) { continue; } List outStationList = safeList(basDevp.getOutStationList$()); for (StationObjModel stationObjModel : outStationList) { if (stationObjModel != null && stationObjModel.getStationId() != null) { stationIds.add(stationObjModel.getStationId()); } } } return stationIds; } Map buildStationOutTaskLimitMap(List stationList) { Map result = new LinkedHashMap<>(); for (BasStation station : safeList(stationList)) { if (station != null && station.getStationId() != null) { result.put(String.valueOf(station.getStationId()), station.getOutTaskLimit()); } } return result; } Map buildStationOutBufferCapacityMap(List stationList) { Map result = new LinkedHashMap<>(); for (BasStation station : safeList(stationList)) { if (station != null && station.getStationId() != null) { result.put(String.valueOf(station.getStationId()), station.getOutBufferCapacity()); } } return result; } private Map buildCrnMaxOutTask(List crnList) { Map result = new LinkedHashMap<>(); for (BasCrnp crn : safeList(crnList)) { if (crn == null || crn.getCrnNo() == null) { continue; } result.put(String.valueOf(crn.getCrnNo()), defaultInt(crn.getMaxOutTask())); } return result; } private Map buildCrnMaxInTask(List crnList) { Map result = new LinkedHashMap<>(); for (BasCrnp crn : safeList(crnList)) { if (crn == null || crn.getCrnNo() == null) { continue; } result.put(String.valueOf(crn.getCrnNo()), defaultInt(crn.getMaxInTask())); } return result; } private Map buildDualCrnMaxOutTask(List dualCrnList) { Map result = new LinkedHashMap<>(); for (BasDualCrnp dualCrn : safeList(dualCrnList)) { if (dualCrn == null || dualCrn.getCrnNo() == null) { continue; } result.put(String.valueOf(dualCrn.getCrnNo()), defaultInt(dualCrn.getMaxOutTask())); } return result; } private Map buildDualCrnMaxInTask(List dualCrnList) { Map result = new LinkedHashMap<>(); for (BasDualCrnp dualCrn : safeList(dualCrnList)) { if (dualCrn == null || dualCrn.getCrnNo() == null) { continue; } result.put(String.valueOf(dualCrn.getCrnNo()), defaultInt(dualCrn.getMaxInTask())); } return result; } private List loadCrnList() { if (basCrnpService == null) { return Collections.emptyList(); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.orderByAsc("crn_no"); return safeList(basCrnpService.list(wrapper)); } private List loadDualCrnList() { if (basDualCrnpService == null) { return Collections.emptyList(); } QueryWrapper wrapper = new QueryWrapper<>(); wrapper.orderByAsc("crn_no"); return safeList(basDualCrnpService.list(wrapper)); } private Map countByTargetStation(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { increment(result, task == null ? null : task.getStaNo()); } return result; } private Map countByBatch(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { String batch = task == null ? null : task.getBatch(); if (batch != null && !batch.trim().isEmpty()) { increment(result, batch.trim()); } } return result; } private Map countByStatus(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { Long wrkSts = task == null ? null : task.getWrkSts(); increment(result, wrkSts == null ? null : String.valueOf(wrkSts)); } return result; } private Map countByCrn(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { increment(result, task == null ? null : task.getCrnNo()); } return result; } private Map countByDualCrn(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { increment(result, task == null ? null : task.getDualCrnNo()); } return result; } private Map countByIoType(List activeTasks) { Map result = new LinkedHashMap<>(); for (WrkMast task : activeTasks) { increment(result, task == null ? null : task.getIoType()); } return result; } private void increment(Map result, Integer key) { if (key == null) { return; } increment(result, String.valueOf(key)); } private void increment(Map result, String key) { if (key == null || key.trim().isEmpty()) { return; } String normalizedKey = key.trim(); result.put(normalizedKey, result.getOrDefault(normalizedKey, 0) + 1); } private int defaultInt(Integer value) { return value == null ? 0 : value; } private List safeList(List value) { return value == null ? Collections.emptyList() : value; } }