ThreatSourceLibaray/ThreatSource/src/Indicator/BaseIndicator.cs

404 lines
16 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 ThreatSource.Utils;
using ThreatSource.Jammer;
using ThreatSource.Simulation;
using ThreatSource.Jammable;
using ThreatSource.Equipment;
using System.Diagnostics;
namespace ThreatSource.Indicator
{
/// <summary>
/// 指示器抽象基类实现IIndicator接口的基本功能
/// </summary>
/// <remarks>
/// 该类提供了指示器的通用实现:
/// - 位置和姿态管理
/// - 激活状态控制
/// - 干扰处理机制
/// 是具体指示器类的实现基础
/// </remarks>
public abstract class BaseIndicator : SimulationElement, IIndicator, IJammable
{
/// <summary>
/// 视觉遮挡阈值 (0.0 - 1.0)
/// </summary>
protected const double TargetObscurationThreshold = 0.8;
/// <summary>
/// 干扰处理组件
/// </summary>
protected readonly JammableComponent _jammingComponent;
/// <summary>
/// 获取目标是否被烟幕遮挡 (由任何烟幕引起)
/// </summary>
protected bool IsTargetObscured { get; private set; } = false;
/// <summary>
/// 最后一次成功获取的目标位置
/// </summary>
protected Vector3D? _lastKnownTargetPosition = null;
/// <summary>
/// 最后一次成功获取的目标朝向
/// </summary>
protected Orientation? _lastKnownTargetOrientation = null;
/// <summary>
/// 获取设备支持的干扰类型
/// </summary>
/// <remarks>
/// 定义指示器可以被哪些类型的干扰影响
/// 子类需要重写此属性以指定支持的干扰类型
/// </remarks>
public abstract IEnumerable<JammingType> SupportedJammingTypes { get; }
/// <summary>
/// 获取设备当前是否处于被干扰状态
/// </summary>
/// <remarks>
/// true表示指示器当前受到干扰影响
/// false表示指示器正常工作
/// </remarks>
public bool IsJammed => _jammingComponent.IsJammed;
/// <summary>
/// 获取或设置目标ID
/// </summary>
public string? TargetId { get; protected set; }
/// <summary>
/// 获取或设置导弹ID
/// </summary>
public string? MissileId { get; protected set; }
/// <summary>
/// 初始化指示器基类的新实例
/// </summary>
/// <param name="id">指示器ID</param>
/// <param name="motionParameters">初始运动参数</param>
/// <param name="manager">仿真管理器实例</param>
/// <remarks>
/// 构造过程:
/// - 调用基类构造器
/// - 初始化基本属性
/// - 初始化干扰处理组件
/// </remarks>
protected BaseIndicator(string id, MotionParameters motionParameters, ISimulationManager manager)
: base(id, motionParameters, manager)
{
_jammingComponent = new JammableComponent(
positionProvider: () => base.Position
);
_jammingComponent.JammingApplied += HandleJammingApplied;
_jammingComponent.JammingCleared += HandleJammingCleared;
}
/// <summary>
/// 初始化干扰阈值和支持的干扰类型
/// </summary>
/// <param name="jammingResistanceThreshold">干扰抗性阈值,单位:瓦特</param>
/// <param name="supportedTypes">支持的干扰类型</param>
/// <remarks>
/// 使用此方法初始化指示器的干扰处理参数:
/// - 设置干扰抗性阈值
/// - 配置支持的干扰类型
/// 子类应在构造函数中调用此方法
/// </remarks>
protected void InitializeJamming(double jammingResistanceThreshold, IEnumerable<JammingType> supportedTypes)
{
_jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedTypes);
}
/// <summary>
/// 应用干扰
/// </summary>
/// <param name="parameters">干扰参数</param>
/// <remarks>
/// 处理对指示器的干扰影响
/// 只有支持的干扰类型才会被处理
/// </remarks>
public virtual void ApplyJamming(JammingParameters parameters)
{
_jammingComponent.ApplyJamming(parameters);
}
/// <summary>
/// 清除干扰
/// </summary>
/// <param name="type">要清除的干扰类型</param>
/// <remarks>
/// 移除特定类型的干扰影响
/// 恢复指示器的正常工作状态
/// </remarks>
public virtual void ClearJamming(JammingType type)
{
_jammingComponent.ClearJamming(type);
}
/// <summary>
/// 处理指示器被干扰的事件
/// </summary>
/// <param name="parameters">干扰参数</param>
protected virtual void HandleJammingApplied(JammingParameters parameters)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (parameters.Type == JammingType.SmokeScreen)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现特定的干扰响应
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} 受到 {parameters.Type} 类型干扰,功率:{parameters.Power}W", "Jamming");
}
}
/// <summary>
/// 处理指示器干扰被清除的事件
/// </summary>
/// <param name="type">被清除的干扰类型</param>
protected virtual void HandleJammingCleared(JammingType type)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (type == JammingType.SmokeScreen)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现干扰清除后的特定行为
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} {type} 类型干扰已清除", "Jamming");
}
}
/// <summary>
/// 激活指示器 (基类统一处理干扰事件订阅)
/// </summary>
public override void Activate()
{
if (!IsActive)
{
IsActive = true;
// 统一订阅 JammingEvent
SimulationManager.SubscribeToEvent<JammingEvent>(HandleJammingEvent);
// 子类仍需在此处订阅其他特定事件(如导弹热源事件)
}
base.Activate(); // 调用 SimulationElement 的基类 Activate
// 子类需要在此之后调用 base.Activate() 并订阅烟幕事件 <--- 旧注释,移除或更新
// 现在基类处理通用 JammingEvent子类只需要处理非干扰事件
RecalculateObscurationStatus(); // 激活时检查一次初始遮挡状态
}
/// <summary>
/// 停用指示器 (基类统一处理干扰事件取消订阅)
/// </summary>
public override void Deactivate()
{
if (IsActive)
{
IsActive = false;
// 统一取消订阅 JammingEvent
SimulationManager.UnsubscribeFromEvent<JammingEvent>(HandleJammingEvent);
// 子类仍需在此处取消订阅其他特定事件
}
// 子类需要在此之前取消订阅烟幕事件 <--- 旧注释,移除或更新
IsTargetObscured = false;
// 重置最后目标状态
_lastKnownTargetPosition = null;
_lastKnownTargetOrientation = null;
base.Deactivate(); // 调用 SimulationElement 的基类 Deactivate
}
/// <summary>
/// 更新指示器状态
/// </summary>
public override void Update(double deltaTime)
{
_jammingComponent.UpdateJammingStatus(deltaTime); // 更新干扰状态(包括持续时间等)
if (IsActive)
{
// 只有未被电子干扰时才更新指示器特定功能
// 烟幕遮挡 (IsTargetObscured) 在 UpdateIndicator 内部处理
if (!IsJammed)
{
UpdateIndicator(deltaTime);
}
}
}
/// <summary>
/// 更新指示器特定功能
/// </summary>
/// <param name="deltaTime">时间步长,单位:秒</param>
/// <remarks>
/// 子类需要重写此方法以实现特定的更新逻辑
/// 只有在未被干扰时才应执行正常的功能逻辑
/// </remarks>
protected virtual void UpdateIndicator(double deltaTime)
{
// 子类中实现具体的指示器更新逻辑
}
/// <summary>
/// 获取指示器运行状态
/// </summary>
/// <returns>包含指示器完整状态信息的结构体</returns>
/// <remarks>
/// 子类需要重写此方法以提供完整的状态信息
/// 包括位置、姿态、目标关联和干扰状态等
/// </remarks>
public abstract IndicatorRunningState GetRunningState();
/// <summary>
/// 获取指示器状态信息
/// </summary>
/// <returns>包含指示器状态信息的字符串</returns>
public override string GetStatus()
{
return $"指示器 {Id} 当前状态: 上次目标位置: {_lastKnownTargetPosition}, 上次目标朝向: {_lastKnownTargetOrientation}";
}
/// <summary>
/// 重新计算目标是否被任何活动的烟幕遮挡,并更新 IsTargetObscured 状态。
/// 子类应在处理烟幕相关事件时调用此方法。
/// </summary>
protected void RecalculateObscurationStatus()
{
bool currentlyObscured = CheckIfTargetIsObscured(out BaseEquipment? currentTarget);
if (IsTargetObscured != currentlyObscured)
{
IsTargetObscured = currentlyObscured;
}
// 仅当目标被看到 (未遮挡) 且目标有效时,才更新最后已知状态
if (!currentlyObscured && currentTarget != null)
{
_lastKnownTargetPosition = currentTarget.Position;
_lastKnownTargetOrientation = currentTarget.Orientation;
}
}
/// <summary>
/// 检查当前目标是否被活动烟幕遮挡。
/// </summary>
/// <param name="targetFound">如果成功找到目标实体,则输出该实体。</param>
/// <returns>如果目标被遮挡,则返回 true否则返回 false。</returns>
private bool CheckIfTargetIsObscured(out BaseEquipment? targetFound)
{
targetFound = null;
// 1. 检查指示器状态和目标ID
if (!IsActive || string.IsNullOrEmpty(TargetId))
{
return false; // 未激活或无目标,视为未遮挡
}
// 2. 尝试获取目标实体
targetFound = SimulationManager.GetEntityById(TargetId) as BaseEquipment;
if (targetFound == null)
{
return false; // 找不到目标实体,视为未遮挡
}
// 3. 获取活动的烟幕
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
.Where(sg => sg != null && sg.IsActive)
.ToList(); // 获取列表以便检查是否为空
if (!activeSmokeGrenades.Any())
{
return false; // 没有活动的烟幕,视为未遮挡
}
// 4. 准备计算所需数据
Vector3D observerPos = Position;
Vector3D targetCenter = targetFound.Position;
Orientation targetOrient = targetFound.Orientation;
Vector3D targetDims = new(targetFound.Properties.Width, targetFound.Properties.Height, targetFound.Properties.Length);
// 5. 遍历烟幕检查遮挡
foreach (var smokeGrenade in activeSmokeGrenades)
{
try
{
Vector3D smokeCenter = smokeGrenade.Position;
Orientation smokeOrient = smokeGrenade.Orientation;
Vector3D smokeDims;
if (smokeGrenade.config.SmokeType == SmokeScreenType.Cloud)
{
smokeDims = new(smokeGrenade.config.Thickness, smokeGrenade.config.CloudDiameter, smokeGrenade.config.CloudDiameter);
}
else // Wall
{
smokeDims = new(smokeGrenade.config.Thickness, smokeGrenade.config.WallHeight, smokeGrenade.config.WallWidth);
}
double ratio = ObscurationUtils.CalculateProjectedOverlapRatio(
observerPos,
smokeCenter, smokeDims, smokeOrient,
targetCenter, targetDims, targetOrient
);
// 只要有一个烟幕满足遮挡条件,即可判定为遮挡
if (ratio >= TargetObscurationThreshold)
{
return true; // 被遮挡
}
}
catch (Exception ex)
{
Debug.WriteLine($"检查烟幕遮挡状态时发生错误: {ex.Message}");
// 在发生错误时,可以选择继续检查其他烟幕,或者直接返回一个默认值(例如 false
}
}
// 6. 如果遍历完所有烟幕都没有达到遮挡阈值
return false; // 未被遮挡
}
// --- 新增干扰事件处理逻辑 ---
/// <summary>
/// 统一处理干扰事件
/// </summary>
/// <param name="evt">干扰事件</param>
protected virtual void HandleJammingEvent(JammingEvent evt)
{
if (evt == null) return;
// 在应用干扰前检查是否应该处理此干扰
if (!ShouldHandleJamming(evt.Parameters))
{
// Debug.WriteLine($"[BaseIndicator] {Id} 忽略干扰事件,类型: {evt.Parameters.Type}");
return;
}
// 如果应该处理,则应用干扰
ApplyJamming(evt.Parameters);
}
/// <summary>
/// 判断是否应该处理传入的干扰参数(可被子类重写以添加特定检查)
/// </summary>
/// <param name="parameters">干扰参数</param>
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected virtual bool ShouldHandleJamming(JammingParameters parameters)
{
// 基类默认处理所有受支持类型的干扰 (包括烟幕)
// 子类可以重写此方法添加额外检查,如波段匹配
// 注意:烟幕干扰虽然通过这里,但主要效果通过 IsTargetObscured 和 RecalculateObscurationStatus 体现
bool supported = SupportedJammingTypes.Contains(parameters.Type);
// if(!supported) Debug.WriteLine($"[BaseIndicator] {Id} 不支持干扰类型: {parameters.Type}");
return supported;
}
// --- 结束新增干扰事件处理逻辑 ---
}
}