package com.zy.core.utils.station; import com.alibaba.fastjson.JSONObject; import com.zy.asrs.domain.path.StationPathResolvedPolicy; import com.zy.asrs.domain.vo.StationCycleCapacityVo; import com.zy.asrs.domain.vo.StationCycleLoopVo; import com.zy.asrs.entity.BasDevp; import com.zy.asrs.entity.WrkMast; import com.zy.asrs.service.BasDevpService; import com.zy.asrs.service.StationCycleCapacityService; import com.zy.asrs.service.StationPathPolicyService; import com.zy.common.model.NavigateNode; import com.zy.common.utils.NavigateUtils; import com.zy.common.utils.RedisUtil; import com.zy.core.News; import com.zy.core.cache.SlaveConnection; import com.zy.core.enums.RedisKeyType; import com.zy.core.enums.SlaveType; import com.zy.core.model.protocol.StationProtocol; import com.zy.core.thread.StationThread; import com.zy.core.utils.station.model.DispatchLimitConfig; import com.zy.core.utils.station.model.LoadGuardState; import com.zy.core.utils.station.model.LoopHitResult; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; import java.util.Locale; import java.util.Map; @Component public class StationDispatchLoadSupport { private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120; @Autowired private RedisUtil redisUtil; @Autowired private BasDevpService basDevpService; @Autowired private StationCycleCapacityService stationCycleCapacityService; @Autowired private StationPathPolicyService stationPathPolicyService; @Autowired private NavigateUtils navigateUtils; public int countCurrentStationTask() { int currentStationTaskCount = 0; List basDevps = basDevpService.list(); for (BasDevp basDevp : basDevps) { StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo()); if (stationThread == null) { continue; } for (StationProtocol stationProtocol : stationThread.getStatus()) { if (stationProtocol.getTaskNo() > 0) { currentStationTaskCount++; } } } return currentStationTaskCount; } public boolean isDispatchBlocked(DispatchLimitConfig config, int currentStationTaskCount, LoadGuardState loadGuardState, boolean needReserveLoopLoad) { if (config != null && config.isLoopModeEnable()) { double currentLoad = loadGuardState.currentLoad(); if (currentLoad >= config.getCircleMaxLoadLimit()) { News.warn("当前承载量达到上限,已停止站点任务下发。当前承载量={},上限={}", formatPercent(currentLoad), formatPercent(config.getCircleMaxLoadLimit())); return true; } if (needReserveLoopLoad) { double reserveLoad = loadGuardState.loadAfterReserve(); if (reserveLoad >= config.getCircleMaxLoadLimit()) { News.warn("预占后承载量达到上限,已停止站点任务下发。预占后承载量={},上限={}", formatPercent(reserveLoad), formatPercent(config.getCircleMaxLoadLimit())); return true; } } } return false; } public LoadGuardState buildLoadGuardState(DispatchLimitConfig config) { LoadGuardState state = new LoadGuardState(); if (config == null || !config.isLoopModeEnable()) { return state; } StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot(); if (capacityVo == null) { return state; } Integer occupiedStationCount = capacityVo.getOccupiedStationCount(); state.setTotalStationCount(toNonNegative(capacityVo.getTotalStationCount())); state.setProjectedTaskStationCount(toNonNegative(occupiedStationCount != null ? occupiedStationCount : capacityVo.getTaskStationCount())); List loopList = capacityVo.getLoopList(); if (loopList != null) { for (StationCycleLoopVo loopVo : loopList) { if (loopVo == null || loopVo.getStationIdList() == null) { continue; } Integer loopNo = loopVo.getLoopNo(); for (Integer stationId : loopVo.getStationIdList()) { if (stationId != null && loopNo != null) { state.putStationLoopNo(stationId, loopNo); } } } } return state; } public LoopHitResult findPathLoopHit(DispatchLimitConfig config, Integer sourceStationId, Integer targetStationId, LoadGuardState loadGuardState) { return findPathLoopHit(config, sourceStationId, targetStationId, loadGuardState, null, null); } public LoopHitResult findPathLoopHit(DispatchLimitConfig config, Integer sourceStationId, Integer targetStationId, LoadGuardState loadGuardState, WrkMast wrkMast, Double pathLenFactor) { if (config == null || !config.isLoopModeEnable()) { return LoopHitResult.noHit(); } if (sourceStationId == null || targetStationId == null) { return LoopHitResult.noHit(); } if (loadGuardState == null || loadGuardState.getStationLoopNoMap().isEmpty()) { return LoopHitResult.noHit(); } try { List nodes = wrkMast == null ? navigateUtils.calcByStationId(sourceStationId, targetStationId) : calcOutboundNavigatePath(wrkMast, sourceStationId, targetStationId, pathLenFactor); if (nodes == null || nodes.isEmpty()) { return LoopHitResult.noHit(); } for (NavigateNode node : nodes) { Integer stationId = getStationIdFromNode(node); if (stationId == null) { continue; } Integer loopNo = loadGuardState.getStationLoopNoMap().get(stationId); if (loopNo != null) { return new LoopHitResult(true, loopNo, stationId); } } } catch (Exception ignore) { return LoopHitResult.noHit(); } return LoopHitResult.noHit(); } public void saveLoopLoadReserve(Integer wrkNo, LoopHitResult loopHitResult) { if (wrkNo == null || wrkNo <= 0 || loopHitResult == null || !loopHitResult.isThroughLoop()) { return; } JSONObject reserveJson = new JSONObject(); reserveJson.put("wrkNo", wrkNo); reserveJson.put("loopNo", loopHitResult.getLoopNo()); reserveJson.put("hitStationId", loopHitResult.getHitStationId()); reserveJson.put("createTime", System.currentTimeMillis()); redisUtil.hset(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, String.valueOf(wrkNo), reserveJson.toJSONString()); redisUtil.expire(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, LOOP_LOAD_RESERVE_EXPIRE_SECONDS); } public DispatchLimitConfig getDispatchLimitConfig(Integer startStationId, Integer endStationId) { DispatchLimitConfig config = new DispatchLimitConfig(); Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key); if (systemConfigMapObj instanceof Map) { Map systemConfigMap = (Map) systemConfigMapObj; config.setCircleMaxLoadLimit(parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.getCircleMaxLoadLimit())); String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable"); if (isBlank(loopModeValue)) { loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable"); } if (isBlank(loopModeValue)) { loopModeValue = getConfigValue(systemConfigMap, "isCircleMode"); } config.setLoopModeEnable(parseBoolean(loopModeValue, config.isLoopModeEnable())); } if (stationPathPolicyService != null && startStationId != null && endStationId != null) { try { StationPathResolvedPolicy resolvedPolicy = stationPathPolicyService.resolvePolicy(startStationId, endStationId); if (resolvedPolicy != null && resolvedPolicy.getProfileConfig() != null) { config.setCircleMaxLoadLimit(parseLoadLimit(String.valueOf(resolvedPolicy.getProfileConfig().getCircleMaxLoadLimit()), config.getCircleMaxLoadLimit())); } } catch (Exception ignore) { } } return config; } private List calcOutboundNavigatePath(WrkMast wrkMast, Integer sourceStationId, Integer targetStationId, Double pathLenFactor) { Double normalizedFactor = normalizePathLenFactor(pathLenFactor); Integer currentTaskNo = wrkMast == null ? null : wrkMast.getWrkNo(); if (currentTaskNo == null) { return navigateUtils.calcByStationId(sourceStationId, targetStationId, normalizedFactor); } return navigateUtils.calcByStationId(sourceStationId, targetStationId, currentTaskNo, normalizedFactor); } private Integer getStationIdFromNode(NavigateNode node) { if (node == null || isBlank(node.getNodeValue())) { return null; } try { JSONObject value = JSONObject.parseObject(node.getNodeValue()); return value == null ? null : value.getInteger("stationId"); } catch (Exception ignore) { return null; } } private int toNonNegative(Integer value) { if (value == null || value < 0) { return 0; } return value; } private Double normalizePathLenFactor(Double pathLenFactor) { if (pathLenFactor == null || pathLenFactor < 0.0d) { return 0.0d; } if (pathLenFactor > 1.0d) { return 1.0d; } return pathLenFactor; } private String getConfigValue(Map configMap, String key) { Object value = configMap.get(key); return value == null ? null : String.valueOf(value).trim(); } private boolean parseBoolean(String value, boolean defaultValue) { if (isBlank(value)) { return defaultValue; } String lowValue = value.toLowerCase(Locale.ROOT); if ("y".equals(lowValue) || "yes".equals(lowValue) || "true".equals(lowValue) || "1".equals(lowValue) || "on".equals(lowValue)) { return true; } if ("n".equals(lowValue) || "no".equals(lowValue) || "false".equals(lowValue) || "0".equals(lowValue) || "off".equals(lowValue)) { return false; } return defaultValue; } private double parseLoadLimit(String value, double defaultValue) { if (isBlank(value)) { return defaultValue; } try { String normalized = value.replace("%", "").trim(); double parsed = Double.parseDouble(normalized); if (parsed > 1.0) { parsed = parsed / 100.0; } if (parsed < 0.0) { return 0.0; } if (parsed > 1.0) { return 1.0; } return parsed; } catch (Exception ignore) { return defaultValue; } } private String formatPercent(double value) { return String.format(Locale.ROOT, "%.1f%%", value * 100.0); } private boolean isBlank(String value) { return value == null || value.trim().isEmpty(); } }