diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs
index d6d19ab..69eb8a4 100644
--- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs
+++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs
@@ -9,16 +9,14 @@ using SQLite;
namespace CounterDrone.Core.Simulation
{
- /// 发射计划中的单次事件
public class FireEvent
{
public float FireTime;
- public int PlatformIndex; // 第几个发射平台(0-based)
+ public int PlatformIndex;
public float TargetX, TargetY, TargetZ;
public float MuzzleVelocity;
}
- /// 仿真引擎 — 纯执行器,不包含任何决策逻辑
/// 仿真引擎 — 纯执行器,接收发射计划按时间表执行
public class SimulationEngine
{
@@ -30,35 +28,37 @@ namespace CounterDrone.Core.Simulation
public SimulationState State { get; private set; } = SimulationState.Idle;
public int FrameIndex { get; private set; }
public float SimulationTime { get; private set; }
- /// 时间倍速:>1 加速仿真(仅测试或快速推演用)
public float TimeScale { get; set; } = 1f;
private float _tickInterval;
+ public IReadOnlyList Drones => _drones;
+ public IReadOnlyList Clouds => _clouds;
+ public IReadOnlyList Munitions => _munitions;
+ public IReadOnlyList Events => _allEvents;
+
+ public event Action OnMunitionLaunched;
+ public event Action OnCloudGenerated;
+ public event Action OnDroneDestroyed;
+ public event Action OnDroneReachedTarget;
+ public event Action OnZoneIntruded;
+ public event Action OnSimulationEnded;
+
private List _drones = new();
private List _platforms = new();
private List _munitions = new();
private List _clouds = new();
private List _zones = new();
-
private CombatScene _scene = new();
private float _disperseHeight;
private AmmunitionSpec _ammoSpec = new();
private int _tickRate = 20;
-
- // 发射计划:算法预计算,引擎按时间表执行
private readonly List _fireSchedule = new();
private int _nextFireIndex;
-
private readonly List _allEvents = new();
private SQLiteConnection _frameDb = null!;
private string _taskId = string.Empty;
private int _entityCounter;
- public IReadOnlyList Drones => _drones;
- public IReadOnlyList Clouds => _clouds;
- public IReadOnlyList Munitions => _munitions;
- public IReadOnlyList Events => _allEvents;
-
public SimulationEngine(IScenarioService scenarioService, FrameDataStore frameStore,
IDamageModel damageModel, IPathProvider paths)
{
@@ -68,7 +68,6 @@ namespace CounterDrone.Core.Simulation
_paths = paths;
}
- /// 设置发射计划(来自推荐算法),在 Initialize 之前调用
public void SetFireSchedule(List schedule)
{
_fireSchedule.Clear();
@@ -88,13 +87,10 @@ namespace CounterDrone.Core.Simulation
_tickInterval = 1.0f / _tickRate;
using (var ammoDb = new SQLiteConnection(_paths.GetMainDbPath()))
- {
_ammoSpec = ammoDb.Table()
.FirstOrDefault(a => a.AerosolType == config.Cloud.AerosolType)
- ?? new AmmunitionSpec { InitialRadius = 50, CoreDensity = 1.0f, SourceStrength = 1.0, MaxRadius = 200, MaxDuration = 60 };
- }
+ ?? DefaultAmmunition.GetByType((AerosolType)config.Cloud.AerosolType);
- // 无人机
_drones.Clear();
foreach (var target in config.Targets)
{
@@ -105,18 +101,12 @@ namespace CounterDrone.Core.Simulation
target, config.Waypoints, i, spacing, mode));
}
- // 装备
_platforms.Clear();
foreach (var equip in config.Equipment)
- {
if (equip.EquipmentRole != (int)EquipmentRole.Detection)
- {
for (int i = 0; i < equip.Quantity; i++)
_platforms.Add(new PlatformEntity($"plat_{++_entityCounter}", equip));
- }
- }
- // 管控区域
_zones.Clear();
foreach (var z in config.ControlZones)
_zones.Add(new ControlZoneEntity(z.Id, z.Name, z.VerticesJson,
@@ -129,7 +119,6 @@ namespace CounterDrone.Core.Simulation
SimulationTime = 0;
_entityCounter = 0;
_nextFireIndex = 0;
-
_frameDb = _frameStore.CreateOrOpen(_taskId);
State = SimulationState.Running;
}
@@ -145,12 +134,10 @@ namespace CounterDrone.Core.Simulation
var frameEvents = new List();
// 1. 按发射计划执行
- while (_nextFireIndex < _fireSchedule.Count &&
- _fireSchedule[_nextFireIndex].FireTime <= SimulationTime)
+ while (_nextFireIndex < _fireSchedule.Count && _fireSchedule[_nextFireIndex].FireTime <= SimulationTime)
{
var fe = _fireSchedule[_nextFireIndex++];
- var platform = _platforms.Count > fe.PlatformIndex
- ? _platforms[fe.PlatformIndex] : null;
+ var platform = _platforms.Count > fe.PlatformIndex ? _platforms[fe.PlatformIndex] : null;
if (platform != null && platform.Ready)
{
platform.Fire();
@@ -158,16 +145,10 @@ namespace CounterDrone.Core.Simulation
platform.PlatformType ?? Models.PlatformType.GroundBased,
platform.AerosolType ?? Models.AerosolType.InertGas,
platform.PosX, platform.PosY, platform.PosZ,
- fe.TargetX, fe.TargetY, fe.TargetZ,
- fe.MuzzleVelocity, _disperseHeight);
+ fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight);
_munitions.Add(m);
- frameEvents.Add(new SimEvent
- {
- Type = SimEventType.MunitionLaunched,
- OccurredAt = SimulationTime,
- SourceId = platform.Id,
- Description = $"计划发射 {m.Id}",
- });
+ OnMunitionLaunched?.Invoke(m);
+ frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = SimulationTime, SourceId = platform.Id, Description = $"计划发射 {m.Id}" });
}
}
@@ -178,16 +159,12 @@ namespace CounterDrone.Core.Simulation
if (m.HasArrived)
{
var disp = AlgorithmFactory.Create();
- disp.Initialize(_ammoSpec, _scene,
- new Algorithms.Vector3(m.TargetX, m.TargetY, m.TargetZ), SimulationTime);
- _clouds.Add(new CloudEntity($"cloud_{++_entityCounter}", m.AerosolType, disp, SimulationTime));
+ disp.Initialize(_ammoSpec, _scene, new Algorithms.Vector3(m.TargetX, m.TargetY, m.TargetZ), SimulationTime);
+ var cloud = new CloudEntity($"cloud_{++_entityCounter}", m.AerosolType, disp, SimulationTime);
+ _clouds.Add(cloud);
_munitions.Remove(m);
- frameEvents.Add(new SimEvent
- {
- Type = SimEventType.CloudGenerated,
- OccurredAt = SimulationTime,
- Description = $"云团生成",
- });
+ OnCloudGenerated?.Invoke(cloud);
+ frameEvents.Add(new SimEvent { Type = SimEventType.CloudGenerated, OccurredAt = SimulationTime, Description = "云团生成" });
}
}
@@ -206,18 +183,13 @@ namespace CounterDrone.Core.Simulation
if (cloud.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
{
drone.ExposureTime += scaledDt;
- var dmg = _damageModel.CalculateDamage(
- drone.TargetType, drone.PowerType, cloud.AerosolType,
- cloud.Dispersion.CoreDensity, drone.ExposureTime, scaledDt);
+ var dmg = _damageModel.CalculateDamage(drone.TargetType, drone.PowerType, cloud.AerosolType, cloud.Dispersion.CoreDensity, drone.ExposureTime, scaledDt);
drone.ApplyDamage(dmg);
if (drone.Status == DroneStatus.Destroyed)
- frameEvents.Add(new SimEvent
- {
- Type = SimEventType.DroneDestroyed,
- OccurredAt = SimulationTime,
- TargetId = drone.Id,
- Description = $"无人机 {drone.Id} 被摧毁",
- });
+ {
+ OnDroneDestroyed?.Invoke(drone);
+ frameEvents.Add(new SimEvent { Type = SimEventType.DroneDestroyed, OccurredAt = SimulationTime, TargetId = drone.Id, Description = $"无人机 {drone.Id} 被摧毁" });
+ }
}
}
}
@@ -227,13 +199,10 @@ namespace CounterDrone.Core.Simulation
{
drone.Update(scaledDt, (float)_scene.WindSpeed, (WindDirection)_scene.WindDirection);
if (drone.Status == DroneStatus.ReachedTarget)
- frameEvents.Add(new SimEvent
- {
- Type = SimEventType.DroneReachedTarget,
- OccurredAt = SimulationTime,
- SourceId = drone.Id,
- Description = "到达攻击目标",
- });
+ {
+ OnDroneReachedTarget?.Invoke(drone);
+ frameEvents.Add(new SimEvent { Type = SimEventType.DroneReachedTarget, OccurredAt = SimulationTime, SourceId = drone.Id, Description = "到达攻击目标" });
+ }
}
// 6. 平台冷却
@@ -247,14 +216,8 @@ namespace CounterDrone.Core.Simulation
if (zone.ContainsPoint(drone.PosX, drone.PosY, drone.PosZ))
{
drone.MarkZoneIntruded();
- frameEvents.Add(new SimEvent
- {
- Type = SimEventType.ZoneIntruded,
- OccurredAt = SimulationTime,
- SourceId = drone.Id,
- TargetId = zone.Id,
- Description = $"侵入 {zone.Name}",
- });
+ OnZoneIntruded?.Invoke(drone, zone);
+ frameEvents.Add(new SimEvent { Type = SimEventType.ZoneIntruded, OccurredAt = SimulationTime, SourceId = drone.Id, TargetId = zone.Id, Description = $"侵入 {zone.Name}" });
}
}
}
@@ -263,24 +226,17 @@ namespace CounterDrone.Core.Simulation
if (_drones.All(d => d.Status != DroneStatus.Flying))
{
State = SimulationState.Completed;
+ OnSimulationEnded?.Invoke();
frameEvents.Add(new SimEvent { Type = SimEventType.SimulationEnd, OccurredAt = SimulationTime });
}
_allEvents.AddRange(frameEvents);
- // 录制帧
var snapshots = CollectSnapshots();
_frameStore.WriteFrame(_frameDb, _taskId, FrameIndex, SimulationTime, snapshots);
_frameDb.Commit();
- return new SimulationFrameResult
- {
- EntitySnapshots = snapshots,
- NewEvents = frameEvents,
- State = State,
- FrameIndex = FrameIndex,
- SimulationTime = SimulationTime,
- };
+ return new SimulationFrameResult { EntitySnapshots = snapshots, NewEvents = frameEvents, State = State, FrameIndex = FrameIndex, SimulationTime = SimulationTime };
}
public void Pause() { if (State == SimulationState.Running) State = SimulationState.Paused; }
@@ -291,28 +247,9 @@ namespace CounterDrone.Core.Simulation
{
var list = new List();
foreach (var d in _drones)
- {
- var stage = _damageModel.GetDamageStage(1 - d.Hp);
- list.Add(new EntitySnapshot
- {
- EntityId = d.Id,
- EntityType = Models.EntityType.Drone,
- PosX = d.PosX,
- PosY = d.PosY,
- PosZ = d.PosZ,
- StateData = JsonSerializer.Serialize(new { damageStage = (int)stage, hp = d.Hp }),
- });
- }
+ list.Add(new EntitySnapshot { EntityId = d.Id, EntityType = Models.EntityType.Drone, PosX = d.PosX, PosY = d.PosY, PosZ = d.PosZ, StateData = JsonSerializer.Serialize(new { damageStage = (int)_damageModel.GetDamageStage(1 - d.Hp), hp = d.Hp }) });
foreach (var c in _clouds)
- list.Add(new EntitySnapshot
- {
- EntityId = c.Id,
- EntityType = Models.EntityType.Cloud,
- PosX = c.Dispersion.Center.X,
- PosY = c.Dispersion.Center.Y,
- PosZ = c.Dispersion.Center.Z,
- StateData = JsonSerializer.Serialize(new { radius = c.Dispersion.Radius, opacity = c.Dispersion.Particles.Opacity }),
- });
+ list.Add(new EntitySnapshot { EntityId = c.Id, EntityType = Models.EntityType.Cloud, PosX = c.Dispersion.Center.X, PosY = c.Dispersion.Center.Y, PosZ = c.Dispersion.Center.Z, StateData = JsonSerializer.Serialize(new { radius = c.Dispersion.Radius, opacity = c.Dispersion.Particles.Opacity }) });
return list;
}
}