#
Junjie
1 天以前 2bba23eb20021363fbc70535a8dd09e3314ae0d5
#
4个文件已修改
525 ■■■■■ 已修改文件
src/main/java/com/zy/asrs/service/impl/StationCycleCapacityServiceImpl.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/enums/RedisKeyType.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/core/utils/StationOperateProcessUtils.java 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/application.yml 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/asrs/service/impl/StationCycleCapacityServiceImpl.java
@@ -12,8 +12,10 @@
import com.zy.asrs.service.DeviceConfigService;
import com.zy.asrs.service.StationCycleCapacityService;
import com.zy.common.model.NavigateNode;
import com.zy.common.utils.RedisUtil;
import com.zy.common.utils.NavigateSolution;
import com.zy.core.cache.SlaveConnection;
import com.zy.core.enums.RedisKeyType;
import com.zy.core.enums.SlaveType;
import com.zy.core.model.StationObjModel;
import com.zy.core.model.protocol.StationProtocol;
@@ -37,6 +39,7 @@
@Service("stationCycleCapacityService")
@Slf4j
public class StationCycleCapacityServiceImpl implements StationCycleCapacityService {
    private static final long LOOP_LOAD_RESERVE_EXPIRE_MILLIS = 120_000L;
    @Autowired
    private BasMapService basMapService;
@@ -44,6 +47,8 @@
    private DeviceConfigService deviceConfigService;
    @Autowired
    private BasDevpService basDevpService;
    @Autowired
    private RedisUtil redisUtil;
    private final AtomicReference<StationCycleCapacityVo> snapshotRef = new AtomicReference<>(new StationCycleCapacityVo());
@@ -92,6 +97,7 @@
        int loopNo = 1;
        int totalStationCount = 0;
        int taskStationCount = 0;
        Set<Integer> actualWorkNoSet = new HashSet<>();
        for (Set<Integer> scc : sccList) {
            if (!isCycleScc(scc, filteredGraph)) {
@@ -111,6 +117,7 @@
                    if (workNo != null && workNo > 0) {
                        workNoList.add(workNo);
                        currentLoopTaskCount++;
                        actualWorkNoSet.add(workNo);
                    }
                }
@@ -128,6 +135,9 @@
            }
        }
        int reserveTaskCount = mergeReserveTaskCount(loopList, actualWorkNoSet);
        taskStationCount += reserveTaskCount;
        StationCycleCapacityVo vo = new StationCycleCapacityVo();
        vo.setLoopList(loopList);
        vo.setLoopCount(loopList.size());
@@ -138,6 +148,153 @@
        return vo;
    }
    private int mergeReserveTaskCount(List<StationCycleLoopVo> loopList, Set<Integer> actualWorkNoSet) {
        if (loopList == null || loopList.isEmpty()) {
            return 0;
        }
        Map<Object, Object> reserveMap = redisUtil.hmget(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key);
        if (reserveMap == null || reserveMap.isEmpty()) {
            return 0;
        }
        Map<Integer, StationCycleLoopVo> loopMap = new HashMap<>();
        Map<Integer, StationCycleLoopVo> stationLoopMap = new HashMap<>();
        for (StationCycleLoopVo loopVo : loopList) {
            if (loopVo != null && loopVo.getLoopNo() != null) {
                loopMap.put(loopVo.getLoopNo(), loopVo);
            }
            if (loopVo == null || loopVo.getStationIdList() == null) {
                continue;
            }
            for (Integer stationId : loopVo.getStationIdList()) {
                if (stationId != null) {
                    stationLoopMap.put(stationId, loopVo);
                }
            }
        }
        long now = System.currentTimeMillis();
        int mergedCount = 0;
        List<Object> removeFieldList = new ArrayList<>();
        for (Map.Entry<Object, Object> entry : reserveMap.entrySet()) {
            ReserveRecord record = parseReserveRecord(entry.getKey(), entry.getValue());
            if (record == null) {
                removeFieldList.add(entry.getKey());
                continue;
            }
            if (actualWorkNoSet.contains(record.wrkNo)) {
                removeFieldList.add(entry.getKey());
                continue;
            }
            if (record.createTime <= 0 || now - record.createTime > LOOP_LOAD_RESERVE_EXPIRE_MILLIS) {
                removeFieldList.add(entry.getKey());
                continue;
            }
            StationCycleLoopVo loopVo = loopMap.get(record.loopNo);
            if (loopVo == null && record.hitStationId != null) {
                loopVo = stationLoopMap.get(record.hitStationId);
            }
            if (loopVo == null) {
                removeFieldList.add(entry.getKey());
                continue;
            }
            List<Integer> workNoList = loopVo.getWorkNoList();
            if (workNoList == null) {
                workNoList = new ArrayList<>();
                loopVo.setWorkNoList(workNoList);
            }
            if (workNoList.contains(record.wrkNo)) {
                continue;
            }
            workNoList.add(record.wrkNo);
            Collections.sort(workNoList);
            int mergedTaskCount = toNonNegative(loopVo.getTaskCount()) + 1;
            loopVo.setTaskCount(mergedTaskCount);
            loopVo.setCurrentLoad(calcCurrentLoad(mergedTaskCount, toNonNegative(loopVo.getStationCount())));
            mergedCount++;
        }
        if (!removeFieldList.isEmpty()) {
            redisUtil.hdel(RedisKeyType.STATION_CYCLE_LOAD_RESERVE.key, removeFieldList.toArray());
        }
        return mergedCount;
    }
    private ReserveRecord parseReserveRecord(Object fieldObj, Object valueObj) {
        if (fieldObj == null || valueObj == null) {
            return null;
        }
        Integer fieldWrkNo = parseInteger(String.valueOf(fieldObj));
        if (fieldWrkNo == null || fieldWrkNo <= 0) {
            return null;
        }
        JSONObject jsonObject;
        try {
            jsonObject = JSON.parseObject(String.valueOf(valueObj));
        } catch (Exception e) {
            return null;
        }
        if (jsonObject == null) {
            return null;
        }
        Integer wrkNo = jsonObject.getInteger("wrkNo");
        Integer loopNo = jsonObject.getInteger("loopNo");
        Integer hitStationId = jsonObject.getInteger("hitStationId");
        Long createTime = jsonObject.getLong("createTime");
        if (wrkNo == null || wrkNo <= 0) {
            wrkNo = fieldWrkNo;
        }
        if ((loopNo == null || loopNo <= 0) && (hitStationId == null || hitStationId <= 0)) {
            return null;
        }
        if (createTime == null || createTime <= 0) {
            return null;
        }
        ReserveRecord record = new ReserveRecord();
        record.wrkNo = wrkNo;
        record.loopNo = loopNo;
        record.hitStationId = hitStationId;
        record.createTime = createTime;
        return record;
    }
    private Integer parseInteger(String value) {
        if (value == null || value.trim().isEmpty()) {
            return null;
        }
        try {
            return Integer.parseInt(value.trim());
        } catch (Exception e) {
            return null;
        }
    }
    private int toNonNegative(Integer value) {
        if (value == null || value < 0) {
            return 0;
        }
        return value;
    }
    private static class ReserveRecord {
        private Integer wrkNo;
        private Integer loopNo;
        private Integer hitStationId;
        private Long createTime;
    }
    private double calcCurrentLoad(int taskCount, int stationCount) {
        if (stationCount <= 0 || taskCount <= 0) {
            return 0.0;
src/main/java/com/zy/core/enums/RedisKeyType.java
@@ -60,6 +60,7 @@
    CRN_OUT_TASK_COMPLETE_STATION_INFO("crn_out_task_complete_station_info_"),
    WATCH_CIRCLE_STATION_("watch_circle_station_"),
    STATION_CYCLE_LOAD_RESERVE("station_cycle_load_reserve"),
    CURRENT_CIRCLE_TASK_CRN_NO("current_circle_task_crn_no_"),
    ASYNC_WMS_IN_TASK_REQUEST("async_wms_in_task_request_"),
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,322 @@
        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;
                }
            }
        }
        if (config.stationMaxTaskCount > 0 && currentStationTaskCount >= config.stationMaxTaskCount) {
            News.warn("输送站点任务数量达到上限,已停止站点任务下发。当前任务数={},上限={}", currentStationTaskCount, config.stationMaxTaskCount);
            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);
        String stationMaxTaskCountValue = getConfigValue(systemConfigMap, "stationMaxTaskCountLimit");
        if (isBlank(stationMaxTaskCountValue)) {
            stationMaxTaskCountValue = getConfigValue(systemConfigMap, "stationMaxTaskCount");
        }
        if (isBlank(stationMaxTaskCountValue)) {
            stationMaxTaskCountValue = getConfigValue(systemConfigMap, "conveyorStationTaskLimit");
        }
        config.stationMaxTaskCount = parseInt(stationMaxTaskCountValue, config.stationMaxTaskCount);
        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;
        // 站点最大任务数量上限,0表示不限制
        private int stationMaxTaskCount = 30;
    }
    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;
        }
    }
}
src/main/resources/application.yml
@@ -1,6 +1,6 @@
# 系统版本信息
app:
  version: 1.0.4.5
  version: 1.0.4.6
  version-type: dev  # prd 或 dev
server: