Junjie
2 天以前 b7a738a16531191b43ea2327acca7d861dfd9f09
#算法增加经过其他出库站点的惩罚系数
1个文件已修改
1个文件已添加
211 ■■■■ 已修改文件
src/main/java/com/zy/common/utils/NavigateUtils.java 180 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/sql/20260313_add_station_path_out_station_config.sql 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/zy/common/utils/NavigateUtils.java
@@ -6,10 +6,14 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.zy.asrs.entity.BasDevp;
import com.zy.asrs.entity.BasStation;
import com.zy.asrs.service.BasDevpService;
import com.zy.asrs.service.BasStationService;
import com.zy.core.News;
import com.zy.core.model.StationObjModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -30,6 +34,11 @@
@Component
public class NavigateUtils {
    private static final String CFG_STATION_PATH_LEN_WEIGHT_PERCENT = "stationPathLenWeightPercent";
    private static final String CFG_STATION_PATH_CONG_WEIGHT_PERCENT = "stationPathCongWeightPercent";
    private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT = "stationPathPassOtherOutStationWeightPercent";
    private static final String CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP = "stationPathPassOtherOutStationForceSkip";
    @Autowired
    private BasStationService basStationService;
@@ -233,10 +242,28 @@
            }
        } catch (Exception ignore) {}
        Set<Integer> outStationIdSet = loadAllOutStationIdSet();
        double lenWeightPercent = 50.0;
        double congWeightPercent = 50.0;
        double passOtherOutStationWeightPercent = 100.0;
        boolean forceSkipPassOtherOutStation = false;
        try {
            ConfigService configService = SpringUtils.getBean(ConfigService.class);
            if (configService != null) {
                lenWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_LEN_WEIGHT_PERCENT, lenWeightPercent);
                congWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_CONG_WEIGHT_PERCENT, congWeightPercent);
                passOtherOutStationWeightPercent = loadDoubleConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_WEIGHT_PERCENT, passOtherOutStationWeightPercent);
                forceSkipPassOtherOutStation = loadBooleanConfig(configService, CFG_STATION_PATH_PASS_OTHER_OUT_STATION_FORCE_SKIP, false);
            }
        } catch (Exception ignore) {}
        List<List<NavigateNode>> candidates = new ArrayList<>();
        List<Integer> lens = new ArrayList<>();
        List<Integer> tasksList = new ArrayList<>();
        List<Double> congs = new ArrayList<>();
        List<Integer> passOtherOutStationCounts = new ArrayList<>();
        int skippedByPassOtherOutStation = 0;
        for (List<NavigateNode> path : allList) {
            if (path == null || path.isEmpty()) {
@@ -266,13 +293,27 @@
                }
            }
            double cong = len <= 0 ? 0.0 : (double) tasks / (double) len;
            int passOtherOutStationCount = countPassOtherOutStations(path, outStationIdSet);
            if (forceSkipPassOtherOutStation && passOtherOutStationCount > 0) {
                skippedByPassOtherOutStation++;
                News.info("[WCS Debug] 站点路径候选已跳过,因经过其他出库站点,startStationId={},endStationId={},passOtherOutStationCount={}",
                        extractStationId(path.get(0)),
                        extractStationId(path.get(path.size() - 1)),
                        passOtherOutStationCount);
                continue;
            }
            candidates.add(path);
            lens.add(len);
            tasksList.add(tasks);
            congs.add(cong);
            passOtherOutStationCounts.add(passOtherOutStationCount);
        }
        if (candidates.isEmpty()) {
            if (forceSkipPassOtherOutStation && skippedByPassOtherOutStation > 0) {
                News.info("[WCS Debug] 所有站点路径候选均因经过其他出库站点被强制跳过");
                return new ArrayList<>();
            }
            return allList.get(0);
        }
@@ -280,59 +321,50 @@
        int maxLen = Integer.MIN_VALUE;
        double minCong = Double.MAX_VALUE;
        double maxCong = -Double.MAX_VALUE;
        int minPassOtherOutStationCount = Integer.MAX_VALUE;
        int maxPassOtherOutStationCount = Integer.MIN_VALUE;
        for (int i = 0; i < candidates.size(); i++) {
            int l = lens.get(i);
            double c = congs.get(i);
            int p = passOtherOutStationCounts.get(i);
            if (l < minLen) minLen = l;
            if (l > maxLen) maxLen = l;
            if (c < minCong) minCong = c;
            if (c > maxCong) maxCong = c;
            if (p < minPassOtherOutStationCount) minPassOtherOutStationCount = p;
            if (p > maxPassOtherOutStationCount) maxPassOtherOutStationCount = p;
        }
        //长度权重百分比
        double lenWeightPercent = 50.0;
        //拥堵权重百分比
        double congWeightPercent = 50.0;
        try {
            ConfigService configService = SpringUtils.getBean(ConfigService.class);
            if (configService != null) {
                Config cfgLen = configService.getOne(new QueryWrapper<Config>().eq("code", "stationPathLenWeightPercent"));
                if (cfgLen != null && cfgLen.getValue() != null) {
                    String v = cfgLen.getValue().trim();
                    if (v.endsWith("%")) v = v.substring(0, v.length() - 1);
                    try { lenWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {}
                }
                Config cfgCong = configService.getOne(new QueryWrapper<Config>().eq("code", "stationPathCongWeightPercent"));
                if (cfgCong != null && cfgCong.getValue() != null) {
                    String v = cfgCong.getValue().trim();
                    if (v.endsWith("%")) v = v.substring(0, v.length() - 1);
                    try { congWeightPercent = Double.parseDouble(v); } catch (Exception ignore) {}
                }
            }
        } catch (Exception ignore) {}
        double weightSum = lenWeightPercent + congWeightPercent;
        double weightSum = lenWeightPercent + congWeightPercent + passOtherOutStationWeightPercent;
        double lenW = weightSum <= 0 ? 0.5 : lenWeightPercent / weightSum;
        double congW = weightSum <= 0 ? 0.5 : congWeightPercent / weightSum;
        double passOtherOutStationW = weightSum <= 0 ? 0.0 : passOtherOutStationWeightPercent / weightSum;
        List<NavigateNode> best = null;
        double bestCost = Double.MAX_VALUE;
        int bestPassOtherOutStationCount = Integer.MAX_VALUE;
        int bestTasks = Integer.MAX_VALUE;
        int bestLen = Integer.MAX_VALUE;
        for (int i = 0; i < candidates.size(); i++) {
            int l = lens.get(i);
            int t = tasksList.get(i);
            double c = congs.get(i);
            int p = passOtherOutStationCounts.get(i);
            //归一化
            double lenNorm = (maxLen - minLen) <= 0 ? 0.0 : (l - minLen) / (double) (maxLen - minLen);
            double congNorm = (maxCong - minCong) <= 0 ? 0.0 : (c - minCong) / (double) (maxCong - minCong);
            double passOtherOutStationNorm = (maxPassOtherOutStationCount - minPassOtherOutStationCount) <= 0
                    ? 0.0
                    : (p - minPassOtherOutStationCount) / (double) (maxPassOtherOutStationCount - minPassOtherOutStationCount);
            //获取权重
            double cost = lenNorm * lenW + congNorm * congW;
            double cost = lenNorm * lenW + congNorm * congW + passOtherOutStationNorm * passOtherOutStationW;
            if (cost < bestCost
                    || (cost == bestCost && t < bestTasks)
                    || (cost == bestCost && t == bestTasks && l < bestLen)) {
                    || (cost == bestCost && p < bestPassOtherOutStationCount)
                    || (cost == bestCost && p == bestPassOtherOutStationCount && t < bestTasks)
                    || (cost == bestCost && p == bestPassOtherOutStationCount && t == bestTasks && l < bestLen)) {
                best = candidates.get(i);
                bestCost = cost;
                bestPassOtherOutStationCount = p;
                bestTasks = t;
                bestLen = l;
            }
@@ -344,6 +376,102 @@
        return best;
    }
    private Set<Integer> loadAllOutStationIdSet() {
        Set<Integer> outStationIdSet = new HashSet<>();
        try {
            BasDevpService basDevpService = SpringUtils.getBean(BasDevpService.class);
            if (basDevpService == null) {
                return outStationIdSet;
            }
            List<BasDevp> basDevpList = basDevpService.list(new QueryWrapper<BasDevp>().eq("status", 1));
            for (BasDevp basDevp : basDevpList) {
                List<StationObjModel> outStationList = basDevp.getOutStationList$();
                for (StationObjModel stationObjModel : outStationList) {
                    if (stationObjModel != null && stationObjModel.getStationId() != null) {
                        outStationIdSet.add(stationObjModel.getStationId());
                    }
                }
            }
        } catch (Exception ignore) {}
        return outStationIdSet;
    }
    private int countPassOtherOutStations(List<NavigateNode> path, Set<Integer> outStationIdSet) {
        if (path == null || path.size() < 3 || outStationIdSet == null || outStationIdSet.isEmpty()) {
            return 0;
        }
        Integer startStationId = extractStationId(path.get(0));
        Integer endStationId = extractStationId(path.get(path.size() - 1));
        Set<Integer> hitStationIdSet = new HashSet<>();
        for (int i = 1; i < path.size() - 1; i++) {
            Integer stationId = extractStationId(path.get(i));
            if (stationId == null) {
                continue;
            }
            if (startStationId != null && startStationId.equals(stationId)) {
                continue;
            }
            if (endStationId != null && endStationId.equals(stationId)) {
                continue;
            }
            if (outStationIdSet.contains(stationId)) {
                hitStationIdSet.add(stationId);
            }
        }
        return hitStationIdSet.size();
    }
    private Integer extractStationId(NavigateNode node) {
        if (node == null || node.getNodeValue() == null) {
            return null;
        }
        try {
            JSONObject value = JSON.parseObject(node.getNodeValue());
            if (value == null) {
                return null;
            }
            return value.getInteger("stationId");
        } catch (Exception ignore) {}
        return null;
    }
    private double loadDoubleConfig(ConfigService configService, String code, double defaultValue) {
        if (configService == null || code == null) {
            return defaultValue;
        }
        Config config = configService.getOne(new QueryWrapper<Config>().eq("code", code));
        if (config == null || config.getValue() == null) {
            return defaultValue;
        }
        String value = config.getValue().trim();
        if (value.endsWith("%")) {
            value = value.substring(0, value.length() - 1);
        }
        try {
            return Double.parseDouble(value);
        } catch (Exception ignore) {}
        return defaultValue;
    }
    private boolean loadBooleanConfig(ConfigService configService, String code, boolean defaultValue) {
        if (configService == null || code == null) {
            return defaultValue;
        }
        Config config = configService.getOne(new QueryWrapper<Config>().eq("code", code));
        if (config == null || config.getValue() == null) {
            return defaultValue;
        }
        String value = config.getValue().trim();
        if (value.isEmpty()) {
            return defaultValue;
        }
        return "1".equals(value)
                || "true".equalsIgnoreCase(value)
                || "yes".equalsIgnoreCase(value)
                || "y".equalsIgnoreCase(value)
                || "on".equalsIgnoreCase(value);
    }
    //判断当前节点到下一个节点是否为拐点
    public HashMap<String,Object> searchInflectionPoint(NavigateNode currentNode, NavigateNode fatherNode, NavigateNode nextNode) {
        HashMap<String, Object> map = new HashMap<>();
src/main/resources/sql/20260313_add_station_path_out_station_config.sql
New file
@@ -0,0 +1,31 @@
-- 站点路径规划:经过其他出库站点时的惩罚与强制跳过配置
-- stationPathPassOtherOutStationWeightPercent:
--   数值越大,越倾向避开“中途经过其他出库站点”的路径;设置为 0 可关闭该惩罚
-- stationPathPassOtherOutStationForceSkip:
--   0/false: 仅惩罚
--   1/true : 发现路径经过其他出库站点时直接过滤
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '站点路径经过其他出库站点惩罚权重(数值越大,越倾向避开)', 'stationPathPassOtherOutStationWeightPercent', '100', 1, 1, 'system'
FROM dual
WHERE NOT EXISTS (
    SELECT 1
    FROM sys_config
    WHERE code = 'stationPathPassOtherOutStationWeightPercent'
);
INSERT INTO sys_config(name, code, value, type, status, select_type)
SELECT '站点路径经过其他出库站点强制跳过(1强制跳过)', 'stationPathPassOtherOutStationForceSkip', '0', 1, 1, 'system'
FROM dual
WHERE NOT EXISTS (
    SELECT 1
    FROM sys_config
    WHERE code = 'stationPathPassOtherOutStationForceSkip'
);
SELECT id, name, code, value, type, status, select_type
FROM sys_config
WHERE code IN (
    'stationPathPassOtherOutStationWeightPercent',
    'stationPathPassOtherOutStationForceSkip'
);