docker-compose
This commit is contained in:
@@ -20,16 +20,21 @@ import com.solution.rule.domain.ultimately.vo.FireRuleRedPlatformSlotVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRedWeaponSlotVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleOutputVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleRouteTrackPointVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleSceneGroupNodeVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskNodeVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleSceneTaskPayloadVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleTrackParamVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleTrackRouteVO;
|
||||
import com.solution.rule.domain.ultimately.vo.FireRuleWingmanDataVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -231,6 +236,241 @@ public final class FireRuleRedWeaponOutputFillHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 编组规则:按红方装备列表分组,写入 {@link FireRuleTrackParamVO#getGroups}(JSON 键 Groups),
|
||||
* groupType=addGroup,drawName=领队装备名+后缀;僚机 wingmanData 中 name 为红方 equipmentId。
|
||||
* 多蓝方任务循环时与航迹一致:在已有 Groups 上追加,且 group id 与已有项冲突时自动加后缀。
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static void fillTrackParamGroups(FireRuleOutputVO out, FireRuleTaskInputDTO blueTask, Map params) {
|
||||
if (out == null || blueTask == null) {
|
||||
return;
|
||||
}
|
||||
boolean enabled = Boolean.parseBoolean(String.valueOf(params.getOrDefault("groupRuleEnabled", true)));
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
List<FireRuleRedWeaponEquipmentVO> reds = out.getRedWeapons();
|
||||
if (reds == null || reds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ensureTrackParam(out);
|
||||
|
||||
String dirMode = str(params, "trackPointDirectionMode", "head2next");
|
||||
double fallbackBrg = readDouble(params, "trackFallbackBearingDeg", readDouble(params, "fallbackBearingDeg", 0d));
|
||||
double mainBearing = computeTrackBearingDeg(blueTask, dirMode, fallbackBrg);
|
||||
|
||||
String mode = str(params, "groupFormationMode", "onePerRed");
|
||||
int clusterSize = readInt(params, "groupClusterSize", 3);
|
||||
if (clusterSize < 1) {
|
||||
clusterSize = 1;
|
||||
}
|
||||
int minWing = readInt(params, "groupMinMembersForWingman", 2);
|
||||
|
||||
List<List<FireRuleRedWeaponEquipmentVO>> buckets = splitRedsIntoGroups(reds, mode, clusterSize);
|
||||
List<FireRuleSceneGroupNodeVO> batch = new ArrayList<>();
|
||||
String blueSeg = sanitizeRouteIdSegment(nz(blueTask.getId()));
|
||||
int gidx = 0;
|
||||
for (List<FireRuleRedWeaponEquipmentVO> bucket : buckets) {
|
||||
if (bucket == null || bucket.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
FireRuleRedWeaponEquipmentVO leader = pickGroupLeader(bucket, params);
|
||||
if (leader == null) {
|
||||
continue;
|
||||
}
|
||||
FireRuleSceneGroupNodeVO node = new FireRuleSceneGroupNodeVO();
|
||||
String baseId = "group_" + blueSeg + "_" + gidx;
|
||||
node.setId(ensureUniqueGroupId(out.getTrackParam(), baseId));
|
||||
String suffix = str(params, "groupDrawNameSuffix", "编组");
|
||||
boolean withIndex = Boolean.parseBoolean(String.valueOf(params.getOrDefault("groupDrawNameWithIndex", false)));
|
||||
String drawName = nz(leader.getName()) + suffix;
|
||||
if (withIndex) {
|
||||
drawName += (gidx + 1);
|
||||
}
|
||||
node.setDrawName(drawName);
|
||||
node.setGroupType("addGroup");
|
||||
node.setName("addGroup");
|
||||
node.setLeader(nz(leader.getEquipmentId()));
|
||||
node.setSort(System.currentTimeMillis() + gidx);
|
||||
node.setWingmanData(buildWingmanData(bucket, leader, mainBearing, params, minWing));
|
||||
batch.add(node);
|
||||
gidx++;
|
||||
}
|
||||
mergeGroupsIntoTrackParam(out.getTrackParam(), batch);
|
||||
}
|
||||
|
||||
private static List<List<FireRuleRedWeaponEquipmentVO>> splitRedsIntoGroups(
|
||||
List<FireRuleRedWeaponEquipmentVO> reds, String mode, int clusterSize) {
|
||||
List<FireRuleRedWeaponEquipmentVO> clean = new ArrayList<>();
|
||||
for (FireRuleRedWeaponEquipmentVO r : reds) {
|
||||
if (r != null) {
|
||||
clean.add(r);
|
||||
}
|
||||
}
|
||||
List<List<FireRuleRedWeaponEquipmentVO>> out = new ArrayList<>();
|
||||
if (clean.isEmpty()) {
|
||||
return out;
|
||||
}
|
||||
if ("singleGroup".equalsIgnoreCase(mode)) {
|
||||
out.add(clean);
|
||||
return out;
|
||||
}
|
||||
if ("clusterByCount".equalsIgnoreCase(mode)) {
|
||||
for (int i = 0; i < clean.size(); i += clusterSize) {
|
||||
int end = Math.min(i + clusterSize, clean.size());
|
||||
out.add(new ArrayList<>(clean.subList(i, end)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
for (FireRuleRedWeaponEquipmentVO r : clean) {
|
||||
out.add(Collections.singletonList(r));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static FireRuleRedWeaponEquipmentVO pickGroupLeader(List<FireRuleRedWeaponEquipmentVO> bucket, Map params) {
|
||||
if (bucket == null || bucket.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String mode = str(params, "groupLeaderPickMode", "byHitRateThenId");
|
||||
FireRuleRedWeaponEquipmentVO best = bucket.get(0);
|
||||
for (int i = 1; i < bucket.size(); i++) {
|
||||
FireRuleRedWeaponEquipmentVO cur = bucket.get(i);
|
||||
if ("byId".equalsIgnoreCase(mode)) {
|
||||
if (compareEquipmentId(cur, best) < 0) {
|
||||
best = cur;
|
||||
}
|
||||
} else {
|
||||
if (isBetterLeaderByHitRateThenId(cur, best)) {
|
||||
best = cur;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
private static boolean isBetterLeaderByHitRateThenId(FireRuleRedWeaponEquipmentVO cand, FireRuleRedWeaponEquipmentVO inc) {
|
||||
double rc = leaderRad(cand);
|
||||
double ri = leaderRad(inc);
|
||||
int c = Double.compare(rc, ri);
|
||||
if (c > 0) {
|
||||
return true;
|
||||
}
|
||||
if (c < 0) {
|
||||
return false;
|
||||
}
|
||||
return compareEquipmentId(cand, inc) < 0;
|
||||
}
|
||||
|
||||
private static double leaderRad(FireRuleRedWeaponEquipmentVO w) {
|
||||
if (w == null || w.getSuccessTargetRad() == null) {
|
||||
return -1d;
|
||||
}
|
||||
return w.getSuccessTargetRad().doubleValue();
|
||||
}
|
||||
|
||||
private static int compareEquipmentId(FireRuleRedWeaponEquipmentVO a, FireRuleRedWeaponEquipmentVO b) {
|
||||
String sa = a != null ? nz(a.getEquipmentId()) : "";
|
||||
String sb = b != null ? nz(b.getEquipmentId()) : "";
|
||||
return sa.compareTo(sb);
|
||||
}
|
||||
|
||||
private static List<FireRuleWingmanDataVO> buildWingmanData(
|
||||
List<FireRuleRedWeaponEquipmentVO> bucket,
|
||||
FireRuleRedWeaponEquipmentVO leader,
|
||||
double mainBearing,
|
||||
Map params,
|
||||
int minMembersForWingman) {
|
||||
if (bucket == null || leader == null || bucket.size() < minMembersForWingman) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
double distBase = readDouble(params, "wingmanDistanceBaseMeters", 100);
|
||||
double distStep = readDouble(params, "wingmanDistanceStepMeters", 50);
|
||||
double angBase = readDouble(params, "wingmanAngleBaseDeg", 50);
|
||||
double angStep = readDouble(params, "wingmanAngleStepDeg", 15);
|
||||
double altBase = readDouble(params, "wingmanAltBaseMeters", 40);
|
||||
double altScale = readDouble(params, "wingmanAltScale", 1.0);
|
||||
|
||||
List<FireRuleWingmanDataVO> list = new ArrayList<>();
|
||||
int wmIdx = 0;
|
||||
for (FireRuleRedWeaponEquipmentVO r : bucket) {
|
||||
if (r == null || r == leader) {
|
||||
continue;
|
||||
}
|
||||
FireRuleWingmanDataVO w = new FireRuleWingmanDataVO();
|
||||
w.setKey(wmIdx);
|
||||
w.setName(nz(r.getEquipmentId()));
|
||||
w.setDistance((int) Math.round(distBase + wmIdx * distStep));
|
||||
double deg = normDeg(mainBearing + angBase + wmIdx * angStep);
|
||||
w.setAngle(String.format(Locale.US, "%.1f", deg));
|
||||
Double lh = extractFirstPlatformHeight(leader);
|
||||
Double wh = extractFirstPlatformHeight(r);
|
||||
double delta = 0d;
|
||||
if (lh != null && wh != null) {
|
||||
delta = wh - lh;
|
||||
}
|
||||
w.setAlt((int) Math.round(altBase + altScale * delta));
|
||||
list.add(w);
|
||||
wmIdx++;
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private static Double extractFirstPlatformHeight(FireRuleRedWeaponEquipmentVO red) {
|
||||
if (red == null || red.getSubComponents() == null || red.getSubComponents().getPlatform() == null) {
|
||||
return null;
|
||||
}
|
||||
for (FireRuleRedPlatformSlotVO pl : red.getSubComponents().getPlatform()) {
|
||||
if (pl == null || pl.getPositions() == null || pl.getPositions().size() < 3) {
|
||||
continue;
|
||||
}
|
||||
return pl.getPositions().get(2);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static double normDeg(double deg) {
|
||||
double a = deg % 360d;
|
||||
if (a < 0) {
|
||||
a += 360d;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
private static String ensureUniqueGroupId(FireRuleTrackParamVO tp, String baseId) {
|
||||
Set<String> seen = new HashSet<>();
|
||||
if (tp.getGroups() != null) {
|
||||
for (FireRuleSceneGroupNodeVO g : tp.getGroups()) {
|
||||
if (g != null && !isBlank(g.getId())) {
|
||||
seen.add(g.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!seen.contains(baseId)) {
|
||||
return baseId;
|
||||
}
|
||||
int k = 1;
|
||||
while (seen.contains(baseId + "_" + k)) {
|
||||
k++;
|
||||
}
|
||||
return baseId + "_" + k;
|
||||
}
|
||||
|
||||
private static void mergeGroupsIntoTrackParam(FireRuleTrackParamVO tp, List<FireRuleSceneGroupNodeVO> batch) {
|
||||
if (tp == null || batch == null || batch.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<FireRuleSceneGroupNodeVO> cur = tp.getGroups();
|
||||
if (cur == null) {
|
||||
cur = new ArrayList<>();
|
||||
} else {
|
||||
cur = new ArrayList<>(cur);
|
||||
}
|
||||
cur.addAll(batch);
|
||||
tp.setGroups(cur);
|
||||
}
|
||||
|
||||
private static void ensureTrackParam(FireRuleOutputVO out) {
|
||||
if (out.getTrackParam() == null) {
|
||||
out.setTrackParam(new FireRuleTrackParamVO());
|
||||
|
||||
@@ -245,6 +245,22 @@ public final class RuleFunction {
|
||||
FireRuleRedWeaponOutputFillHelper.fillTrackParamAndBindMoveRoute(out, fact.getTask(), p);
|
||||
}
|
||||
|
||||
/**
|
||||
* 编组规则:填充 {@link com.solution.rule.domain.ultimately.vo.FireRuleTrackParamVO#getGroups}(groupType=addGroup)与僚机 wingmanData。
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static void groupFormation(DroolsFact fact, Map globalParams) {
|
||||
if (fact == null || fact.getTask() == null || fact.getFireRuleOutputVO() == null) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> p = castParams(globalParams);
|
||||
boolean enabled = Boolean.parseBoolean(String.valueOf(p.getOrDefault("groupRuleEnabled", true)));
|
||||
if (!enabled) {
|
||||
return;
|
||||
}
|
||||
FireRuleRedWeaponOutputFillHelper.fillTrackParamGroups(fact.getFireRuleOutputVO(), fact.getTask(), p);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, Object> castParams(Map raw) {
|
||||
return raw == null ? new java.util.HashMap<>() : (Map<String, Object>) raw;
|
||||
|
||||
Reference in New Issue
Block a user