diff --git a/auto-solution-rule/src/main/resources/rules/fire-rule.drl b/auto-solution-rule/src/main/resources/rules/fire-rule.drl index 843b680..e905204 100644 --- a/auto-solution-rule/src/main/resources/rules/fire-rule.drl +++ b/auto-solution-rule/src/main/resources/rules/fire-rule.drl @@ -149,6 +149,14 @@ function Map buildBusinessConfig() { cfg.put("blueMissileRangeDefault", 220); // 蓝方导弹范围默认值 cfg.put("minBlueMissileCountForLinkage", 1); // 联动触发门槛 + // ---------- 全组件数量匹配(可改) ---------- + // 逻辑:按红方武器 targetId 找到对应蓝方装备(equipmentId),然后覆盖所有“非导弹组件”的数量 + cfg.put("enableComponentQuantityMatch", Boolean.TRUE); + // 格式示例:蓝穿甲弹->红穿甲弹,蓝火控雷达->红火控雷达;为空则默认 deviceName 一致匹配 + cfg.put("componentDeviceNameMappingCsv", ""); + // 跳过覆盖:蓝方组件 deviceName 包含该关键词则不覆盖(默认用来保留导弹联动偏移) + cfg.put("skipMissileComponentsByNameContains", "导弹"); + // ---------- 任务自动命名模板(可改) ---------- // 任务分类优先级:导弹突击 > 防空压制 > 反装甲打击 > 炮火压制 > 通用打击 cfg.put("taskName_missile_strike", "导弹突击打击任务"); @@ -219,6 +227,18 @@ then applyMissileLinkage($fact, cfg); end +//------------------------------------------------------------------------------- +// 全组件数量匹配:按 redWeapon.targetId 对应蓝方装备,覆盖“非导弹组件”的数量 +rule "全组件数量匹配规则" +agenda-group "打击任务" +salience 53 +when + $fact : FactTask(blueTask.side == "蓝方", redTask.side == "红方") +then + Map cfg = buildBusinessConfig(); + applyAllComponentQuantities($fact, cfg); +end + //------------------------------------------------------------------------------- rule "任务自动匹配规则" agenda-group "打击任务" @@ -582,6 +602,159 @@ function void bindTargetIdsForRedWeapons(List redWeapons, List blueWeapons, Map } } +//------------------------------------------------------------------------------- +// component 映射解析 + 全组件数量覆盖 +function Map parseDeviceNameMapping(String csv) { + Map result = new java.util.HashMap(); + if (csv == null) { + return result; + } + String text = csv.trim(); + if (text.equals("")) { + return result; + } + String[] parts = text.split(","); + for (int i = 0; i < parts.length; i++) { + String one = parts[i]; + if (one == null) { + continue; + } + String item = one.trim(); + if (item.equals("")) { + continue; + } + int idx = item.indexOf("->"); + if (idx <= 0 || idx >= item.length() - 2) { + continue; + } + String left = item.substring(0, idx).trim(); + String right = item.substring(idx + 2).trim(); + if (left.equals("") || right.equals("")) { + continue; + } + result.put(left, right); + } + return result; +} + +function void applyAllComponentQuantities(FactTask fact, Map cfg) { + if (fact == null || fact.getBlueTask() == null || fact.getRedTask() == null) { + return; + } + if (!readBooleanCfg(cfg, "enableComponentQuantityMatch", true)) { + return; + } + Task blueTask = fact.getBlueTask(); + Task redTask = fact.getRedTask(); + List blueWeapons = blueTask.getTaskWeapons(); + List redWeapons = redTask.getTaskWeapons(); + if (blueWeapons == null || blueWeapons.isEmpty() || redWeapons == null || redWeapons.isEmpty()) { + return; + } + + String mappingCsv = cfg == null ? null : (String) cfg.get("componentDeviceNameMappingCsv"); + Map deviceNameMapping = parseDeviceNameMapping(mappingCsv); + + String skipKeyword = cfg == null ? null : (String) cfg.get("skipMissileComponentsByNameContains"); + if (skipKeyword != null) { + skipKeyword = skipKeyword.trim(); + if (skipKeyword.equals("")) { + skipKeyword = null; + } + } + + // 遍历红方每个武器:用 targetId 找蓝方装备(equipmentId) + for (Object objR : redWeapons) { + Weapon redWeapon = (Weapon) objR; + if (redWeapon == null) { + continue; + } + String targetId = redWeapon.getTargetId(); + if (isBlank(targetId)) { + // 允许不匹配:targetId 为空直接跳过 + continue; + } + + Weapon blueWeapon = null; + for (Object objB : blueWeapons) { + Weapon w = (Weapon) objB; + if (w == null) { + continue; + } + String eqId = w.getEquipmentId(); + if (eqId != null && eqId.equals(targetId)) { + blueWeapon = w; + break; + } + } + if (blueWeapon == null) { + continue; + } + + List blueComps = blueWeapon.getComponents(); + List redComps = redWeapon.getComponents(); + if (blueComps == null || blueComps.isEmpty() || redComps == null || redComps.isEmpty()) { + continue; + } + + // 用蓝方组件驱动覆盖红方组件数量 + for (Object objBC : blueComps) { + SubComponents blueComp = (SubComponents) objBC; + if (blueComp == null) { + continue; + } + String blueCompName = blueComp.getDeviceName(); + if (isBlank(blueCompName)) { + continue; + } + + // 跳过导弹组件覆盖,避免覆盖导弹联动偏移逻辑 + if (skipKeyword != null && blueCompName.contains(skipKeyword)) { + continue; + } + + String redCompName = (String) deviceNameMapping.get(blueCompName); + if (redCompName == null || redCompName.trim().equals("")) { + redCompName = blueCompName; + } + + SubComponents redComp = null; + for (Object objRC : redComps) { + SubComponents rc = (SubComponents) objRC; + if (rc == null) { + continue; + } + if (redCompName.equals(rc.getDeviceName())) { + redComp = rc; + break; + } + } + if (redComp == null) { + // 允许不匹配:红方下没有该组件则跳过 + continue; + } + + List blueParams = blueComp.getComponentParams(); + List redParams = redComp.getComponentParams(); + if (blueParams == null || blueParams.isEmpty() || redParams == null || redParams.isEmpty()) { + continue; + } + + ComponentParam blueFirst = (ComponentParam) blueParams.get(0); + ComponentParam redFirst = (ComponentParam) redParams.get(0); + if (blueFirst == null || redFirst == null) { + continue; + } + if (blueFirst.getNumber() == null) { + continue; + } + + // 只覆盖数量(componentParams[0].number) + redFirst.setNumber(blueFirst.getNumber()); + } + } +} + function void prepareDeploymentPools(FactTask fact, Map cfg, Map runtime) { if (!readBooleanCfg(cfg, "enablePositionRules", true)) { return;