635 lines
22 KiB
C#
635 lines
22 KiB
C#
using System.Diagnostics;
|
||
using ThreatSource.Missile;
|
||
using ThreatSource.Equipment;
|
||
using ThreatSource.Utils;
|
||
using AirTransmission;
|
||
|
||
namespace ThreatSource.Simulation
|
||
{
|
||
/// <summary>
|
||
/// 仿真管理器的默认实现,提供仿真系统的核心功能
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 该类实现了ISimulationManager接口,提供:
|
||
/// - 事件系统:支持发布/订阅模式的事件处理
|
||
/// - 实体管理:实体的注册、注销和查询
|
||
/// - 第三方集成:支持与外部仿真环境的对接
|
||
///
|
||
/// 线程安全说明:
|
||
/// - 实体管理相关操作使用锁机制确保线程安全
|
||
/// - 事件系统的操作不保证线程安全,应在单线程环境中使用
|
||
/// </remarks>
|
||
public class SimulationManager : ISimulationManager
|
||
{
|
||
/// <summary>
|
||
/// 事件处理器集合,按事件类型分类存储
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 键:事件类型
|
||
/// 值:该类型事件的所有处理器列表
|
||
/// </remarks>
|
||
private readonly Dictionary<Type, List<Delegate>> eventHandlers = new();
|
||
|
||
/// <summary>
|
||
/// 实体集合,使用ID作为键存储所有注册的实体
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 键:实体ID
|
||
/// 值:实体对象
|
||
/// </remarks>
|
||
private readonly Dictionary<string, object> entities = new();
|
||
|
||
/// <summary>
|
||
/// 用于实体管理操作的同步锁对象
|
||
/// </summary>
|
||
private readonly object _lock = new();
|
||
|
||
/// <summary>
|
||
/// 第三方仿真环境适配器实例
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 用于与外部仿真系统进行交互
|
||
/// 可以为null,表示未配置外部系统
|
||
/// </remarks>
|
||
private ISimulationAdapter? _simulationAdapter;
|
||
|
||
private SimulationState _state = SimulationState.Stopped;
|
||
|
||
/// <summary>
|
||
/// 当前仿真状态
|
||
/// </summary>
|
||
public SimulationState State => _state;
|
||
|
||
/// <summary>
|
||
/// 当前仿真时间
|
||
/// </summary>
|
||
public double CurrentTime { get; private set; }
|
||
|
||
/// <summary>
|
||
/// 固定时间步长
|
||
/// </summary>
|
||
private double _timeStep;
|
||
|
||
/// <summary>
|
||
/// 当前天气系统
|
||
/// 默认为无风晴朗天气
|
||
/// </summary>
|
||
private Weather _currentWeather = new(WeatherType.Clear, 25, 50, 23, 0, 415, 1013, 0, 0);
|
||
|
||
/// <summary>
|
||
/// 获取当前天气系统
|
||
/// </summary>
|
||
public Weather CurrentWeather => _currentWeather;
|
||
|
||
/// <summary>
|
||
/// 启动仿真系统
|
||
/// </summary>
|
||
/// <param name="timeStep">时间步长</param>
|
||
public void StartSimulation(double timeStep)
|
||
{
|
||
if (_state != SimulationState.Stopped)
|
||
return;
|
||
|
||
_timeStep = timeStep;
|
||
CurrentTime = 0;
|
||
_state = SimulationState.Running;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 暂停仿真系统
|
||
/// </summary>
|
||
public void PauseSimulation()
|
||
{
|
||
if (_state == SimulationState.Running)
|
||
_state = SimulationState.Paused;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 恢复仿真系统
|
||
/// </summary>
|
||
public void ResumeSimulation()
|
||
{
|
||
if (_state == SimulationState.Paused)
|
||
_state = SimulationState.Running;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 停止仿真系统
|
||
/// </summary>
|
||
public void StopSimulation()
|
||
{
|
||
_state = SimulationState.Stopped;
|
||
CurrentTime = 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 更新仿真系统
|
||
/// </summary>
|
||
/// <param name="deltaTime">时间步长</param>
|
||
public void Update(double deltaTime)
|
||
{
|
||
if (_state != SimulationState.Running)
|
||
return;
|
||
|
||
// 更新仿真时间
|
||
CurrentTime += deltaTime;
|
||
|
||
// 更新所有实体
|
||
UpdateEntities(deltaTime);
|
||
|
||
// 检查命中情况
|
||
CheckHits();
|
||
}
|
||
|
||
private void UpdateEntities(double deltaTime)
|
||
{
|
||
// 创建活跃实体的快照
|
||
List<SimulationElement> activeElements;
|
||
lock (_lock)
|
||
{
|
||
activeElements = entities.Values
|
||
.Cast<SimulationElement>()
|
||
.Where(e => e.IsActive)
|
||
.ToList();
|
||
}
|
||
|
||
// 使用快照更新实体
|
||
foreach (var element in activeElements)
|
||
{
|
||
try
|
||
{
|
||
element.Update(deltaTime);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"更新实体 {element.Id} 时发生错误: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查命中情况
|
||
private void CheckHits()
|
||
{
|
||
try
|
||
{
|
||
// 创建活跃导弹和坦克的快照
|
||
var activeMissiles = entities.Values.OfType<BaseMissile>().Where(e => e.IsActive).ToList();
|
||
var activeTargets = entities.Values.OfType<Tank>().Where(e => e.IsActive).ToList();
|
||
var hitEvents = new List<(Tank tank, BaseMissile missile, double damage)>();
|
||
|
||
Debug.WriteLine($"活动导弹数量: {activeMissiles.Count}");
|
||
Debug.WriteLine($"活动目标数量: {activeTargets.Count}");
|
||
|
||
// 收集所有的命中信息
|
||
foreach (var missile in activeMissiles)
|
||
{
|
||
foreach (var target in activeTargets)
|
||
{
|
||
double distance = Vector3D.Distance(missile.KState.Position, target.KState.Position);
|
||
Debug.WriteLine($"导弹 {missile.Id} 和目标 {target.Id} 之间的距离: {distance:F2}m");
|
||
if (distance <= missile.Properties.ExplosionRadius)
|
||
{
|
||
// 使用命中概率进行随机判定
|
||
var random = new Random();
|
||
double randomValue = random.NextDouble(); // 生成0.0到1.0的随机数
|
||
|
||
Debug.WriteLine($"导弹 {missile.Id} 命中概率判定: 随机值={randomValue:F3}, 命中概率={missile.Properties.HitProbability:F3}");
|
||
|
||
if (randomValue <= missile.Properties.HitProbability)
|
||
{
|
||
Debug.WriteLine($"导弹 {missile.Id} 命中概率判定成功,将造成伤害");
|
||
double damage = CalculateMissileDamage(missile);
|
||
hitEvents.Add((target, missile, damage));
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"导弹 {missile.Id} 命中概率判定失败,未造成伤害");
|
||
}
|
||
break; // 一个导弹只能命中一个目标
|
||
}
|
||
}
|
||
}
|
||
|
||
// 先处理所有伤害
|
||
foreach (var (tank, missile, damage) in hitEvents)
|
||
{
|
||
if (tank.IsActive)
|
||
{
|
||
tank.TakeDamage(damage);
|
||
Debug.WriteLine($"目标 {tank.Id} 被导弹 {missile.Id} 击中,造成 {damage} 点伤害");
|
||
}
|
||
}
|
||
|
||
// 最后一次性发布所有命中事件
|
||
foreach (var (tank, missile, _) in hitEvents)
|
||
{
|
||
try
|
||
{
|
||
PublishEvent(new TargetHitEvent { TargetId = tank.Id, MissileId = missile.Id });
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"发布命中事件时发生错误: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"处理命中事件时发生错误: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 计算导弹造成的伤害
|
||
/// </summary>
|
||
private static double CalculateMissileDamage(BaseMissile missile)
|
||
{
|
||
// 这里可以根据导弹类型、速度等因素计算伤害
|
||
// 现在我们简单地返回一个固定值
|
||
return 50;
|
||
}
|
||
|
||
#region 事件系统
|
||
/// <summary>
|
||
/// 发布事件到仿真系统
|
||
/// </summary>
|
||
/// <typeparam name="T">事件类型</typeparam>
|
||
/// <param name="evt">要发布的事件实例</param>
|
||
/// <remarks>
|
||
/// 发布过程:
|
||
/// 1. 查找并调用所有注册的事件处理器
|
||
/// 2. 如果配置了外部仿真适配器,同时发布到外部系统
|
||
///
|
||
/// 异常处理:
|
||
/// - 捕获并记录内部事件处理异常
|
||
/// - 捕获并记录外部事件发布异常
|
||
/// </remarks>
|
||
public void PublishEvent<T>(T evt)
|
||
{
|
||
if (evt == null)
|
||
{
|
||
throw new ArgumentNullException(nameof(evt), "事件对象不能为空");
|
||
}
|
||
|
||
var eventType = typeof(T);
|
||
var actualType = evt.GetType();
|
||
Debug.WriteLine($"[事件] 发布: {eventType.Name}, 实际类型: {actualType.Name}");
|
||
|
||
// 1. 尝试使用实际类型查找处理器
|
||
if (eventHandlers.TryGetValue(actualType, out var actualHandlers))
|
||
{
|
||
Debug.WriteLine($"[事件] 找到 {actualHandlers.Count} 个处理器(实际类型)");
|
||
// 创建处理器列表的副本
|
||
var handlers = actualHandlers.ToList();
|
||
foreach (var handler in handlers)
|
||
{
|
||
try
|
||
{
|
||
handler.DynamicInvoke(evt);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"[事件] 处理异常: {ex.Message}");
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 2. 如果找不到实际类型的处理器,尝试使用声明类型
|
||
if (eventHandlers.TryGetValue(eventType, out var typeHandlers))
|
||
{
|
||
Debug.WriteLine($"[事件] 找到 {typeHandlers.Count} 个处理器(声明类型)");
|
||
// 创建处理器列表的副本
|
||
var handlers = typeHandlers.ToList();
|
||
foreach (var handler in handlers)
|
||
{
|
||
try
|
||
{
|
||
((Action<T>)handler).Invoke(evt);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"[事件] 处理异常: {ex.Message}");
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[事件] 未找到处理器,已注册的事件类型:");
|
||
foreach (var type in eventHandlers.Keys)
|
||
{
|
||
Debug.WriteLine($"[事件] - {type.Name}");
|
||
}
|
||
}
|
||
|
||
// 3. 发送到外部仿真系统
|
||
try
|
||
{
|
||
_simulationAdapter?.PublishToExternalSimulation(evt);
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"[事件] 外部系统异常: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 订阅特定类型的事件
|
||
/// </summary>
|
||
/// <typeparam name="T">事件类型</typeparam>
|
||
/// <param name="handler">事件处理函数</param>
|
||
/// <remarks>
|
||
/// 如果是首次订阅该类型事件,会创建新的处理器列表
|
||
/// 同一个处理器可以多次订阅,会被多次调用
|
||
/// </remarks>
|
||
public void SubscribeToEvent<T>(Action<T> handler)
|
||
{
|
||
var eventType = typeof(T);
|
||
Debug.WriteLine($"[事件] 订阅: {eventType.Name}, 处理器: {handler.Method.Name}");
|
||
|
||
if (!eventHandlers.ContainsKey(eventType))
|
||
{
|
||
eventHandlers[eventType] = new List<Delegate>();
|
||
}
|
||
eventHandlers[eventType].Add(handler);
|
||
Debug.WriteLine($"[事件] 当前处理器数量: {eventHandlers[eventType].Count}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 取消订阅特定类型的事件
|
||
/// </summary>
|
||
/// <typeparam name="T">事件类型</typeparam>
|
||
/// <param name="handler">要取消的事件处理函数</param>
|
||
/// <remarks>
|
||
/// 如果取消订阅后该类型没有其他处理器,会删除整个处理器列表
|
||
/// 如果处理器不存在,操作会被忽略
|
||
/// </remarks>
|
||
public void UnsubscribeFromEvent<T>(Action<T> handler)
|
||
{
|
||
var eventType = typeof(T);
|
||
Debug.WriteLine($"[事件] 取消订阅: {eventType.Name}, 处理器: {handler.Method.Name}");
|
||
|
||
if (eventHandlers.TryGetValue(eventType, out var handlers))
|
||
{
|
||
handlers.Remove(handler);
|
||
Debug.WriteLine($"[事件] 剩余处理器数量: {handlers.Count}");
|
||
if (handlers.Count == 0)
|
||
{
|
||
eventHandlers.Remove(eventType);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清理所有事件处理器和待处理事件
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 用于导弹切换或仿真重置时清理事件系统状态
|
||
/// 防止事件残留影响新的制导系统
|
||
/// </remarks>
|
||
public void ClearAllEvents()
|
||
{
|
||
Debug.WriteLine("[事件] 清理所有事件处理器和待处理事件");
|
||
|
||
// 清理所有事件处理器
|
||
eventHandlers.Clear();
|
||
|
||
Debug.WriteLine("[事件] 事件系统已完全清理");
|
||
}
|
||
#endregion
|
||
|
||
#region 实体管理
|
||
/// <summary>
|
||
/// 注册实体到仿真系统
|
||
/// </summary>
|
||
/// <param name="id">实体ID</param>
|
||
/// <param name="entity">实体对象</param>
|
||
/// <returns>注册是否成功</returns>
|
||
/// <remarks>
|
||
/// 注册失败的情况:
|
||
/// - ID为空或实体为null
|
||
/// - ID已被其他实体使用
|
||
///
|
||
/// 线程安全:使用锁保护注册操作
|
||
/// </remarks>
|
||
public bool RegisterEntity(string id, object entity)
|
||
{
|
||
if (string.IsNullOrEmpty(id) || entity == null)
|
||
return false;
|
||
|
||
lock (_lock)
|
||
{
|
||
if (entities.ContainsKey(id))
|
||
return false;
|
||
|
||
entities[id] = entity;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从仿真系统注销实体
|
||
/// </summary>
|
||
/// <param name="id">要注销的实体ID</param>
|
||
/// <returns>注销是否成功</returns>
|
||
/// <remarks>
|
||
/// 如果实体不存在,返回false
|
||
/// 线程安全:使用锁保护注销操作
|
||
/// </remarks>
|
||
public bool UnregisterEntity(string id)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
return entities.Remove(id);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 根据ID获取实体
|
||
/// </summary>
|
||
/// <param name="id">实体ID</param>
|
||
/// <returns>实体对象,如果不存在则返回null</returns>
|
||
/// <remarks>
|
||
/// 查找顺序:
|
||
/// 1. 在本地实体集合中查找
|
||
/// 2. 如果未找到且配置了外部适配器,尝试从外部系统获取
|
||
///
|
||
/// 线程安全:使用锁保护查询操作
|
||
/// </remarks>
|
||
public object? GetEntityById(string id)
|
||
{
|
||
lock (_lock)
|
||
{
|
||
if (entities.TryGetValue(id, out var entity))
|
||
{
|
||
return entity;
|
||
}
|
||
|
||
if (_simulationAdapter != null)
|
||
{
|
||
var externalEntity = _simulationAdapter.GetEntity(id);
|
||
if (externalEntity != null)
|
||
{
|
||
return externalEntity;
|
||
}
|
||
}
|
||
|
||
Debug.WriteLine($"[实体管理] 警告: 未找到实体: {id}");
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取特定类型的所有实体
|
||
/// </summary>
|
||
/// <typeparam name="T">实体类型</typeparam>
|
||
/// <returns>指定类型的实体列表</returns>
|
||
/// <remarks>
|
||
/// 只返回本地注册的实体,不包括外部系统的实体
|
||
/// 返回的列表是原始集合的副本,可以安全地遍历
|
||
///
|
||
/// 线程安全:使用锁保护查询操作
|
||
/// </remarks>
|
||
public IReadOnlyList<T> GetEntitiesByType<T>() where T : class
|
||
{
|
||
lock (_lock)
|
||
{
|
||
var result = new List<T>();
|
||
foreach (var entity in entities.Values)
|
||
{
|
||
if (entity is T typedEntity)
|
||
{
|
||
result.Add(typedEntity);
|
||
}
|
||
}
|
||
return result;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取仿真系统中的所有实体
|
||
/// </summary>
|
||
/// <returns>所有实体的列表</returns>
|
||
/// <remarks>
|
||
/// 只返回本地注册的实体,不包括外部系统的实体
|
||
/// 返回的列表是原始集合的副本,可以安全地遍历
|
||
///
|
||
/// 线程安全:使用锁保护查询操作
|
||
/// </remarks>
|
||
public IReadOnlyList<object> GetAllEntities()
|
||
{
|
||
lock (_lock)
|
||
{
|
||
var result = new List<object>(entities.Values);
|
||
Debug.WriteLine($"[实体管理] 获取所有实体,总数: {result.Count}");
|
||
return result;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 第三方仿真环境集成
|
||
/// <summary>
|
||
/// 设置第三方仿真环境适配器
|
||
/// </summary>
|
||
/// <param name="adapter">要设置的适配器实例</param>
|
||
/// <exception cref="ArgumentNullException">适配器参数为null时抛出</exception>
|
||
/// <remarks>
|
||
/// 设置适配器后,仿真系统将:
|
||
/// - 在发布事件时同步到外部系统
|
||
/// - 在查询实体时同时查询外部系统
|
||
/// </remarks>
|
||
public void SetSimulationAdapter(ISimulationAdapter adapter)
|
||
{
|
||
_simulationAdapter = adapter ?? throw new ArgumentNullException(nameof(adapter));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前的仿真环境适配器
|
||
/// </summary>
|
||
/// <returns>当前配置的适配器实例,如果未配置则返回null</returns>
|
||
public ISimulationAdapter? GetSimulationAdapter()
|
||
{
|
||
return _simulationAdapter;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 处理来自外部仿真系统的事件
|
||
/// </summary>
|
||
/// <typeparam name="T">事件类型</typeparam>
|
||
/// <param name="evt">接收到的事件</param>
|
||
/// <remarks>
|
||
/// 将外部事件转发到内部事件系统进行处理
|
||
/// 所有注册的内部处理器都会收到该事件
|
||
/// </remarks>
|
||
public void HandleExternalEvent<T>(T evt)
|
||
{
|
||
PublishEvent(evt);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 同步仿真时间
|
||
/// </summary>
|
||
/// <param name="simulationTime">外部仿真系统的时间</param>
|
||
/// <remarks>
|
||
/// 用于与外部仿真环境进行时间同步:
|
||
/// - 设置当前仿真时间为指定值
|
||
/// - 通知适配器进行时间同步(如果存在)
|
||
/// - 发布时间同步事件通知其他系统
|
||
///
|
||
/// 使用场景:
|
||
/// - 外部仿真环境驱动威胁源库时间
|
||
/// - 分布式仿真中的时间协调
|
||
/// - 仿真回放功能
|
||
/// </remarks>
|
||
public void SynchronizeTime(double simulationTime)
|
||
{
|
||
// 更新内部仿真时间
|
||
CurrentTime = simulationTime;
|
||
|
||
// 如果配置了外部适配器,通知其进行时间同步
|
||
if (_simulationAdapter != null)
|
||
{
|
||
_simulationAdapter.SynchronizeTime(simulationTime);
|
||
}
|
||
|
||
// 发布时间同步事件,通知其他组件
|
||
var timeEvent = new TimeSynchronizationEvent
|
||
{
|
||
SynchronizedTime = simulationTime,
|
||
Timestamp = simulationTime
|
||
};
|
||
PublishEvent(timeEvent);
|
||
|
||
Debug.WriteLine($"[时间同步] 仿真时间已同步至: {simulationTime:F3}s");
|
||
}
|
||
#endregion
|
||
|
||
/// <summary>
|
||
/// 设置当前天气
|
||
/// </summary>
|
||
/// <param name="weather">天气条件</param>
|
||
public void SetWeather(Weather weather)
|
||
{
|
||
_currentWeather = weather;
|
||
Debug.WriteLine($"已设置天气:{weather.Type}");
|
||
|
||
// 通知其他实体天气已变化
|
||
var evt = new WeatherChangedEvent
|
||
{
|
||
NewWeather = weather
|
||
};
|
||
PublishEvent(evt);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 天气变化事件,当天气系统变化时触发
|
||
/// </summary>
|
||
public class WeatherChangedEvent : SimulationEvent
|
||
{
|
||
/// <summary>
|
||
/// 新的天气系统
|
||
/// </summary>
|
||
public Weather? NewWeather { get; set; }
|
||
}
|
||
} |