Target→Profile / Targets→DroneProfiles 重命名
- DroneWave.Target → Profile - ScenarioConfig.Targets → DroneProfiles - DefensePlanner / SimulationEngine / ReportService 全部引用同步 - 更新 Unity DLL + defaults.json - 238 测试全过
This commit is contained in:
parent
46ce1234ee
commit
67cea1490d
@ -114,7 +114,7 @@
|
||||
}
|
||||
],
|
||||
|
||||
"targets": [
|
||||
"drones": [
|
||||
{ "Id": "quadcopter", "Name": "[Demo] 小型四旋翼(DJI类)",
|
||||
"DroneType": 0, "PowerType": 0, "Wingspan": 1.2, "TypicalSpeed": 60.0, "TypicalAltitude": 300.0 },
|
||||
{ "Id": "electric-scout", "Name": "[Demo] 电推侦察无人机",
|
||||
|
||||
@ -51,7 +51,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
public class DroneWave
|
||||
{
|
||||
public string WaveId { get; set; } = string.Empty;
|
||||
public DroneProfile Target { get; set; } = new();
|
||||
public DroneProfile Profile { get; set; } = new();
|
||||
public RoutePlan Route { get; set; } = new();
|
||||
public List<Waypoint> Waypoints { get; set; } = new();
|
||||
|
||||
|
||||
@ -7,14 +7,14 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
public (int laneCount, float laneSpacing) Divide(DroneWave wave, float cloudRadius)
|
||||
{
|
||||
if (wave.Route == null || wave.Target.Quantity <= 1)
|
||||
if (wave.Route == null || wave.Profile.Quantity <= 1)
|
||||
return (1, 0);
|
||||
|
||||
var mode = (FormationMode)wave.Route.FormationMode;
|
||||
if (mode != FormationMode.Formation)
|
||||
return (1, 0);
|
||||
|
||||
int latCount = wave.Route.LateralCount ?? wave.Target.Quantity;
|
||||
int latCount = wave.Route.LateralCount ?? wave.Profile.Quantity;
|
||||
int longCount = wave.Route.LongitudinalCount ?? 1;
|
||||
float latWidth = (latCount - 1) * (float)wave.Route.LateralSpacing;
|
||||
float longDepth = (longCount - 1) * (float)wave.Route.LongitudinalSpacing;
|
||||
@ -22,7 +22,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
// 任一维度超过云团半径,则无法一个云团覆盖
|
||||
if (latWidth > cloudRadius || longDepth > cloudRadius)
|
||||
return (wave.Target.Quantity, (float)wave.Route.LateralSpacing);
|
||||
return (wave.Profile.Quantity, (float)wave.Route.LateralSpacing);
|
||||
|
||||
return (1, 0);
|
||||
}
|
||||
|
||||
@ -62,14 +62,14 @@ namespace CounterDrone.Core.Algorithms
|
||||
foreach (var t in threats)
|
||||
{
|
||||
t.ArrivalTime = t.GetArrivalTime();
|
||||
t.ThreatIndex = CalcThreatIndex(_config, t.Target);
|
||||
t.ThreatIndex = CalcThreatIndex(_config, t.Profile);
|
||||
}
|
||||
var sorted = threats.OrderByDescending(t => t.Priority).ToList();
|
||||
|
||||
// Step 2-4: 贪心分配求解
|
||||
// 预先检查:所有需要的弹药类型都在目录中
|
||||
var neededTypes = sorted
|
||||
.Select(t => MatchAmmo(_config, (PowerType)t.Target.PowerType))
|
||||
.Select(t => MatchAmmo(_config, (PowerType)t.Profile.PowerType))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
foreach (var t in neededTypes)
|
||||
@ -132,7 +132,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
}
|
||||
|
||||
// 计算总弹药需求
|
||||
var neededAmmo = MatchAmmo(_config, (PowerType)threat.Target.PowerType);
|
||||
var neededAmmo = MatchAmmo(_config, (PowerType)threat.Profile.PowerType);
|
||||
var ammo = _ammoCatalog.First(a => a.AerosolType == (int)neededAmmo);
|
||||
|
||||
// 单机需求
|
||||
@ -234,7 +234,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
foreach (var threat in sortedThreats)
|
||||
{
|
||||
var a2 = _ammoCatalog.FirstOrDefault(s =>
|
||||
s.AerosolType == (int)MatchAmmo(_config, (PowerType)threat.Target.PowerType));
|
||||
s.AerosolType == (int)MatchAmmo(_config, (PowerType)threat.Profile.PowerType));
|
||||
int totalRounds = plan.Assignments
|
||||
.Where(a => a.DroneWaveId == threat.WaveId)
|
||||
.Sum(a => a.RoundsFired);
|
||||
@ -249,7 +249,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
{
|
||||
bool engaged = plan.Assignments.Any(a => a.DroneWaveId == threat.WaveId);
|
||||
if (engaged) continue;
|
||||
var ammo = MatchAmmo(_config, (PowerType)threat.Target.PowerType);
|
||||
var ammo = MatchAmmo(_config, (PowerType)threat.Profile.PowerType);
|
||||
var matching = fireUnits.Where(u => u.AmmoTypes.Contains(ammo)).ToList();
|
||||
if (matching.Count == 0)
|
||||
reasons.Add($"威胁 {threat.WaveId}: 无火力单元装载 {ammo} 弹药");
|
||||
@ -323,7 +323,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
List<FireUnit> availableUnits, CombatScene env)
|
||||
{
|
||||
var candidates = new List<InterceptCandidate>();
|
||||
var neededAmmo = MatchAmmo(_config, (PowerType)threat.Target.PowerType);
|
||||
var neededAmmo = MatchAmmo(_config, (PowerType)threat.Profile.PowerType);
|
||||
var ammo = _ammoCatalog.First(a => a.AerosolType == (int)neededAmmo);
|
||||
|
||||
var (ammoEff, expansionTime, _) = ComputeEffectiveRadius(ammo, env);
|
||||
@ -359,7 +359,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
if (!HasInterceptWindow(threat, midArc, expansionTime, deliveryTime)) return null;
|
||||
|
||||
float avgSpeed = GetDroneSpeedMs(threat);
|
||||
float neededExposure = _damageModel.RequiredExposureSeconds((DroneType)threat.Target.DroneType, (PowerType)threat.Target.PowerType, ammoType);
|
||||
float neededExposure = _damageModel.RequiredExposureSeconds((DroneType)threat.Profile.DroneType, (PowerType)threat.Profile.PowerType, ammoType);
|
||||
float actualExposure = effectiveR * 2f / avgSpeed;
|
||||
float prob = Math.Min(_config.MaxInterceptProbability, actualExposure / neededExposure);
|
||||
|
||||
@ -394,7 +394,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
var cloudModel = new CloudExpansionModel(ammo, env);
|
||||
float avgSpeed = GetDroneSpeedMs(threat);
|
||||
float neededExposure = _damageModel.RequiredExposureSeconds(
|
||||
(DroneType)threat.Target.DroneType, (PowerType)threat.Target.PowerType, (AerosolType)ammo.AerosolType);
|
||||
(DroneType)threat.Profile.DroneType, (PowerType)threat.Profile.PowerType, (AerosolType)ammo.AerosolType);
|
||||
float requiredCoverage = neededExposure * avgSpeed;
|
||||
// 间距由配置的重叠比例驱动,公式在 CloudExpansionModel(共享)
|
||||
float spacing = 2f * turbulentRadius * (1f - _config.CloudOverlapRatio);
|
||||
@ -411,7 +411,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
float actualExposure = actualCoverage / avgSpeed;
|
||||
|
||||
var aerosolType = (AerosolType)ammo.AerosolType;
|
||||
float neededExposure = _damageModel.RequiredExposureSeconds((DroneType)threat.Target.DroneType, (PowerType)threat.Target.PowerType, aerosolType);
|
||||
float neededExposure = _damageModel.RequiredExposureSeconds((DroneType)threat.Profile.DroneType, (PowerType)threat.Profile.PowerType, aerosolType);
|
||||
|
||||
return Math.Min(_config.MaxInterceptProbability, actualExposure / neededExposure);
|
||||
}
|
||||
@ -571,7 +571,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
private static Vector3 ThreatMidpoint(DroneWave threat)
|
||||
{
|
||||
if (threat.Waypoints.Count < 2)
|
||||
return new Vector3(0, (float)threat.Target.TypicalAltitude, 0);
|
||||
return new Vector3(0, (float)threat.Profile.TypicalAltitude, 0);
|
||||
var s = threat.Waypoints[0];
|
||||
var e = threat.Waypoints[^1];
|
||||
return new Vector3(
|
||||
|
||||
@ -19,7 +19,7 @@ namespace CounterDrone.Core
|
||||
public List<RouteTemplate> Routes { get; set; } = new();
|
||||
[System.Text.Json.Serialization.JsonPropertyName("fireUnits")]
|
||||
public List<FireUnitSpec> FireUnits { get; set; } = new();
|
||||
[System.Text.Json.Serialization.JsonPropertyName("targets")]
|
||||
[System.Text.Json.Serialization.JsonPropertyName("drones")]
|
||||
public List<DroneSpec> Drones { get; set; } = new();
|
||||
[System.Text.Json.Serialization.JsonPropertyName("detectionEquipment")]
|
||||
public List<SensorSpec> Sensors { get; set; } = new();
|
||||
|
||||
@ -8,7 +8,7 @@ namespace CounterDrone.Core.Models
|
||||
public Scenario Info { get; set; } = new();
|
||||
public CombatScene Scene { get; set; } = new();
|
||||
public List<ControlZone> ControlZones { get; set; } = new();
|
||||
public List<DroneProfile> Targets { get; set; } = new();
|
||||
public List<DroneProfile> Drones { get; set; } = new();
|
||||
public List<EquipmentDeployment> Equipment { get; set; } = new();
|
||||
public CloudDispersal Cloud { get; set; } = new();
|
||||
/// <summary>多批次航路(多批次支持)</summary>
|
||||
|
||||
@ -16,8 +16,8 @@ namespace CounterDrone.Core.Repository
|
||||
|
||||
public void DeleteByScenarioId(string scenarioId)
|
||||
{
|
||||
var targets = GetByScenarioId(scenarioId);
|
||||
foreach (var t in targets)
|
||||
var drones = GetByScenarioId(scenarioId);
|
||||
foreach (var t in drones)
|
||||
Db.Delete(t);
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,12 +14,12 @@ namespace CounterDrone.Core.Services
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
var platforms = config.Equipment.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform).ToList();
|
||||
var target = config.Targets.FirstOrDefault();
|
||||
var target = config.Drones.FirstOrDefault();
|
||||
var scene = config.Scene;
|
||||
var destroyed = events.Count(e => e.Type == SimEventType.DroneDestroyed);
|
||||
var reached = events.Count(e => e.Type == SimEventType.DroneReachedTarget);
|
||||
var intruded = events.Count(e => e.Type == SimEventType.ZoneIntruded);
|
||||
var totalDrones = config.Targets.Sum(t => t.Quantity);
|
||||
var totalDrones = config.Drones.Sum(t => t.Quantity);
|
||||
var result = DetermineInterceptResult(destroyed, reached + intruded, totalDrones);
|
||||
|
||||
sb.AppendLine("# 仿真评估报告");
|
||||
|
||||
@ -26,7 +26,7 @@ namespace CounterDrone.Core.Services
|
||||
var destroyed = events.Count(e => e.Type == SimEventType.DroneDestroyed);
|
||||
var reached = events.Count(e => e.Type == SimEventType.DroneReachedTarget);
|
||||
var intruded = events.Count(e => e.Type == SimEventType.ZoneIntruded);
|
||||
var total = config.Targets.Sum(t => t.Quantity);
|
||||
var total = config.Drones.Sum(t => t.Quantity);
|
||||
var result = _generator.DetermineInterceptResult(destroyed, reached + intruded, total);
|
||||
|
||||
var report = new SimulationReport
|
||||
|
||||
@ -96,7 +96,7 @@ namespace CounterDrone.Core.Services
|
||||
Info = scenario,
|
||||
Scene = _sceneRepo.GetById(id) ?? new CombatScene { ScenarioId = id },
|
||||
ControlZones = _zoneRepo.GetByScenarioId(id),
|
||||
Targets = _targetRepo.GetByScenarioId(id),
|
||||
Drones = _targetRepo.GetByScenarioId(id),
|
||||
Equipment = _equipRepo.GetByScenarioId(id),
|
||||
Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { ScenarioId = id },
|
||||
Routes = _routeRepo.GetByScenarioId(id),
|
||||
|
||||
@ -103,7 +103,7 @@ namespace CounterDrone.Core.Simulation
|
||||
.First(a => a.AerosolType == config.Cloud.AerosolType);
|
||||
|
||||
_drones.Clear();
|
||||
foreach (var target in config.Targets)
|
||||
foreach (var target in config.Drones)
|
||||
{
|
||||
var route = config.Routes.FirstOrDefault(r => r.WaveId == target.WaveId);
|
||||
var wps = config.WaypointGroups.GetValueOrDefault(target.WaveId, new List<Waypoint>());
|
||||
@ -496,14 +496,14 @@ namespace CounterDrone.Core.Simulation
|
||||
private static List<DroneWave> BuildDroneWaves(ScenarioConfig config)
|
||||
{
|
||||
var groups = new List<DroneWave>();
|
||||
foreach (var t in config.Targets)
|
||||
foreach (var t in config.Drones)
|
||||
{
|
||||
var route = config.Routes.FirstOrDefault(r => r.WaveId == t.WaveId);
|
||||
var wps = config.WaypointGroups.GetValueOrDefault(t.WaveId, new List<Waypoint>());
|
||||
groups.Add(new DroneWave
|
||||
{
|
||||
WaveId = t.WaveId,
|
||||
Target = t,
|
||||
Profile = t,
|
||||
Route = route ?? new RoutePlan(),
|
||||
Waypoints = wps,
|
||||
});
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
"drones": {
|
||||
".NETStandard,Version=v2.1": {},
|
||||
".NETStandard,Version=v2.1/": {
|
||||
"CounterDrone.Core/1.0.0": {
|
||||
|
||||
Binary file not shown.
@ -33,7 +33,7 @@ namespace CounterDrone.Unity
|
||||
Debug.Log($"2. ScenarioManager OK. scenario: {scenarioId}");
|
||||
|
||||
scenarioMgr.SaveScene(scenarioId, new CombatScene { WindSpeed = 0 });
|
||||
scenarioMgr.SaveTarget(scenarioId, new DroneProfile
|
||||
scenarioMgr.SaveDrone(scenarioId, new DroneProfile
|
||||
{
|
||||
WaveId = "default",
|
||||
DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston,
|
||||
@ -56,7 +56,7 @@ namespace CounterDrone.Unity
|
||||
var droneGroup = new DroneWave
|
||||
{
|
||||
WaveId = "default",
|
||||
Target = detail.Targets[0],
|
||||
Drone = detail.Drones[0],
|
||||
Route = detail.Routes[0],
|
||||
Waypoints = detail.WaypointGroups["default"],
|
||||
};
|
||||
@ -77,7 +77,7 @@ namespace CounterDrone.Unity
|
||||
PositionX = (droneGroup.Waypoints[0].PosX + droneGroup.Waypoints[^1].PosX) / 2,
|
||||
PositionY = (droneGroup.Waypoints[0].PosY + droneGroup.Waypoints[^1].PosY) / 2,
|
||||
PositionZ = (droneGroup.Waypoints[0].PosZ + droneGroup.Waypoints[^1].PosZ) / 2,
|
||||
DisperseHeight = droneGroup.Target.TypicalAltitude,
|
||||
DisperseHeight = droneGroup.Drone.TypicalAltitude,
|
||||
});
|
||||
|
||||
var equips = new List<EquipmentDeployment>();
|
||||
|
||||
@ -82,7 +82,7 @@ namespace CounterDrone.Unity
|
||||
var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths));
|
||||
var threats = new List<DroneWave>
|
||||
{
|
||||
new DroneWave { WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"] }
|
||||
new DroneWave { WaveId = "default", Drone = detail.Drones[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"] }
|
||||
};
|
||||
var fireUnits = new List<FireUnit>();
|
||||
for (int i = 0; i < 4; i++)
|
||||
@ -99,7 +99,7 @@ namespace CounterDrone.Unity
|
||||
scenario.SaveCloudDispersal(scenarioId, new CloudDispersal
|
||||
{
|
||||
AerosolType = (int)AerosolType.InertGas,
|
||||
DisperseHeight = detail.Targets[0].TypicalAltitude,
|
||||
DisperseHeight = detail.Drones[0].TypicalAltitude,
|
||||
Source = "Algorithm",
|
||||
});
|
||||
scenario.SaveDeployment(scenarioId, new List<EquipmentDeployment>
|
||||
|
||||
@ -114,7 +114,7 @@
|
||||
}
|
||||
],
|
||||
|
||||
"targets": [
|
||||
"drones": [
|
||||
{ "Id": "quadcopter", "Name": "[Demo] 小型四旋翼(DJI类)",
|
||||
"DroneType": 0, "PowerType": 0, "Wingspan": 1.2, "TypicalSpeed": 60.0, "TypicalAltitude": 300.0 },
|
||||
{ "Id": "electric-scout", "Name": "[Demo] 电推侦察无人机",
|
||||
|
||||
@ -43,7 +43,7 @@ namespace CounterDrone.Core.Tests
|
||||
var t = new DroneWave
|
||||
{
|
||||
WaveId = "default",
|
||||
Target = new DroneProfile
|
||||
Profile = new DroneProfile
|
||||
{
|
||||
DroneType = (int)DroneType.FixedWing, PowerType = (int)power,
|
||||
Quantity = 1, TypicalSpeed = speed, TypicalAltitude = alt,
|
||||
@ -130,7 +130,7 @@ namespace CounterDrone.Core.Tests
|
||||
public void MultiLane_3Drones_3Lanes_ExactOutput()
|
||||
{
|
||||
var threat = MakeThreat(speed: 200);
|
||||
threat.Target.Quantity = 3;
|
||||
threat.Profile.Quantity = 3;
|
||||
threat.Route = new RoutePlan { FormationMode = (int)Models.FormationMode.Formation, LateralSpacing = 50 };
|
||||
threat.Waypoints.Clear();
|
||||
threat.Waypoints.Add(new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 });
|
||||
@ -378,7 +378,7 @@ namespace CounterDrone.Core.Tests
|
||||
var threat = new DroneWave
|
||||
{
|
||||
WaveId = "default",
|
||||
Target = new DroneProfile
|
||||
Profile = new DroneProfile
|
||||
{
|
||||
DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston,
|
||||
Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500,
|
||||
|
||||
@ -36,7 +36,7 @@ namespace CounterDrone.Core.Tests
|
||||
var config = new ScenarioConfig
|
||||
{
|
||||
Info = new Scenario { Name = "测试任务", ScenarioNumber = "SIM-001" },
|
||||
Targets = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Drones = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Equipment = new List<EquipmentDeployment>
|
||||
{
|
||||
new EquipmentDeployment { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 3 },
|
||||
|
||||
@ -37,7 +37,7 @@ namespace CounterDrone.Core.Tests
|
||||
var config = new ScenarioConfig
|
||||
{
|
||||
Info = new Scenario { Name = "Test", ScenarioNumber = "SIM-001" },
|
||||
Targets = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Drones = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Equipment = new List<EquipmentDeployment>(),
|
||||
Cloud = new CloudDispersal(),
|
||||
};
|
||||
@ -59,7 +59,7 @@ namespace CounterDrone.Core.Tests
|
||||
var config = new ScenarioConfig
|
||||
{
|
||||
Info = new Scenario { Name = "Del", ScenarioNumber = "SIM-DEL" },
|
||||
Targets = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Drones = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Equipment = new List<EquipmentDeployment>(),
|
||||
Cloud = new CloudDispersal(),
|
||||
};
|
||||
@ -75,7 +75,7 @@ namespace CounterDrone.Core.Tests
|
||||
var cfg = new ScenarioConfig
|
||||
{
|
||||
Info = new Scenario { Name = "城市防御", ScenarioNumber = "SIM-A" },
|
||||
Targets = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Drones = new List<DroneProfile> { new DroneProfile { Quantity = 1 } },
|
||||
Equipment = new List<EquipmentDeployment>(),
|
||||
Cloud = new CloudDispersal(),
|
||||
};
|
||||
|
||||
@ -123,7 +123,7 @@ namespace CounterDrone.Core.Tests
|
||||
Assert.Equal(4, detail.Info.CurrentStep);
|
||||
Assert.Equal((int)SceneType.Mountain, detail.Scene.SceneType);
|
||||
Assert.Equal(10.0, detail.Scene.WindSpeed);
|
||||
Assert.Equal(3, detail.Targets[0].Quantity);
|
||||
Assert.Equal(3, detail.Drones[0].Quantity);
|
||||
Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType);
|
||||
}
|
||||
|
||||
@ -211,7 +211,7 @@ namespace CounterDrone.Core.Tests
|
||||
});
|
||||
|
||||
var detail = _service.GetScenarioDetail(scenario.Id);
|
||||
var target = detail.Targets[0];
|
||||
var target = detail.Drones[0];
|
||||
Assert.Equal((int)DroneType.FixedWing, target.DroneType);
|
||||
Assert.Equal(5, target.Quantity);
|
||||
Assert.Equal(120.0, target.TypicalSpeed);
|
||||
@ -464,9 +464,9 @@ namespace CounterDrone.Core.Tests
|
||||
Assert.Equal("核心区", detail.ControlZones[0].Name);
|
||||
|
||||
// 目标
|
||||
Assert.Single(detail.Targets);
|
||||
Assert.Equal(2, detail.Targets[0].Quantity);
|
||||
Assert.Equal(300.0, detail.Targets[0].TypicalSpeed);
|
||||
Assert.Single(detail.Drones);
|
||||
Assert.Equal(2, detail.Drones[0].Quantity);
|
||||
Assert.Equal(300.0, detail.Drones[0].TypicalSpeed);
|
||||
|
||||
// 装备
|
||||
Assert.Equal(2, detail.Equipment.Count);
|
||||
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user