package com.zy.ai.service.impl; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.zy.ai.domain.autotune.AutoTuneHotPathSegmentItem; import com.zy.ai.domain.autotune.AutoTuneRoutePressureSnapshot; import com.zy.ai.domain.autotune.AutoTuneRoutePressureRuleSnapshot; import com.zy.ai.domain.autotune.AutoTuneStationRuntimeItem; import com.zy.ai.domain.autotune.AutoTuneTargetStationRoutePressureItem; import com.zy.ai.domain.autotune.AutoTuneTaskDetailItem; import com.zy.ai.domain.autotune.AutoTuneTaskRouteSampleItem; import com.zy.ai.domain.autotune.AutoTuneTaskSnapshot; import com.zy.ai.service.RoutePressureSnapshotService; import com.zy.asrs.domain.vo.StationTaskTraceVo; import com.zy.asrs.entity.WrkMast; import com.zy.common.model.NavigateNode; import com.zy.common.utils.NavigateUtils; import com.zy.core.enums.WrkIoType; import com.zy.core.enums.WrkStsType; import com.zy.core.trace.StationTaskTraceRegistry; import com.zy.core.utils.station.StationOutboundDecisionSupport; import com.zy.system.service.ConfigService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; @Service("routePressureSnapshotService") public class RoutePressureSnapshotServiceImpl implements RoutePressureSnapshotService { private static final int MAX_ANALYZED_TASK_COUNT = 50; private static final int DEFAULT_SEGMENT_WINDOW_SIZE = 4; private static final int DEFAULT_MEDIUM_PERCENT = 50; private static final int DEFAULT_HIGH_PERCENT = 75; private static final int DEFAULT_PASS_WEIGHT_PERCENT = 35; private static final int DEFAULT_OCCUPIED_WEIGHT_PERCENT = 25; private static final int DEFAULT_BLOCKED_WEIGHT_PERCENT = 20; private static final int DEFAULT_NON_AUTOING_WEIGHT_PERCENT = 10; private static final int DEFAULT_RUN_BLOCK_WEIGHT_PERCENT = 10; private static final int MAX_HOT_PATH_SEGMENT_COUNT = 10; private static final int MAX_TARGET_MAIN_SEGMENT_COUNT = 3; private static final String CONFIG_SEGMENT_WINDOW_SIZE = "aiAutoTuneRoutePressureSegmentWindowSize"; private static final String CONFIG_MEDIUM_PERCENT = "aiAutoTuneRoutePressureMediumPercent"; private static final String CONFIG_HIGH_PERCENT = "aiAutoTuneRoutePressureHighPercent"; private static final String CONFIG_PASS_WEIGHT_PERCENT = "aiAutoTuneRoutePressurePassWeightPercent"; private static final String CONFIG_OCCUPIED_WEIGHT_PERCENT = "aiAutoTuneRoutePressureOccupiedWeightPercent"; private static final String CONFIG_BLOCKED_WEIGHT_PERCENT = "aiAutoTuneRoutePressureBlockedWeightPercent"; private static final String CONFIG_NON_AUTOING_WEIGHT_PERCENT = "aiAutoTuneRoutePressureNonAutoingWeightPercent"; private static final String CONFIG_RUN_BLOCK_WEIGHT_PERCENT = "aiAutoTuneRoutePressureRunBlockWeightPercent"; private static final String PATH_SOURCE_TRACE_PENDING = "trace_pending"; private static final String PATH_SOURCE_TRACE_ISSUED = "trace_issued"; private static final String PATH_SOURCE_ESTIMATED = "estimated"; private static final String PATH_SOURCE_MISSING = "missing"; private static final String PRESSURE_HIGH = "high"; private static final String PRESSURE_MEDIUM = "medium"; private static final String PRESSURE_LOW = "low"; private static final String DIRECTION_INCREASE_CANDIDATE = "increase_candidate"; private static final String DIRECTION_OBSERVE = "observe"; private static final String DIRECTION_DECREASE_CANDIDATE = "decrease_candidate"; private static final String CONFIDENCE_HIGH = "high"; private static final String CONFIDENCE_MEDIUM = "medium"; private static final String CONFIDENCE_LOW = "low"; @Autowired private StationTaskTraceRegistry stationTaskTraceRegistry; @Autowired private NavigateUtils navigateUtils; @Autowired private StationOutboundDecisionSupport stationOutboundDecisionSupport; @Autowired private ConfigService configService; @Override public AutoTuneRoutePressureSnapshot buildSnapshot(List activeTasks, AutoTuneTaskSnapshot taskSnapshot, List stationRuntimeSnapshot) { AutoTuneRoutePressureSnapshot snapshot = new AutoTuneRoutePressureSnapshot(); AutoTuneRoutePressureRuleSnapshot ruleSnapshot = buildRoutePressureRuleSnapshot(); List selectedTasks = selectTasksForAnalysis(activeTasks, taskSnapshot); Map traceMap = buildTraceMap(); Map estimatedPathCache = new LinkedHashMap<>(); List routeSamples = new ArrayList<>(); for (WrkMast task : selectedTasks) { AutoTuneTaskRouteSampleItem routeSample = buildRouteSample(task, traceMap, estimatedPathCache); routeSamples.add(routeSample); } snapshot.setAnalyzedTaskCount(selectedTasks.size()); snapshot.setTracePathCount(countByPathSource(routeSamples, PATH_SOURCE_TRACE_PENDING) + countByPathSource(routeSamples, PATH_SOURCE_TRACE_ISSUED)); snapshot.setEstimatedPathCount(countByPathSource(routeSamples, PATH_SOURCE_ESTIMATED)); snapshot.setPathErrorCount(countByPathSource(routeSamples, PATH_SOURCE_MISSING)); snapshot.setConfidence(snapshotConfidence(snapshot.getAnalyzedTaskCount(), snapshot.getPathErrorCount())); snapshot.setRoutePressureRuleSnapshot(ruleSnapshot); snapshot.setTaskRouteSamples(routeSamples); Map runtimeMap = buildRuntimeMap(stationRuntimeSnapshot); List allHotPathSegments = buildHotPathSegments( routeSamples, runtimeMap, ruleSnapshot ); snapshot.setHotPathSegments(topHotPathSegments(allHotPathSegments)); snapshot.setTargetStationRoutePressure(buildTargetStationRoutePressure( routeSamples, allHotPathSegments, taskSnapshot, ruleSnapshot )); return snapshot; } private AutoTuneRoutePressureRuleSnapshot buildRoutePressureRuleSnapshot() { AutoTuneRoutePressureRuleSnapshot snapshot = new AutoTuneRoutePressureRuleSnapshot(); snapshot.setSegmentWindowSize(readIntConfig( CONFIG_SEGMENT_WINDOW_SIZE, DEFAULT_SEGMENT_WINDOW_SIZE, 2, 20 )); snapshot.setMediumPercent(readIntConfig(CONFIG_MEDIUM_PERCENT, DEFAULT_MEDIUM_PERCENT, 1, 100)); snapshot.setHighPercent(readIntConfig(CONFIG_HIGH_PERCENT, DEFAULT_HIGH_PERCENT, 1, 100)); if (snapshot.getMediumPercent() > snapshot.getHighPercent()) { snapshot.setMediumPercent(DEFAULT_MEDIUM_PERCENT); snapshot.setHighPercent(DEFAULT_HIGH_PERCENT); } snapshot.setPassWeightPercent(readIntConfig(CONFIG_PASS_WEIGHT_PERCENT, DEFAULT_PASS_WEIGHT_PERCENT, 0, 100)); snapshot.setOccupiedWeightPercent(readIntConfig( CONFIG_OCCUPIED_WEIGHT_PERCENT, DEFAULT_OCCUPIED_WEIGHT_PERCENT, 0, 100 )); snapshot.setBlockedWeightPercent(readIntConfig( CONFIG_BLOCKED_WEIGHT_PERCENT, DEFAULT_BLOCKED_WEIGHT_PERCENT, 0, 100 )); snapshot.setNonAutoingWeightPercent(readIntConfig( CONFIG_NON_AUTOING_WEIGHT_PERCENT, DEFAULT_NON_AUTOING_WEIGHT_PERCENT, 0, 100 )); snapshot.setRunBlockWeightPercent(readIntConfig( CONFIG_RUN_BLOCK_WEIGHT_PERCENT, DEFAULT_RUN_BLOCK_WEIGHT_PERCENT, 0, 100 )); return snapshot; } private int readIntConfig(String code, int defaultValue, int minValue, int maxValue) { if (configService == null) { return defaultValue; } String value = configService.getConfigValue(code, String.valueOf(defaultValue)); try { if (value == null || value.trim().isEmpty()) { return defaultValue; } int parsedValue = Integer.parseInt(value.trim()); if (parsedValue < minValue || parsedValue > maxValue) { return defaultValue; } return parsedValue; } catch (Exception e) { return defaultValue; } } private List selectTasksForAnalysis(List activeTasks, AutoTuneTaskSnapshot taskSnapshot) { List sortedOutboundTasks = sortedOutboundTasks(activeTasks); Set blockedWrkNos = collectBlockedWrkNos(taskSnapshot); LinkedHashMap selected = new LinkedHashMap<>(); for (WrkMast task : sortedOutboundTasks) { Integer wrkNo = task.getWrkNo(); if (wrkNo != null && blockedWrkNos.contains(wrkNo)) { selected.put(wrkNo, task); } if (selected.size() >= MAX_ANALYZED_TASK_COUNT) { return new ArrayList<>(selected.values()); } } for (WrkMast task : sortedOutboundTasks) { Integer wrkNo = task.getWrkNo(); if (wrkNo != null) { selected.putIfAbsent(wrkNo, task); } if (selected.size() >= MAX_ANALYZED_TASK_COUNT) { break; } } return new ArrayList<>(selected.values()); } private Set collectBlockedWrkNos(AutoTuneTaskSnapshot taskSnapshot) { Set blockedWrkNos = new HashSet<>(); if (taskSnapshot == null || taskSnapshot.getStationLimitBlockedTasks() == null) { return blockedWrkNos; } for (AutoTuneTaskDetailItem blockedTask : taskSnapshot.getStationLimitBlockedTasks()) { if (blockedTask != null && blockedTask.getWrkNo() != null) { blockedWrkNos.add(blockedTask.getWrkNo()); } } return blockedWrkNos; } private List sortedOutboundTasks(List activeTasks) { List sortedTasks = new ArrayList<>(); if (activeTasks == null) { return sortedTasks; } for (WrkMast task : activeTasks) { if (task != null && Objects.equals(task.getIoType(), WrkIoType.OUT.id)) { sortedTasks.add(task); } } sortedTasks.sort(Comparator .comparing(WrkMast::getBatch, Comparator.nullsLast(String::compareTo)) .thenComparing(WrkMast::getBatchSeq, Comparator.nullsLast(Integer::compareTo)) .thenComparing(WrkMast::getWrkNo, Comparator.nullsLast(Integer::compareTo))); return sortedTasks; } private Map buildTraceMap() { Map traceMap = new LinkedHashMap<>(); if (stationTaskTraceRegistry == null) { return traceMap; } List traceSnapshots; try { traceSnapshots = stationTaskTraceRegistry.listPlanningActiveTraceSnapshots(); } catch (Exception e) { return traceMap; } if (traceSnapshots == null) { return traceMap; } for (StationTaskTraceVo traceSnapshot : traceSnapshots) { if (traceSnapshot != null && traceSnapshot.getTaskNo() != null) { traceMap.putIfAbsent(traceSnapshot.getTaskNo(), traceSnapshot); } } return traceMap; } private AutoTuneTaskRouteSampleItem buildRouteSample(WrkMast task, Map traceMap, Map estimatedPathCache) { AutoTuneTaskRouteSampleItem item = baseRouteSample(task); if (!isRunningStationTask(task)) { return buildEstimatedRouteSample(item, task, estimatedPathCache); } StationTaskTraceVo trace = task == null || task.getWrkNo() == null ? null : traceMap.get(task.getWrkNo()); if (trace == null) { return completeRouteSample(item, PATH_SOURCE_MISSING, Collections.emptyList(), "missing station trace for running station task"); } List remainingPath = traceRemainingPath(trace); if (!remainingPath.isEmpty()) { return completeRouteSample(item, PATH_SOURCE_TRACE_PENDING, remainingPath, null); } List issuedPath = firstNonEmptyDistinct( trace == null ? null : trace.getLatestIssuedSegmentPath(), trace == null ? null : trace.getIssuedStationIds() ); if (!issuedPath.isEmpty()) { return completeRouteSample(item, PATH_SOURCE_TRACE_ISSUED, issuedPath, null); } return completeRouteSample(item, PATH_SOURCE_MISSING, Collections.emptyList(), "missing usable station trace path for running station task"); } private List traceRemainingPath(StationTaskTraceVo trace) { if (trace == null) { return Collections.emptyList(); } List pendingPath = distinctPositive(trace.getPendingStationIds()); Integer currentStationId = trace.getCurrentStationId(); if (currentStationId == null || currentStationId <= 0) { return pendingPath; } List remainingPath = new ArrayList<>(); remainingPath.add(currentStationId); Integer previousStationId = currentStationId; for (Integer stationId : pendingPath) { if (Objects.equals(stationId, previousStationId)) { continue; } remainingPath.add(stationId); previousStationId = stationId; } return remainingPath; } private boolean isRunningStationTask(WrkMast task) { return task != null && Objects.equals(task.getWrkSts(), WrkStsType.STATION_RUN.sts); } private AutoTuneTaskRouteSampleItem baseRouteSample(WrkMast task) { AutoTuneTaskRouteSampleItem item = new AutoTuneTaskRouteSampleItem(); if (task == null) { return item; } item.setWrkNo(task.getWrkNo()); item.setWrkSts(task.getWrkSts()); item.setBatch(task.getBatch()); item.setBatchSeq(task.getBatchSeq()); item.setSourceStaNo(task.getSourceStaNo()); item.setTargetStaNo(task.getStaNo()); return item; } private AutoTuneTaskRouteSampleItem buildEstimatedRouteSample(AutoTuneTaskRouteSampleItem item, WrkMast task, Map cache) { if (task == null || task.getSourceStaNo() == null || task.getStaNo() == null) { return completeRouteSample(item, PATH_SOURCE_MISSING, Collections.emptyList(), "missing sourceStaNo or staNo"); } Double pathLenFactor = resolvePathLenFactor(task); String cacheKey = buildEstimatedPathCacheKey(task, pathLenFactor); EstimatedPathResult pathResult = cache.get(cacheKey); if (pathResult == null) { pathResult = estimatePath(task, pathLenFactor); cache.put(cacheKey, pathResult); } if (pathResult.pathStationIds.isEmpty()) { return completeRouteSample(item, PATH_SOURCE_MISSING, Collections.emptyList(), pathResult.pathError); } return completeRouteSample(item, PATH_SOURCE_ESTIMATED, pathResult.pathStationIds, null); } private Double resolvePathLenFactor(WrkMast task) { if (stationOutboundDecisionSupport == null) { return 0.0d; } try { Double pathLenFactor = stationOutboundDecisionSupport.resolveOutboundPathLenFactor(task); return pathLenFactor == null ? 0.0d : pathLenFactor; } catch (Exception e) { return 0.0d; } } private String buildEstimatedPathCacheKey(WrkMast task, Double pathLenFactor) { return task.getWrkNo() + ":" + task.getSourceStaNo() + "->" + task.getStaNo() + ":" + pathLenFactor; } private EstimatedPathResult estimatePath(WrkMast task, Double pathLenFactor) { List navigateNodes; try { navigateNodes = navigateUtils.calcOptimalPathByStationId( task.getSourceStaNo(), task.getStaNo(), task.getWrkNo(), pathLenFactor ); } catch (Exception e) { return EstimatedPathResult.error("path estimate exception: " + e.getClass().getSimpleName()); } List pathStationIds = stationIdsFromNavigateNodes(navigateNodes); if (pathStationIds.isEmpty()) { return EstimatedPathResult.error("path estimate failed"); } return EstimatedPathResult.success(pathStationIds); } private List stationIdsFromNavigateNodes(List navigateNodes) { List stationIds = new ArrayList<>(); if (navigateNodes == null) { return stationIds; } for (NavigateNode navigateNode : navigateNodes) { Integer stationId = stationIdFromNavigateNode(navigateNode); if (stationId != null && stationId > 0) { stationIds.add(stationId); } } return stationIds; } private Integer stationIdFromNavigateNode(NavigateNode navigateNode) { if (navigateNode == null) { return null; } if (navigateNode.getStationId() != null) { return navigateNode.getStationId(); } String nodeValue = navigateNode.getNodeValue(); if (nodeValue == null || nodeValue.trim().isEmpty()) { return null; } try { JSONObject value = JSON.parseObject(nodeValue); return value == null ? null : value.getInteger("stationId"); } catch (Exception e) { return null; } } @SafeVarargs private final List firstNonEmptyDistinct(List... pathCandidates) { if (pathCandidates == null) { return Collections.emptyList(); } for (List pathCandidate : pathCandidates) { List pathStationIds = distinctPositive(pathCandidate); if (!pathStationIds.isEmpty()) { return pathStationIds; } } return Collections.emptyList(); } private List distinctPositive(List source) { List result = new ArrayList<>(); if (source == null) { return result; } Integer previousStationId = null; for (Integer stationId : source) { if (stationId == null || stationId <= 0 || Objects.equals(stationId, previousStationId)) { continue; } result.add(stationId); previousStationId = stationId; } return result; } private AutoTuneTaskRouteSampleItem completeRouteSample(AutoTuneTaskRouteSampleItem item, String pathSource, List pathStationIds, String pathError) { List safePathStationIds = pathStationIds == null ? Collections.emptyList() : new ArrayList<>(pathStationIds); item.setPathSource(pathSource); item.setPathStationIds(safePathStationIds); item.setPathLength(safePathStationIds.size()); item.setPathError(pathError); return item; } private int countByPathSource(List routeSamples, String pathSource) { int count = 0; for (AutoTuneTaskRouteSampleItem routeSample : routeSamples) { if (routeSample != null && pathSource.equals(routeSample.getPathSource())) { count++; } } return count; } private Map buildRuntimeMap(List stationRuntimeSnapshot) { Map runtimeMap = new LinkedHashMap<>(); if (stationRuntimeSnapshot == null) { return runtimeMap; } for (AutoTuneStationRuntimeItem runtimeItem : stationRuntimeSnapshot) { if (runtimeItem != null && runtimeItem.getStationId() != null) { runtimeMap.putIfAbsent(runtimeItem.getStationId(), runtimeItem); } } return runtimeMap; } private List buildHotPathSegments(List routeSamples, Map runtimeMap, AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { LinkedHashMap aggregateMap = new LinkedHashMap<>(); if (routeSamples == null) { return Collections.emptyList(); } int validRouteSampleCount = 0; for (AutoTuneTaskRouteSampleItem routeSample : routeSamples) { if (!hasValidPath(routeSample)) { continue; } validRouteSampleCount++; Set sampleSegmentKeys = new HashSet<>(); List> routeSegments = splitRouteSegments(routeSample.getPathStationIds(), ruleSnapshot); for (List routeSegment : routeSegments) { String segmentKey = buildSegmentKey(routeSegment); if (segmentKey == null || !sampleSegmentKeys.add(segmentKey)) { continue; } SegmentAggregate aggregate = aggregateMap.computeIfAbsent( segmentKey, key -> new SegmentAggregate(key, routeSegment) ); aggregate.addRouteSample(routeSample); } } List hotPathSegments = new ArrayList<>(); for (SegmentAggregate aggregate : aggregateMap.values()) { hotPathSegments.add(aggregate.toItem(runtimeMap, ruleSnapshot, validRouteSampleCount)); } hotPathSegments.sort(this::compareHotPathSegment); return hotPathSegments; } private boolean hasValidPath(AutoTuneTaskRouteSampleItem routeSample) { return routeSample != null && routeSample.getTargetStaNo() != null && routeSample.getPathStationIds() != null && routeSample.getPathStationIds().size() >= 2 && !PATH_SOURCE_MISSING.equals(routeSample.getPathSource()); } private List> splitRouteSegments(List pathStationIds, AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { if (pathStationIds == null || pathStationIds.isEmpty()) { return Collections.emptyList(); } List safePathStationIds = new ArrayList<>(pathStationIds); int segmentWindowSize = ruleWindowSize(ruleSnapshot); if (safePathStationIds.size() <= segmentWindowSize) { return Collections.singletonList(safePathStationIds); } List> routeSegments = new ArrayList<>(); for (int startIndex = 0; startIndex <= safePathStationIds.size() - segmentWindowSize; startIndex++) { int endIndex = startIndex + segmentWindowSize; routeSegments.add(new ArrayList<>(safePathStationIds.subList(startIndex, endIndex))); } return routeSegments; } private int ruleWindowSize(AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { Integer segmentWindowSize = ruleSnapshot == null ? null : ruleSnapshot.getSegmentWindowSize(); return segmentWindowSize == null || segmentWindowSize < 2 ? DEFAULT_SEGMENT_WINDOW_SIZE : segmentWindowSize; } private String buildSegmentKey(List stationIds) { if (stationIds == null || stationIds.isEmpty()) { return null; } StringBuilder segmentKey = new StringBuilder(); for (Integer stationId : stationIds) { if (stationId == null) { return null; } if (segmentKey.length() > 0) { segmentKey.append("-"); } segmentKey.append(stationId); } return segmentKey.toString(); } private int compareHotPathSegment(AutoTuneHotPathSegmentItem left, AutoTuneHotPathSegmentItem right) { int pressureCompare = Integer.compare( pressureRank(right.getPressureLevel()), pressureRank(left.getPressureLevel()) ); if (pressureCompare != 0) { return pressureCompare; } int scoreCompare = Integer.compare(nullSafe(right.getPressureScore()), nullSafe(left.getPressureScore())); if (scoreCompare != 0) { return scoreCompare; } int passCompare = Integer.compare(nullSafe(right.getPassTaskCount()), nullSafe(left.getPassTaskCount())); if (passCompare != 0) { return passCompare; } int runBlockCompare = Integer.compare(nullSafe(right.getRunBlockCount()), nullSafe(left.getRunBlockCount())); if (runBlockCompare != 0) { return runBlockCompare; } int taskHoldingCompare = Integer.compare( nullSafe(right.getTaskHoldingCount()), nullSafe(left.getTaskHoldingCount()) ); if (taskHoldingCompare != 0) { return taskHoldingCompare; } int loadingCompare = Integer.compare(nullSafe(right.getLoadingCount()), nullSafe(left.getLoadingCount())); if (loadingCompare != 0) { return loadingCompare; } return nullSafeString(left.getSegmentKey()).compareTo(nullSafeString(right.getSegmentKey())); } private int pressureRank(String pressureLevel) { if (PRESSURE_HIGH.equals(pressureLevel)) { return 3; } if (PRESSURE_MEDIUM.equals(pressureLevel)) { return 2; } return 1; } private int nullSafe(Integer value) { return value == null ? 0 : value; } private String nullSafeString(String value) { return value == null ? "" : value; } private List topHotPathSegments(List allHotPathSegments) { if (allHotPathSegments == null || allHotPathSegments.isEmpty()) { return Collections.emptyList(); } int toIndex = Math.min(MAX_HOT_PATH_SEGMENT_COUNT, allHotPathSegments.size()); return new ArrayList<>(allHotPathSegments.subList(0, toIndex)); } private List buildTargetStationRoutePressure( List routeSamples, List allHotPathSegments, AutoTuneTaskSnapshot taskSnapshot, AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { LinkedHashMap targetAggregateMap = new LinkedHashMap<>(); Set blockedWrkNos = collectBlockedWrkNos(taskSnapshot); if (routeSamples != null) { for (AutoTuneTaskRouteSampleItem routeSample : routeSamples) { if (!hasValidPath(routeSample)) { continue; } Integer targetStaNo = routeSample.getTargetStaNo(); TargetPressureAggregate aggregate = targetAggregateMap.computeIfAbsent( targetStaNo, TargetPressureAggregate::new ); aggregate.routeTaskCount++; if (routeSample.getWrkNo() != null && blockedWrkNos.contains(routeSample.getWrkNo())) { aggregate.blockedTaskCount++; } } } if (allHotPathSegments != null) { for (AutoTuneHotPathSegmentItem hotPathSegment : allHotPathSegments) { if (hotPathSegment == null || hotPathSegment.getRelatedTargetStations() == null) { continue; } for (Integer targetStationId : hotPathSegment.getRelatedTargetStations()) { TargetPressureAggregate aggregate = targetAggregateMap.get(targetStationId); if (aggregate != null) { aggregate.relatedHotSegments.add(hotPathSegment); } } } } List targetPressureItems = new ArrayList<>(); for (TargetPressureAggregate aggregate : targetAggregateMap.values()) { targetPressureItems.add(aggregate.toItem(ruleSnapshot)); } targetPressureItems.sort(Comparator.comparing( AutoTuneTargetStationRoutePressureItem::getTargetStationId, Comparator.nullsLast(Integer::compareTo) )); return targetPressureItems; } private int calculateSegmentPressureScore(int passTaskCount, int taskHoldingCount, int loadingCount, int runBlockCount, int nonAutoingCount, int segmentStationCount, int validRouteSampleCount, AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { double passRatio = ratio(passTaskCount, validRouteSampleCount); double occupiedRatio = ratio(taskHoldingCount + loadingCount, segmentStationCount); double nonAutoingRatio = ratio(nonAutoingCount, segmentStationCount); double runBlockRatio = ratio(runBlockCount, segmentStationCount); double score = passRatio * nullSafe(ruleSnapshot.getPassWeightPercent()) + occupiedRatio * nullSafe(ruleSnapshot.getOccupiedWeightPercent()) + nonAutoingRatio * nullSafe(ruleSnapshot.getNonAutoingWeightPercent()) + runBlockRatio * nullSafe(ruleSnapshot.getRunBlockWeightPercent()); return clampPercent((int) Math.round(score)); } private String calculatePressureLevel(int pressureScore, AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { if (pressureScore >= nullSafe(ruleSnapshot.getHighPercent())) { return PRESSURE_HIGH; } if (pressureScore >= nullSafe(ruleSnapshot.getMediumPercent())) { return PRESSURE_MEDIUM; } return PRESSURE_LOW; } private Map segmentPressureFactors(int passTaskCount, int taskHoldingCount, int loadingCount, int runBlockCount, int nonAutoingCount, int segmentStationCount, int validRouteSampleCount) { Map factors = new LinkedHashMap<>(); factors.put("passRatio", percentValue(passTaskCount, validRouteSampleCount)); factors.put("occupiedRatio", percentValue(taskHoldingCount + loadingCount, segmentStationCount)); factors.put("nonAutoingRatio", percentValue(nonAutoingCount, segmentStationCount)); factors.put("runBlockRatio", percentValue(runBlockCount, segmentStationCount)); return factors; } private double ratio(int numerator, int denominator) { if (numerator <= 0 || denominator <= 0) { return 0.0d; } return Math.min(1.0d, (double) numerator / (double) denominator); } private Integer percentValue(int numerator, int denominator) { return clampPercent((int) Math.round(ratio(numerator, denominator) * 100.0d)); } private int clampPercent(int value) { if (value < 0) { return 0; } return Math.min(value, 100); } private String snapshotConfidence(int analyzedTaskCount, int pathErrorCount) { if (analyzedTaskCount >= 10 && pathErrorCount == 0) { return CONFIDENCE_HIGH; } if (analyzedTaskCount >= 3 && pathErrorCount < analyzedTaskCount) { return CONFIDENCE_MEDIUM; } return CONFIDENCE_LOW; } private String targetConfidence(int routeTaskCount) { if (routeTaskCount >= 10) { return CONFIDENCE_HIGH; } if (routeTaskCount >= 3) { return CONFIDENCE_MEDIUM; } return CONFIDENCE_LOW; } private static class EstimatedPathResult { private final List pathStationIds; private final String pathError; private EstimatedPathResult(List pathStationIds, String pathError) { this.pathStationIds = pathStationIds; this.pathError = pathError; } private static EstimatedPathResult success(List pathStationIds) { return new EstimatedPathResult(new ArrayList<>(pathStationIds), null); } private static EstimatedPathResult error(String pathError) { return new EstimatedPathResult(Collections.emptyList(), pathError); } } private class SegmentAggregate { private final String segmentKey; private final List stationIds; private final Set relatedTargetStations = new LinkedHashSet<>(); private final Set sampleWrkNos = new LinkedHashSet<>(); private int passTaskCount; private SegmentAggregate(String segmentKey, List stationIds) { this.segmentKey = segmentKey; this.stationIds = new ArrayList<>(stationIds); } private void addRouteSample(AutoTuneTaskRouteSampleItem routeSample) { passTaskCount++; if (routeSample.getTargetStaNo() != null) { relatedTargetStations.add(routeSample.getTargetStaNo()); } if (routeSample.getWrkNo() != null) { sampleWrkNos.add(routeSample.getWrkNo()); } } private AutoTuneHotPathSegmentItem toItem(Map runtimeMap, AutoTuneRoutePressureRuleSnapshot ruleSnapshot, int validRouteSampleCount) { int loadingCount = 0; int taskHoldingCount = 0; int runBlockCount = 0; int nonAutoingCount = 0; for (Integer stationId : stationIds) { AutoTuneStationRuntimeItem runtimeItem = runtimeMap.get(stationId); if (runtimeItem == null) { continue; } if (Objects.equals(runtimeItem.getLoading(), 1)) { loadingCount++; } if (runtimeItem.getTaskNo() != null && runtimeItem.getTaskNo() > 0) { taskHoldingCount++; } if (Objects.equals(runtimeItem.getRunBlock(), 1)) { runBlockCount++; } if (!Objects.equals(runtimeItem.getAutoing(), 1)) { nonAutoingCount++; } } int pressureScore = calculateSegmentPressureScore( passTaskCount, taskHoldingCount, loadingCount, runBlockCount, nonAutoingCount, stationIds.size(), validRouteSampleCount, ruleSnapshot ); AutoTuneHotPathSegmentItem item = new AutoTuneHotPathSegmentItem(); item.setSegmentKey(segmentKey); item.setStationIds(new ArrayList<>(stationIds)); item.setPassTaskCount(passTaskCount); item.setLoadingCount(loadingCount); item.setTaskHoldingCount(taskHoldingCount); item.setRunBlockCount(runBlockCount); item.setNonAutoingCount(nonAutoingCount); item.setPressureScore(pressureScore); item.setPressureLevel(calculatePressureLevel(pressureScore, ruleSnapshot)); item.setPressureFactors(segmentPressureFactors( passTaskCount, taskHoldingCount, loadingCount, runBlockCount, nonAutoingCount, stationIds.size(), validRouteSampleCount )); item.setRelatedTargetStations(new ArrayList<>(relatedTargetStations)); item.setSampleWrkNos(new ArrayList<>(sampleWrkNos)); return item; } } private class TargetPressureAggregate { private final Integer targetStationId; private final List relatedHotSegments = new ArrayList<>(); private int blockedTaskCount; private int routeTaskCount; private TargetPressureAggregate(Integer targetStationId) { this.targetStationId = targetStationId; } private AutoTuneTargetStationRoutePressureItem toItem(AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { int pressureScore = targetPressureScore(ruleSnapshot); String pressureLevel = calculatePressureLevel(pressureScore, ruleSnapshot); String heuristicDirection = heuristicDirection(pressureLevel); String evidenceText = evidenceText(pressureLevel, pressureScore, heuristicDirection); AutoTuneTargetStationRoutePressureItem item = new AutoTuneTargetStationRoutePressureItem(); item.setTargetStationId(targetStationId); item.setBlockedTaskCount(blockedTaskCount); item.setRouteTaskCount(routeTaskCount); item.setMainHotSegments(mainHotSegmentKeys()); item.setPressureLevel(pressureLevel); item.setPressureScore(pressureScore); item.setConfidence(targetConfidence(routeTaskCount)); item.setPressureFactors(targetPressureFactors(ruleSnapshot)); item.setHeuristicDirection(heuristicDirection); item.setRecommendedDirection(heuristicDirection); item.setRecommendedTargets(Collections.singletonList("station/" + targetStationId + "/outTaskLimit")); item.setReason(evidenceText); item.setEvidenceText(evidenceText); return item; } private int targetPressureScore(AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { int pressureScore = highestSegmentPressureScore(); int blockedScore = (int) Math.round( ratio(blockedTaskCount, routeTaskCount) * nullSafe(ruleSnapshot.getBlockedWeightPercent()) ); return clampPercent(pressureScore + blockedScore); } private int highestSegmentPressureScore() { int pressureScore = 0; for (AutoTuneHotPathSegmentItem hotPathSegment : relatedHotSegments) { pressureScore = Math.max(pressureScore, nullSafe(hotPathSegment.getPressureScore())); } return pressureScore; } private Map targetPressureFactors(AutoTuneRoutePressureRuleSnapshot ruleSnapshot) { Map factors = new LinkedHashMap<>(); factors.put("blockedRatio", percentValue(blockedTaskCount, routeTaskCount)); factors.put("highestSegmentScore", highestSegmentPressureScore()); factors.put("blockedScore", (int) Math.round( ratio(blockedTaskCount, routeTaskCount) * nullSafe(ruleSnapshot.getBlockedWeightPercent()) )); return factors; } private List mainHotSegmentKeys() { List segmentKeys = new ArrayList<>(); for (AutoTuneHotPathSegmentItem hotPathSegment : relatedHotSegments) { if (hotPathSegment.getSegmentKey() != null) { segmentKeys.add(hotPathSegment.getSegmentKey()); } if (segmentKeys.size() >= MAX_TARGET_MAIN_SEGMENT_COUNT) { break; } } return segmentKeys; } private String heuristicDirection(String pressureLevel) { if (PRESSURE_HIGH.equals(pressureLevel)) { return DIRECTION_DECREASE_CANDIDATE; } if (blockedTaskCount > 0) { return DIRECTION_INCREASE_CANDIDATE; } return DIRECTION_OBSERVE; } private String evidenceText(String pressureLevel, int pressureScore, String heuristicDirection) { return "目标站" + targetStationId + "路径事实:pressure=" + pressureLevel + ",score=" + pressureScore + ",heuristicDirection=" + heuristicDirection + ",blockedTaskCount=" + blockedTaskCount + ",routeTaskCount=" + routeTaskCount + ",mainHotSegments=" + mainHotSegmentKeys() + "。"; } } }