#
Junjie
昨天 53332ecb0edc651bae91f1e7a74a795d76d87cb2
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java
@@ -7,6 +7,8 @@
import com.core.common.Cools;
import com.core.exception.CoolException;
import com.zy.asrs.domain.enums.NotifyMsgType;
import com.zy.asrs.domain.vo.StationCycleCapacityVo;
import com.zy.asrs.domain.vo.StationCycleLoopVo;
import com.zy.asrs.entity.*;
import com.zy.asrs.service.*;
import com.zy.asrs.utils.NotifyUtils;
@@ -32,6 +34,7 @@
@Component
public class StationOperateProcessUtils {
    private static final int LOOP_LOAD_RESERVE_EXPIRE_SECONDS = 120;
    @Autowired
    private BasDevpService basDevpService;
@@ -51,10 +54,16 @@
    private NavigateUtils navigateUtils;
    @Autowired
    private BasStationService basStationService;
    @Autowired
    private StationCycleCapacityService stationCycleCapacityService;
    //执行输送站点入库任务
    public synchronized void stationInExecute() {
        try {
            DispatchLimitConfig limitConfig = getDispatchLimitConfig();
            int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()};
            LoadGuardState loadGuardState = buildLoadGuardState(limitConfig);
            List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<>());
            for (BasDevp basDevp : basDevps) {
                StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getDevpNo());
@@ -109,6 +118,12 @@
                            continue;
                        }
                        LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), targetStationId, loadGuardState);
                        if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
                            return;
                        }
                        StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationId, targetStationId, 0);
                        if (command == null) {
                            News.taskInfo(wrkMast.getWrkNo(), "{}工作,获取输送线命令失败", wrkMast.getWrkNo());
@@ -124,6 +139,8 @@
                            MessageQueue.offer(SlaveType.Devp, basDevp.getDevpNo(), new Task(2, command));
                            News.info("输送站点入库命令下发成功,站点号={},工作号={},命令数据={}", stationId, wrkMast.getWrkNo(), JSON.toJSONString(command));
                            redisUtil.set(RedisKeyType.STATION_IN_EXECUTE_LIMIT.key + stationId, "lock", 5);
                            loadGuardState.reserveLoopTask(loopHitResult.getLoopNo());
                            saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
                        }
                    }
                }
@@ -136,6 +153,10 @@
    //执行堆垛机输送站点出库任务
    public synchronized void crnStationOutExecute() {
        try {
            DispatchLimitConfig limitConfig = getDispatchLimitConfig();
            int[] currentStationTaskCountRef = new int[]{countCurrentStationTask()};
            LoadGuardState loadGuardState = buildLoadGuardState(limitConfig);
            List<WrkMast> wrkMasts = wrkMastService.selectList(new EntityWrapper<WrkMast>()
                    .eq("wrk_sts", WrkStsType.OUTBOUND_RUN_COMPLETE.sts)
                    .isNotNull("crn_no")
@@ -188,6 +209,12 @@
                        }
                    }
                    LoopHitResult loopHitResult = findPathLoopHit(limitConfig, stationProtocol.getStationId(), moveStaNo, loadGuardState);
                    if (isDispatchBlocked(limitConfig, currentStationTaskCountRef[0], loadGuardState, loopHitResult.isThroughLoop())) {
                        return;
                    }
                    StationCommand command = stationThread.getCommand(StationCommandType.MOVE, wrkMast.getWrkNo(), stationProtocol.getStationId(), moveStaNo, 0);
                    if (command == null) {
                        News.taskInfo(wrkMast.getWrkNo(), "获取输送线命令失败");
@@ -202,6 +229,9 @@
                        News.info("输送站点出库命令下发成功,站点号={},工作号={},命令数据={}", stationProtocol.getStationId(), wrkMast.getWrkNo(), JSON.toJSONString(command));
                        redisUtil.set(RedisKeyType.STATION_OUT_EXECUTE_LIMIT.key + stationProtocol.getStationId(), "lock", 5);
                        redisUtil.del(RedisKeyType.CRN_OUT_TASK_COMPLETE_STATION_INFO.key + wrkMast.getWrkNo());
                        currentStationTaskCountRef[0]++;
                        loadGuardState.reserveLoopTask(loopHitResult.getLoopNo());
                        saveLoopLoadReserve(wrkMast.getWrkNo(), loopHitResult);
                    }
                }
            }
@@ -495,22 +525,7 @@
    //获取输送线任务数量
    public synchronized int getCurrentStationTaskCount() {
        int currentStationTaskCount = 0;
        List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>());
        for (BasDevp basDevp : basDevps) {
            StationThread stationThread = (StationThread) SlaveConnection.get(SlaveType.Devp, basDevp.getId());
            if (stationThread == null) {
                continue;
            }
            for (StationProtocol stationProtocol : stationThread.getStatus()) {
                if (stationProtocol.getTaskNo() > 0) {
                    currentStationTaskCount++;
                }
            }
        }
        return currentStationTaskCount;
        return countCurrentStationTask();
    }
    // 检测出库排序
@@ -756,4 +771,306 @@
        return seq;
    }
    private int countCurrentStationTask() {
        int currentStationTaskCount = 0;
        List<BasDevp> basDevps = basDevpService.selectList(new EntityWrapper<BasDevp>());
        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;
    }
    private boolean isDispatchBlocked(DispatchLimitConfig config,
                                      int currentStationTaskCount,
                                      LoadGuardState loadGuardState,
                                      boolean needReserveLoopLoad) {
        if (config.loopModeEnable) {
            double currentLoad = loadGuardState.currentLoad();
            if (currentLoad >= config.circleMaxLoadLimit) {
                News.warn("当前承载量达到上限,已停止站点任务下发。当前承载量={},上限={}", formatPercent(currentLoad), formatPercent(config.circleMaxLoadLimit));
                return true;
            }
            if (needReserveLoopLoad) {
                double reserveLoad = loadGuardState.loadAfterReserve();
                if (reserveLoad >= config.circleMaxLoadLimit) {
                    News.warn("预占后承载量达到上限,已停止站点任务下发。预占后承载量={},上限={}", formatPercent(reserveLoad), formatPercent(config.circleMaxLoadLimit));
                    return true;
                }
            }
        }
        return false;
    }
    private LoadGuardState buildLoadGuardState(DispatchLimitConfig config) {
        LoadGuardState state = new LoadGuardState();
        if (!config.loopModeEnable) {
            return state;
        }
        StationCycleCapacityVo capacityVo = stationCycleCapacityService.getLatestSnapshot();
        if (capacityVo == null) {
            return state;
        }
        state.totalStationCount = toNonNegative(capacityVo.getTotalStationCount());
        state.projectedTaskStationCount = toNonNegative(capacityVo.getTaskStationCount());
        List<StationCycleLoopVo> 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) {
                        if (loopNo != null) {
                            state.stationLoopNoMap.put(stationId, loopNo);
                        }
                    }
                }
            }
        }
        return state;
    }
    private LoopHitResult findPathLoopHit(DispatchLimitConfig config,
                                          Integer sourceStationId,
                                          Integer targetStationId,
                                          LoadGuardState loadGuardState) {
        if (!config.loopModeEnable) {
            return LoopHitResult.NO_HIT;
        }
        if (sourceStationId == null || targetStationId == null) {
            return LoopHitResult.NO_HIT;
        }
        if (loadGuardState.stationLoopNoMap.isEmpty()) {
            return LoopHitResult.NO_HIT;
        }
        try {
            List<NavigateNode> nodes = navigateUtils.calcByStationId(sourceStationId, targetStationId);
            if (nodes == null || nodes.isEmpty()) {
                return LoopHitResult.NO_HIT;
            }
            for (NavigateNode node : nodes) {
                Integer stationId = getStationIdFromNode(node);
                if (stationId == null) {
                    continue;
                }
                Integer loopNo = loadGuardState.stationLoopNoMap.get(stationId);
                if (loopNo != null) {
                    return new LoopHitResult(true, loopNo, stationId);
                }
            }
        } catch (Exception e) {
            return LoopHitResult.NO_HIT;
        }
        return LoopHitResult.NO_HIT;
    }
    private Integer getStationIdFromNode(NavigateNode node) {
        if (node == null || isBlank(node.getNodeValue())) {
            return null;
        }
        try {
            JSONObject v = JSONObject.parseObject(node.getNodeValue());
            if (v == null) {
                return null;
            }
            return v.getInteger("stationId");
        } catch (Exception e) {
            return null;
        }
    }
    private int toNonNegative(Integer value) {
        if (value == null || value < 0) {
            return 0;
        }
        return value;
    }
    private 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);
    }
    private DispatchLimitConfig getDispatchLimitConfig() {
        DispatchLimitConfig config = new DispatchLimitConfig();
        Object systemConfigMapObj = redisUtil.get(RedisKeyType.SYSTEM_CONFIG_MAP.key);
        if (!(systemConfigMapObj instanceof Map)) {
            return config;
        }
        Map<?, ?> systemConfigMap = (Map<?, ?>) systemConfigMapObj;
        config.circleMaxLoadLimit = parseLoadLimit(getConfigValue(systemConfigMap, "circleMaxLoadLimit"), config.circleMaxLoadLimit);
        String loopModeValue = getConfigValue(systemConfigMap, "circleLoopModeEnable");
        if (isBlank(loopModeValue)) {
            loopModeValue = getConfigValue(systemConfigMap, "circleModeEnable");
        }
        if (isBlank(loopModeValue)) {
            loopModeValue = getConfigValue(systemConfigMap, "isCircleMode");
        }
        config.loopModeEnable = parseBoolean(loopModeValue, config.loopModeEnable);
        return config;
    }
    private String getConfigValue(Map<?, ?> configMap, String key) {
        Object value = configMap.get(key);
        if (value == null) {
            return null;
        }
        return 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 e) {
            return defaultValue;
        }
    }
    private int parseInt(String value, int defaultValue) {
        if (isBlank(value)) {
            return defaultValue;
        }
        try {
            int parsed = Integer.parseInt(value.trim());
            return parsed < 0 ? defaultValue : parsed;
        } catch (Exception e) {
            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();
    }
    private static class DispatchLimitConfig {
        // 圈最大承载能力,默认80%
        private double circleMaxLoadLimit = 0.8d;
        // 是否启用绕圈模式(仅启用时才生效承载限制)
        private boolean loopModeEnable = false;
    }
    private static class LoadGuardState {
        private int totalStationCount = 0;
        private int projectedTaskStationCount = 0;
        private final Map<Integer, Integer> stationLoopNoMap = new HashMap<>();
        private double currentLoad() {
            return calcLoad(this.projectedTaskStationCount, this.totalStationCount);
        }
        private double loadAfterReserve() {
            return calcLoad(this.projectedTaskStationCount + 1, this.totalStationCount);
        }
        private void reserveLoopTask(Integer loopNo) {
            if (loopNo == null || loopNo <= 0) {
                return;
            }
            if (this.totalStationCount <= 0) {
                return;
            }
            this.projectedTaskStationCount++;
        }
        private double calcLoad(int taskCount, int stationCount) {
            if (stationCount <= 0 || taskCount <= 0) {
                return 0.0;
            }
            double load = (double) taskCount / (double) stationCount;
            if (load < 0.0) {
                return 0.0;
            }
            if (load > 1.0) {
                return 1.0;
            }
            return load;
        }
    }
    private static class LoopHitResult {
        private static final LoopHitResult NO_HIT = new LoopHitResult(false, null, null);
        private final boolean throughLoop;
        private final Integer loopNo;
        private final Integer hitStationId;
        private LoopHitResult(boolean throughLoop, Integer loopNo, Integer hitStationId) {
            this.throughLoop = throughLoop;
            this.loopNo = loopNo;
            this.hitStationId = hitStationId;
        }
        private boolean isThroughLoop() {
            return throughLoop;
        }
        private Integer getLoopNo() {
            return loopNo;
        }
        private Integer getHitStationId() {
            return hitStationId;
        }
    }
}