ThreatSourceLibaray/ThreatSource/src/Simulation/SimulationManager.cs

635 lines
22 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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