Target→Profile / Targets→DroneProfiles 重命名

- DroneWave.Target → Profile
- ScenarioConfig.Targets → DroneProfiles
- DefensePlanner / SimulationEngine / ReportService 全部引用同步
- 更新 Unity DLL + defaults.json
- 238 测试全过
This commit is contained in:
tian 2026-06-18 15:37:25 +08:00
parent 46ce1234ee
commit 67cea1490d
21 changed files with 45 additions and 45 deletions

View File

@ -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] 电推侦察无人机",

View File

@ -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();

View File

@ -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);
}

View File

@ -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(

View File

@ -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();

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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("# 仿真评估报告");

View File

@ -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

View File

@ -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),

View File

@ -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,
});

View File

@ -4,7 +4,7 @@
"signature": ""
},
"compilationOptions": {},
"targets": {
"drones": {
".NETStandard,Version=v2.1": {},
".NETStandard,Version=v2.1/": {
"CounterDrone.Core/1.0.0": {

View File

@ -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>();

View File

@ -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>

View File

@ -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] 电推侦察无人机",

View File

@ -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,

View File

@ -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 },

View File

@ -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(),
};

View File

@ -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.