增加了单元测试框架,重构了告警器模型,增加了告警器配置,编写了新增告警器的单元测试

This commit is contained in:
Tian jianyong 2024-11-19 16:57:57 +08:00
parent 3f02f52074
commit af702cbbda
18 changed files with 1354 additions and 313 deletions

View File

@ -4,8 +4,19 @@
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<LangVersion>9.0</LangVersion> <LangVersion>9.0</LangVersion>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Compile Remove="obj\**" />
<Compile Remove="src\Tests\**" />
<EmbeddedResource Remove="obj\**" />
<EmbeddedResource Remove="src\Tests\**" />
<None Remove="obj\**" />
<None Remove="src\Tests\**" />
</ItemGroup>
</Project> </Project>

View File

@ -1,29 +1,33 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17 # Visual Studio Version 17
VisualStudioVersion = 17.5.002.0 VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActiveProtect", "ActiveProtect.csproj", "{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveProtect", "ActiveProtect.csproj", "{7138CDD4-EC75-4803-8050-D72A32B381CA}"
EndProject EndProject
Global Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{88B204F0-6636-4265-ACFB-52D889A56413}"
GlobalSection(SolutionConfigurationPlatforms) = preSolution EndProject
Debug|Any CPU = Debug|Any CPU Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveProtect.Tests", "src\Tests\ActiveProtect.Tests.csproj", "{55F5BDBE-025D-465B-BB34-2262369715BB}"
Release|Any CPU = Release|Any CPU EndProject
EndGlobalSection Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU Release|Any CPU = Release|Any CPU
{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU EndGlobalSection
{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Release|Any CPU.Build.0 = Release|Any CPU GlobalSection(SolutionProperties) = preSolution
{644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU HideSolutionNode = FALSE
{644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection
{644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU GlobalSection(ProjectConfigurationPlatforms) = postSolution
{644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Release|Any CPU.Build.0 = Release|Any CPU {7138CDD4-EC75-4803-8050-D72A32B381CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
EndGlobalSection {7138CDD4-EC75-4803-8050-D72A32B381CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
GlobalSection(SolutionProperties) = preSolution {7138CDD4-EC75-4803-8050-D72A32B381CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
HideSolutionNode = FALSE {7138CDD4-EC75-4803-8050-D72A32B381CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection {55F5BDBE-025D-465B-BB34-2262369715BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
GlobalSection(ExtensibilityGlobals) = postSolution {55F5BDBE-025D-465B-BB34-2262369715BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
SolutionGuid = {73773200-50D4-40ED-803B-FC16B131FFD2} {55F5BDBE-025D-465B-BB34-2262369715BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection {55F5BDBE-025D-465B-BB34-2262369715BB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobal EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{55F5BDBE-025D-465B-BB34-2262369715BB} = {88B204F0-6636-4265-ACFB-52D889A56413}
EndGlobalSection
EndGlobal

View File

@ -1,51 +0,0 @@
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 红外告警器类,用于检测红外辐射并发出警报
/// </summary>
public class InfraredWarner : WarnerBase
{
/// <summary>
/// 红外告警器灵敏度阈值
/// </summary>
private readonly double sensitivityThreshold;
public InfraredWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, double warningDuration, double sensitivityThreshold)
: base(id, position, orientation, manager, tankId, warningDuration)
{
this.sensitivityThreshold = sensitivityThreshold;
SimulationManager.SubscribeToEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
private void OnInfraredDetection(InfraredDetectionEvent evt)
{
if (evt?.TargetId == TankId && evt.Intensity >= sensitivityThreshold)
{
StartWarning();
}
}
public override string GetWarningStatus()
{
return $"红外告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}";
}
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
}
}

View File

@ -1,160 +0,0 @@
using ActiveProtect.Simulation;
using System;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 激光告警器类,用于检测激光照射并发出警报
/// </summary>
public class LaserWarner : SimulationElement
{
/// <summary>
/// 是否正在发出警报
/// </summary>
public bool IsAlarming { get; private set; }
/// <summary>
/// 警报持续时间(秒)
/// </summary>
private readonly double alarmDuration;
/// <summary>
/// 当前警报计时器
/// </summary>
private double alarmTimer = 0;
/// <summary>
/// 被监视实体的ID
/// </summary>
public string MonitoredEntityId { get; private set; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="id">激光告警器ID</param>
/// <param name="position">初始位置</param>
/// <param name="orientation">初始朝向</param>
/// <param name="simulationManager">仿真管理器</param>
/// <param name="monitoredEntityId">被监视实体的ID</param>
/// <param name="config">激光告警器配置</param>
public LaserWarner(string id, Vector3D position, Orientation orientation, ISimulationManager simulationManager, string monitoredEntityId, LaserWarnerConfig config)
: base(id, position, orientation, 0, simulationManager)
{
MonitoredEntityId = monitoredEntityId;
IsAlarming = false;
alarmDuration = config.AlarmDuration;
base.SimulationManager.SubscribeToEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
base.SimulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
/// <summary>
/// 更新激光告警器状态
/// </summary>
/// <param name="deltaTime">时间步长</param>
public override void Update(double deltaTime)
{
if (!IsActive) return;
var tank = SimulationManager.GetEntityById(MonitoredEntityId) as Tank;
if (tank != null)
{
Position = tank.Position;
Orientation = tank.Orientation;
}
if (IsAlarming)
{
alarmTimer += deltaTime;
if (alarmTimer >= alarmDuration)
{
StopAlarm();
}
}
}
/// <summary>
/// 处理激光照射事件
/// </summary>
/// <param name="evt">激光照射事件</param>
private void OnLaserIlluminationStart(LaserIlluminationStartEvent evt)
{
if (evt?.Target != null && evt.Target.Id == MonitoredEntityId)
{
TriggerAlarm();
}
}
/// <summary>
/// 处理激光照射停止事件
/// </summary>
/// <param name="evt">激光照射停止事件</param>
private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
{
if (evt?.Target != null && evt.Target.Id == MonitoredEntityId)
{
StopAlarm();
}
}
/// <summary>
/// 触发警报
/// </summary>
public void TriggerAlarm()
{
if (!IsAlarming)
{
IsAlarming = true;
alarmTimer = 0;
PublishEvent(new LaserWarnerAlarmEvent() { TargetId = MonitoredEntityId });
Console.WriteLine($"激光告警器 {Id} 发出警报!");
}
}
/// <summary>
/// 停止警报
/// </summary>
public void StopAlarm()
{
if (IsAlarming)
{
IsAlarming = false;
alarmTimer = 0;
PublishEvent(new LaserWarnerAlarmStopEvent() { TargetId = MonitoredEntityId });
Console.WriteLine($"激光告警器 {Id} 停止警报");
}
}
/// <summary>
/// 获取激光告警器状态信息
/// </summary>
/// <returns>状态信息字符串</returns>
public override string GetStatus()
{
return $"激光告警器 {Id}:\n" +
$" 监视实体: {MonitoredEntityId}\n" +
$" 警报状态: {(IsAlarming ? "" : "")}\n" +
$" 警报持续时间: {(IsAlarming ? alarmTimer : 0):F2}/{alarmDuration:F2}";
}
/// <summary>
/// 激活激光告警器
/// </summary>
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
/// <summary>
/// 停用激光告警器
/// </summary>
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.UnsubscribeFromEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
}
}

View File

@ -1,62 +0,0 @@
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 毫米波告警器类,用于检测毫米波辐射并发出警报
/// </summary>
public class MillimeterWaveWarner : WarnerBase
{
/// <summary>
/// 毫米波告警器灵敏度阈值
/// </summary>
private readonly double sensitivityThreshold;
/// <summary>
/// 频率范围GHz
/// </summary>
private readonly (double Min, double Max) frequencyRange;
public MillimeterWaveWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, double warningDuration,
double sensitivityThreshold, (double Min, double Max) frequencyRange)
: base(id, position, orientation, manager, tankId, warningDuration)
{
this.sensitivityThreshold = sensitivityThreshold;
this.frequencyRange = frequencyRange;
SimulationManager.SubscribeToEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
private void OnMillimeterWaveDetection(MillimeterWaveDetectionEvent evt)
{
if (evt?.TargetId == TankId &&
evt.Intensity >= sensitivityThreshold &&
evt.Frequency >= frequencyRange.Min &&
evt.Frequency <= frequencyRange.Max)
{
StartWarning();
}
}
public override string GetWarningStatus()
{
return $"毫米波告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}\n" +
$" 频率范围: {frequencyRange.Min}-{frequencyRange.Max} GHz";
}
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
}
}

View File

@ -0,0 +1,71 @@
using System;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 红外告警器类,用于检测红外辐射并发出警报
/// </summary>
public class InfraredWarner : WarnerBase
{
private readonly double _sensitivityThreshold;
private readonly double _wavelengthMin;
private readonly double _wavelengthMax;
public InfraredWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, InfraredWarnerConfig config)
: base(id, position, orientation, manager, tankId, config.SensitivityThreshold,
(config.WavelengthMin, config.WavelengthMax), config.AlarmDuration)
{
_sensitivityThreshold = config.SensitivityThreshold;
_wavelengthMin = config.WavelengthMin;
_wavelengthMax = config.WavelengthMax;
SimulationManager.SubscribeToEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
private void OnInfraredDetection(InfraredDetectionEvent evt)
{
if (evt?.TargetId == TankId && evt.Intensity >= _sensitivityThreshold)
{
StartWarning();
}
}
public override string GetWarningStatus()
{
return $"红外告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}\n" +
$" 波长范围: {_wavelengthMin:F2}-{_wavelengthMax:F2}微米";
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (IsWarning && IsActive)
{
// 发布告警事件
SimulationManager.PublishEvent(new InfraredWarnerAlarmEvent
{
SenderId = Id,
Timestamp = SimulationManager.CurrentTime,
TargetId = TankId
});
}
}
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<InfraredDetectionEvent>(OnInfraredDetection);
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 激光告警器类,用于检测激光照射并发出警报
/// </summary>
public class LaserWarner : WarnerBase
{
/// <summary>
/// 构造函数
/// </summary>
public LaserWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, LaserWarnerConfig config)
: base(id, position, orientation, manager, tankId, config.SensitivityThreshold,
(config.WavelengthMin, config.WavelengthMax), config.AlarmDuration)
{
SimulationManager.SubscribeToEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
/// <summary>
/// 处理激光照射事件
/// </summary>
private void OnLaserIlluminationStart(LaserIlluminationStartEvent evt)
{
// 添加日志以帮助调试
Console.WriteLine($"收到激光照射事件: TargetId={evt?.Target?.Id}, ExpectedId={TankId}");
if (evt?.Target != null && evt.Target.Id == TankId)
{
Console.WriteLine("开始告警");
StartWarning();
}
}
/// <summary>
/// 处理激光照射停止事件
/// </summary>
private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
{
// 添加日志以帮助调试
Console.WriteLine($"收到激光照射停止事件: TargetId={evt?.Target?.Id}, ExpectedId={TankId}");
if (evt?.Target != null && evt.Target.Id == TankId)
{
Console.WriteLine("停止告警");
StopWarning();
}
}
/// <summary>
/// 获取告警状态信息
/// </summary>
public override string GetWarningStatus()
{
return $"激光告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}";
}
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.UnsubscribeFromEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (IsWarning)
{
// 发布告警事件
SimulationManager.PublishEvent(new LaserWarnerAlarmEvent
{
SenderId = Id,
Timestamp = SimulationManager.CurrentTime,
TargetId = TankId
});
}
}
}
}

View File

@ -0,0 +1,75 @@
using System;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Models
{
/// <summary>
/// 毫米波告警器类,用于检测毫米波辐射并发出警报
/// </summary>
public class MillimeterWaveWarner : WarnerBase
{
private readonly double _sensitivityThreshold;
private readonly double _wavelengthMin;
private readonly double _wavelengthMax;
public MillimeterWaveWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, MillimeterWaveWarnerConfig config)
: base(id, position, orientation, manager, tankId, config.SensitivityThreshold,
(config.WavelengthMin, config.WavelengthMax), config.AlarmDuration)
{
_sensitivityThreshold = config.SensitivityThreshold;
_wavelengthMin = config.WavelengthMin;
_wavelengthMax = config.WavelengthMax;
SimulationManager.SubscribeToEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
private void OnMillimeterWaveDetection(MillimeterWaveDetectionEvent evt)
{
if (evt?.TargetId == TankId &&
evt.Intensity >= _sensitivityThreshold &&
evt.Frequency >= _wavelengthMin &&
evt.Frequency <= _wavelengthMax)
{
StartWarning();
}
}
public override string GetWarningStatus()
{
return $"毫米波告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}\n" +
$" 频率范围: {_wavelengthMin:F2}-{_wavelengthMax:F2}GHz";
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (IsWarning && IsActive)
{
// 发布告警事件
SimulationManager.PublishEvent(new MillimeterWaveWarnerAlarmEvent
{
SenderId = Id,
Timestamp = SimulationManager.CurrentTime,
TargetId = TankId,
DetectedFrequency = (_wavelengthMin + _wavelengthMax) / 2 // 使用中心频率
});
}
}
public override void Activate()
{
base.Activate();
SimulationManager.SubscribeToEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
public override void Deactivate()
{
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<MillimeterWaveDetectionEvent>(OnMillimeterWaveDetection);
}
}
}

View File

@ -1,3 +1,4 @@
using System;
using ActiveProtect.Simulation; using ActiveProtect.Simulation;
using ActiveProtect.Utility; using ActiveProtect.Utility;
@ -8,22 +9,24 @@ namespace ActiveProtect.Models
/// </summary> /// </summary>
public class UltravioletWarner : WarnerBase public class UltravioletWarner : WarnerBase
{ {
/// <summary> private readonly double _sensitivityThreshold;
/// 紫外告警器灵敏度阈值 private readonly double _wavelengthMin;
/// </summary> private readonly double _wavelengthMax;
private readonly double sensitivityThreshold;
public UltravioletWarner(string id, Vector3D position, Orientation orientation, public UltravioletWarner(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, double warningDuration, double sensitivityThreshold) ISimulationManager manager, string tankId, UltravioletWarnerConfig config)
: base(id, position, orientation, manager, tankId, warningDuration) : base(id, position, orientation, manager, tankId, config.SensitivityThreshold,
(config.WavelengthMin, config.WavelengthMax), config.AlarmDuration)
{ {
this.sensitivityThreshold = sensitivityThreshold; _sensitivityThreshold = config.SensitivityThreshold;
_wavelengthMin = config.WavelengthMin;
_wavelengthMax = config.WavelengthMax;
SimulationManager.SubscribeToEvent<UltravioletDetectionEvent>(OnUltravioletDetection); SimulationManager.SubscribeToEvent<UltravioletDetectionEvent>(OnUltravioletDetection);
} }
private void OnUltravioletDetection(UltravioletDetectionEvent evt) private void OnUltravioletDetection(UltravioletDetectionEvent evt)
{ {
if (evt?.TargetId == TankId && evt.Intensity >= sensitivityThreshold) if (evt?.TargetId == TankId && evt.Intensity >= _sensitivityThreshold)
{ {
StartWarning(); StartWarning();
} }
@ -33,7 +36,24 @@ namespace ActiveProtect.Models
{ {
return $"紫外告警器 {Id}:\n" + return $"紫外告警器 {Id}:\n" +
$" 告警状态: {(IsWarning ? "" : "")}\n" + $" 告警状态: {(IsWarning ? "" : "")}\n" +
$" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}"; $" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}\n" +
$" 波长范围: {_wavelengthMin:F2}-{_wavelengthMax:F2}纳米";
}
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (IsWarning && IsActive)
{
// 发布告警事件
SimulationManager.PublishEvent(new UltravioletWarnerAlarmEvent
{
SenderId = Id,
Timestamp = SimulationManager.CurrentTime,
TargetId = TankId
});
}
} }
public override void Activate() public override void Activate()

View File

@ -14,6 +14,16 @@ namespace ActiveProtect.Models
/// </summary> /// </summary>
public bool IsWarning { get; protected set; } public bool IsWarning { get; protected set; }
/// <summary>
/// 告警灵敏度阈值
/// </summary>
protected double SensitivityThreshold { get; set; }
/// <summary>
/// 告警频率范围
/// </summary>
protected (double Min, double Max) FrequencyRange { get; set; }
/// <summary> /// <summary>
/// 告警持续时间(秒) /// 告警持续时间(秒)
/// </summary> /// </summary>
@ -30,10 +40,12 @@ namespace ActiveProtect.Models
protected string TankId { get; set; } protected string TankId { get; set; }
protected WarnerBase(string id, Vector3D position, Orientation orientation, protected WarnerBase(string id, Vector3D position, Orientation orientation,
ISimulationManager manager, string tankId, double warningDuration) ISimulationManager manager, string tankId, double sensitivityThreshold, (double Min, double Max) frequencyRange, double warningDuration)
: base(id, position, orientation, 0, manager) : base(id, position, orientation, 0, manager)
{ {
TankId = tankId; TankId = tankId;
SensitivityThreshold = sensitivityThreshold;
FrequencyRange = frequencyRange;
WarningDuration = warningDuration; WarningDuration = warningDuration;
CurrentWarningTime = 0; CurrentWarningTime = 0;
IsWarning = false; IsWarning = false;

View File

@ -165,18 +165,165 @@ namespace ActiveProtect.Simulation
/// </summary> /// </summary>
public string Id { get; set; } public string Id { get; set; }
/// <summary>
/// 告警灵敏度阈值
/// </summary>
public double SensitivityThreshold { get; set; }
/// <summary> /// <summary>
/// 警报持续时间(秒) /// 警报持续时间(秒)
/// </summary> /// </summary>
public double AlarmDuration { get; set; } public double AlarmDuration { get; set; }
/// <summary>
/// 最小波长(纳米)
/// </summary>
public double WavelengthMin { get; set; }
/// <summary>
/// 最大波长(纳米)
/// </summary>
public double WavelengthMax { get; set; }
/// <summary> /// <summary>
/// 构造函数,设置默认值 /// 构造函数,设置默认值
/// </summary> /// </summary>
public LaserWarnerConfig() public LaserWarnerConfig()
{ {
Id = ""; Id = "";
SensitivityThreshold = 0;
AlarmDuration = 5.0; // 默认警报持续5秒 AlarmDuration = 5.0; // 默认警报持续5秒
WavelengthMin = 0;
WavelengthMax = 0;
}
}
/// <summary>
/// 紫外线告警器配置类
/// </summary>
public class UltravioletWarnerConfig
{
/// <summary>
/// 紫外线告警器ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 告警灵敏度阈值
/// </summary>
public double SensitivityThreshold { get; set; }
/// <summary>
/// 警报持续时间(秒)
/// </summary>
public double AlarmDuration { get; set; }
/// <summary>
/// 波长范围(纳米)
/// </summary>
public double WavelengthMin { get; set; }
/// <summary>
/// 最大波长(纳米)
/// </summary>
public double WavelengthMax { get; set; }
/// <summary>
/// 构造函数,设置默认值
/// </summary>
public UltravioletWarnerConfig()
{
Id = "";
SensitivityThreshold = 0;
AlarmDuration = 5.0; // 默认警报持续5秒
WavelengthMin = 0;
WavelengthMax = 0;
}
}
/// <summary>
/// 红外告警器配置类
/// </summary>
public class InfraredWarnerConfig
{
/// <summary>
/// 红外告警器ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 告警灵敏度阈值
/// </summary>
public double SensitivityThreshold { get; set; }
/// <summary>
/// 警报持续时间(秒)
/// </summary>
public double AlarmDuration { get; set; }
/// <summary>
/// 最小波长(纳米)
/// </summary>
public double WavelengthMin { get; set; }
/// <summary>
/// 最大波长(纳米)
/// </summary>
public double WavelengthMax { get; set; }
/// <summary>
/// 构造函数,设置默认值
/// </summary>
public InfraredWarnerConfig()
{
Id = "";
SensitivityThreshold = 0;
AlarmDuration = 5.0; // 默认警报持续5秒
WavelengthMin = 0;
WavelengthMax = 0;
}
}
/// <summary>
/// 毫米波告警器配置类
/// </summary>
public class MillimeterWaveWarnerConfig
{
/// <summary>
/// 毫米波告警器ID
/// </summary>
public string Id { get; set; }
/// <summary>
/// 告警灵敏度阈值
/// </summary>
public double SensitivityThreshold { get; set; }
/// <summary>
/// 警报持续时间(秒)
/// </summary>
public double AlarmDuration { get; set; }
/// <summary>
/// 波长范围(纳米)
/// </summary>
public double WavelengthMin { get; set; }
/// <summary>
/// 最大波长(纳米)
/// </summary>
public double WavelengthMax { get; set; }
/// <summary>
/// 构造函数,设置默认值
/// </summary>
public MillimeterWaveWarnerConfig()
{
Id = "";
SensitivityThreshold = 0;
AlarmDuration = 5.0; // 默认警报持续5秒
WavelengthMin = 0;
WavelengthMax = 0;
} }
} }

View File

@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>9.0</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.2.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.18.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\ActiveProtect.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Remove="obj\**" />
<EmbeddedResource Remove="obj\**" />
<None Remove="obj\**" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,167 @@
using Xunit;
using Moq;
using System;
using System.Linq;
using ActiveProtect.Models;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Tests.Unit.Models.Jammer
{
public class InfraredJammerTests : IDisposable
{
private readonly Mock<ISimulationManager> _mockSimManager;
private readonly InfraredJammer _jammer;
private readonly string _jammerId = "TestJammer";
private readonly string _tankId = "TestTank";
public InfraredJammerTests()
{
// 设置测试环境
_mockSimManager = new Mock<ISimulationManager>();
_mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
// 设置事件发布
_mockSimManager.Setup(m => m.PublishEvent(It.IsAny<SimulationEvent>()));
_jammer = new InfraredJammer(
_jammerId,
new Vector3D(0, 0, 0),
new Orientation(),
_mockSimManager.Object,
_tankId,
maxJammingPower: 1000.0,
initialJammingPower: 400.0,
powerIncreaseRate: 100.0,
maxCooldownTime: 5.0,
wavelengthRange: (3.0, 5.0)
);
}
public void Dispose()
{
// 清理资源
_jammer.Deactivate();
}
[Fact]
public void Constructor_InitializesCorrectly()
{
Assert.Equal(_jammerId, _jammer.Id);
Assert.False(_jammer.IsJamming);
Assert.Equal(400.0, _jammer.CurrentJammingPower);
}
[Fact]
public void StartJamming_WhenNotJamming_StartsJamming()
{
// Act
_jammer.StartJamming();
// Assert
Assert.True(_jammer.IsJamming);
}
[Fact]
public void StopJamming_WhenJamming_StopsAndEntersCooldown()
{
// Arrange
_jammer.StartJamming();
// Act
_jammer.StopJamming();
// Assert
Assert.False(_jammer.IsJamming);
Assert.Equal(400.0, _jammer.CurrentJammingPower); // 重置为初始功率
}
[Theory]
[InlineData(1.0, 500.0)] // 1秒后功率增加100
[InlineData(2.0, 600.0)] // 2秒后功率增加200
[InlineData(10.0, 1000.0)] // 10秒后达到最大功率1000
public void Update_WhenJamming_IncreasesPowerCorrectly(double deltaTime, double expectedPower)
{
// Arrange
_jammer.StartJamming();
_jammer.Activate(); // 确保激活状态
// Act
_jammer.Update(deltaTime);
// Assert
Assert.Equal(expectedPower, _jammer.CurrentJammingPower, 1); // 允许1瓦特的误差
}
[Fact]
public void OnInfraredWarnerAlarm_StartsJamming()
{
// Arrange
Action<InfraredWarnerAlarmEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<InfraredWarnerAlarmEvent>>()))
.Callback<Action<InfraredWarnerAlarmEvent>>(handler => eventHandler = handler);
// 激活干扰器以触发事件订阅
_jammer.Activate();
// 确保事件处理程序被设置
Assert.NotNull(eventHandler);
// Act
eventHandler?.Invoke(new InfraredWarnerAlarmEvent { TargetId = _tankId });
// Assert
Assert.True(_jammer.IsJamming);
}
[Fact]
public void Update_PublishesJammingEvent()
{
// Arrange
_jammer.StartJamming();
_jammer.Activate();
// Act
_jammer.Update(1.0);
// Assert
_mockSimManager.Verify(m => m.PublishEvent(It.Is<InfraredJammingEvent>(e =>
e.TargetId == _tankId &&
Math.Abs(e.JammingPower - 500.0) < 1 &&
e.WavelengthMin == 3.0 &&
e.WavelengthMax == 5.0
)), Times.Once);
}
[Fact]
public void GetJammingStatus_ReturnsCorrectFormat()
{
// Arrange
_jammer.StartJamming();
// Act
string status = _jammer.GetJammingStatus();
// Assert
Assert.Contains("红外干扰器", status);
Assert.Contains("干扰中", status);
Assert.Contains("400.00/1000.00", status);
Assert.Contains("3.00-5.00微米", status);
}
[Fact]
public void Deactivate_UnsubscribesFromEvents()
{
// Act
_jammer.Deactivate();
// Assert
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<InfraredWarnerAlarmEvent>(It.IsAny<Action<InfraredWarnerAlarmEvent>>()),
Times.Once);
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<InfraredWarnerAlarmStopEvent>(It.IsAny<Action<InfraredWarnerAlarmStopEvent>>()),
Times.Once);
}
}
}

View File

@ -0,0 +1,161 @@
using Xunit;
using Moq;
using System;
using ActiveProtect.Models;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Tests.Unit.Models.Warner
{
public class InfraredWarnerTests : IDisposable
{
private readonly Mock<ISimulationManager> _mockSimManager;
private readonly InfraredWarner _warner;
private readonly string _warnerId = "TestInfraredWarner";
private readonly string _tankId = "TestTank";
private readonly InfraredWarnerConfig _config;
public InfraredWarnerTests()
{
_mockSimManager = new Mock<ISimulationManager>();
_mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
_mockSimManager.Setup(m => m.PublishEvent(It.IsAny<SimulationEvent>()));
_config = new InfraredWarnerConfig
{
AlarmDuration = 5.0,
SensitivityThreshold = 0.1,
WavelengthMin = 3.0,
WavelengthMax = 5.0
};
_warner = new InfraredWarner(
_warnerId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
_mockSimManager.Object,
_tankId,
_config
);
}
public void Dispose()
{
_warner.Deactivate();
}
[Fact]
public void Constructor_InitializesCorrectly()
{
Assert.Equal(_warnerId, _warner.Id);
Assert.False(_warner.IsWarning);
}
[Fact]
public void StartWarning_WhenNotWarning_StartsWarning()
{
// Act
_warner.StartWarning();
// Assert
Assert.True(_warner.IsWarning);
}
[Fact]
public void StopWarning_WhenWarning_StopsWarning()
{
// Arrange
_warner.StartWarning();
// Act
_warner.StopWarning();
// Assert
Assert.False(_warner.IsWarning);
}
[Fact]
public void Update_WhenWarningTimeExceeded_StopsWarning()
{
// Arrange
_warner.Activate();
_warner.StartWarning();
// Act
_warner.Update(6.0); // 超过警报持续时间
// Assert
Assert.False(_warner.IsWarning);
}
[Theory]
[InlineData(0.2, true)] // 高于阈值,应该触发
[InlineData(0.05, false)] // 低于阈值,不应该触发
public void OnInfraredDetection_RespondsToIntensityThreshold(double intensity, bool shouldTrigger)
{
// Arrange
Action<InfraredDetectionEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<InfraredDetectionEvent>>()))
.Callback<Action<InfraredDetectionEvent>>(handler => eventHandler = handler);
_warner.Activate();
Assert.NotNull(eventHandler);
var detectionEvent = new InfraredDetectionEvent
{
TargetId = _tankId,
Intensity = intensity
};
// Act
eventHandler?.Invoke(detectionEvent);
// Assert
Assert.Equal(shouldTrigger, _warner.IsWarning);
}
[Fact]
public void GetWarningStatus_ReturnsCorrectFormat()
{
// Arrange
_warner.StartWarning();
// Act
string status = _warner.GetWarningStatus();
// Assert
Assert.Contains("红外告警器", status);
Assert.Contains("告警中", status);
Assert.Contains("3.00-5.00微米", status);
}
[Fact]
public void Update_PublishesWarningEvent_WhenWarning()
{
// Arrange
_warner.StartWarning();
_warner.Activate();
// Act
_warner.Update(1.0);
// Assert
_mockSimManager.Verify(m => m.PublishEvent(It.Is<InfraredWarnerAlarmEvent>(e =>
e.TargetId == _tankId
)), Times.Once);
}
[Fact]
public void Deactivate_UnsubscribesFromEvents()
{
// Act
_warner.Deactivate();
// Assert
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<InfraredDetectionEvent>(
It.IsAny<Action<InfraredDetectionEvent>>()),
Times.Once);
}
}
}

View File

@ -0,0 +1,204 @@
using Xunit;
using Moq;
using System;
using ActiveProtect.Models;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Tests.Unit.Models.Warner
{
public class LaserWarnerTests : IDisposable
{
private readonly Mock<ISimulationManager> _mockSimManager;
private readonly LaserWarner _warner;
private readonly string _warnerId = "TestLaserWarner";
private readonly string _tankId = "TestTank";
private readonly LaserWarnerConfig _config;
public LaserWarnerTests()
{
_mockSimManager = new Mock<ISimulationManager>();
_mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
_mockSimManager.Setup(m => m.PublishEvent(It.IsAny<SimulationEvent>()));
_config = new LaserWarnerConfig
{
AlarmDuration = 5.0,
};
_warner = new LaserWarner(
_warnerId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
_mockSimManager.Object,
_tankId,
_config
);
}
public void Dispose()
{
_warner.Deactivate();
}
[Fact]
public void Constructor_InitializesCorrectly()
{
Assert.Equal(_warnerId, _warner.Id);
Assert.False(_warner.IsWarning);
}
[Fact]
public void StartWarning_WhenNotWarning_StartsWarning()
{
// Act
_warner.StartWarning();
// Assert
Assert.True(_warner.IsWarning);
}
[Fact]
public void StopWarning_WhenWarning_StopsWarning()
{
// Arrange
_warner.StartWarning();
// Act
_warner.StopWarning();
// Assert
Assert.False(_warner.IsWarning);
}
[Fact]
public void Update_WhenWarningTimeExceeded_StopsWarning()
{
// Arrange
_warner.Activate();
_warner.StartWarning();
// Act
_warner.Update(6.0); // 超过警报持续时间
// Assert
Assert.False(_warner.IsWarning);
}
[Fact]
public void OnLaserIllumination_StartsWarning()
{
// Arrange
Action<LaserIlluminationStartEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<LaserIlluminationStartEvent>>()))
.Callback<Action<LaserIlluminationStartEvent>>(handler => eventHandler = handler);
_warner.Activate();
Assert.NotNull(eventHandler);
// 创建一个模拟的目标
var mockTarget = new Mock<SimulationElement>(_tankId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
0,
_mockSimManager.Object);
// 设置模拟对象的 Id 属性
mockTarget.Setup(t => t.Id).Returns(_tankId);
var illuminationEvent = new LaserIlluminationStartEvent
{
Target = mockTarget.Object,
SenderId = "TestLaser" // 添加发送者ID
};
// Act
eventHandler?.Invoke(illuminationEvent);
// Assert
Assert.True(_warner.IsWarning);
}
[Fact]
public void OnLaserIlluminationStop_StopsWarning()
{
// Arrange
Action<LaserIlluminationStopEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<LaserIlluminationStopEvent>>()))
.Callback<Action<LaserIlluminationStopEvent>>(handler => eventHandler = handler);
_warner.Activate();
_warner.StartWarning();
Assert.NotNull(eventHandler);
// 创建一个模拟的目标
var mockTarget = new Mock<SimulationElement>(_tankId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
0,
_mockSimManager.Object);
// 设置模拟对象的 Id 属性
mockTarget.Setup(t => t.Id).Returns(_tankId);
var illuminationStopEvent = new LaserIlluminationStopEvent
{
Target = mockTarget.Object,
SenderId = "TestLaser" // 添加发送者ID
};
// Act
eventHandler?.Invoke(illuminationStopEvent);
// Assert
Assert.False(_warner.IsWarning);
}
[Fact]
public void GetWarningStatus_ReturnsCorrectFormat()
{
// Arrange
_warner.StartWarning();
// Act
string status = _warner.GetWarningStatus();
// Assert
Assert.Contains("激光告警器", status);
Assert.Contains("告警中", status);
}
[Fact]
public void Update_PublishesWarningEvent_WhenWarning()
{
// Arrange
_warner.StartWarning();
_warner.Activate();
// Act
_warner.Update(1.0);
// Assert
_mockSimManager.Verify(m => m.PublishEvent(It.Is<LaserWarnerAlarmEvent>(e =>
e.TargetId == _tankId
)), Times.Once);
}
[Fact]
public void Deactivate_UnsubscribesFromEvents()
{
// Act
_warner.Deactivate();
// Assert
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<LaserIlluminationStartEvent>(
It.IsAny<Action<LaserIlluminationStartEvent>>()),
Times.Once);
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<LaserIlluminationStopEvent>(
It.IsAny<Action<LaserIlluminationStopEvent>>()),
Times.Once);
}
}
}

View File

@ -0,0 +1,150 @@
using Xunit;
using Moq;
using System;
using ActiveProtect.Models;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Tests.Unit.Models.Warner
{
public class MillimeterWaveWarnerTests : IDisposable
{
private readonly Mock<ISimulationManager> _mockSimManager;
private readonly MillimeterWaveWarner _warner;
private readonly string _warnerId = "TestMMWWarner";
private readonly string _tankId = "TestTank";
private readonly MillimeterWaveWarnerConfig _config;
public MillimeterWaveWarnerTests()
{
_mockSimManager = new Mock<ISimulationManager>();
_mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
_mockSimManager.Setup(m => m.PublishEvent(It.IsAny<SimulationEvent>()));
_config = new MillimeterWaveWarnerConfig
{
AlarmDuration = 5.0,
SensitivityThreshold = 0.1,
WavelengthMin = 30.0, // 30 GHz
WavelengthMax = 300.0 // 300 GHz
};
_warner = new MillimeterWaveWarner(
_warnerId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
_mockSimManager.Object,
_tankId,
_config
);
}
public void Dispose()
{
_warner.Deactivate();
}
[Fact]
public void Constructor_InitializesCorrectly()
{
Assert.Equal(_warnerId, _warner.Id);
Assert.False(_warner.IsWarning);
}
[Fact]
public void StartWarning_WhenNotWarning_StartsWarning()
{
// Act
_warner.StartWarning();
// Assert
Assert.True(_warner.IsWarning);
}
[Fact]
public void StopWarning_WhenWarning_StopsWarning()
{
// Arrange
_warner.StartWarning();
// Act
_warner.StopWarning();
// Assert
Assert.False(_warner.IsWarning);
}
[Theory]
[InlineData(0.2, 100.0, true)] // 高于阈值,频率在范围内
[InlineData(0.05, 100.0, false)] // 低于阈值,频率在范围内
[InlineData(0.2, 400.0, false)] // 高于阈值,频率超出范围
public void OnMillimeterWaveDetection_RespondsToThresholds(
double intensity, double frequency, bool shouldTrigger)
{
// Arrange
Action<MillimeterWaveDetectionEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<MillimeterWaveDetectionEvent>>()))
.Callback<Action<MillimeterWaveDetectionEvent>>(handler => eventHandler = handler);
_warner.Activate();
Assert.NotNull(eventHandler);
var detectionEvent = new MillimeterWaveDetectionEvent
{
TargetId = _tankId,
Intensity = intensity,
Frequency = frequency
};
// Act
eventHandler?.Invoke(detectionEvent);
// Assert
Assert.Equal(shouldTrigger, _warner.IsWarning);
}
[Fact]
public void GetWarningStatus_ReturnsCorrectFormat()
{
// Arrange
_warner.StartWarning();
// Act
string status = _warner.GetWarningStatus();
// Assert
Assert.Contains("毫米波告警器", status);
Assert.Contains("告警中", status);
Assert.Contains("30.00-300.00GHz", status);
}
[Fact]
public void Update_PublishesWarningEvent_WhenWarning()
{
// Arrange
_warner.StartWarning();
_warner.Activate();
// Act
_warner.Update(1.0);
// Assert
_mockSimManager.Verify(m => m.PublishEvent(It.Is<MillimeterWaveWarnerAlarmEvent>(e =>
e.TargetId == _tankId
)), Times.Once);
}
[Fact]
public void Deactivate_UnsubscribesFromEvents()
{
// Act
_warner.Deactivate();
// Assert
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<MillimeterWaveDetectionEvent>(
It.IsAny<Action<MillimeterWaveDetectionEvent>>()),
Times.Once);
}
}
}

View File

@ -0,0 +1,161 @@
using Xunit;
using Moq;
using System;
using ActiveProtect.Models;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
namespace ActiveProtect.Tests.Unit.Models.Warner
{
public class UltravioletWarnerTests : IDisposable
{
private readonly Mock<ISimulationManager> _mockSimManager;
private readonly UltravioletWarner _warner;
private readonly string _warnerId = "TestUVWarner";
private readonly string _tankId = "TestTank";
private readonly UltravioletWarnerConfig _config;
public UltravioletWarnerTests()
{
_mockSimManager = new Mock<ISimulationManager>();
_mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
_mockSimManager.Setup(m => m.PublishEvent(It.IsAny<SimulationEvent>()));
_config = new UltravioletWarnerConfig
{
AlarmDuration = 5.0,
SensitivityThreshold = 0.1,
WavelengthMin = 200.0,
WavelengthMax = 280.0
};
_warner = new UltravioletWarner(
_warnerId,
new Vector3D(0, 0, 0),
new Orientation(0, 0, 0),
_mockSimManager.Object,
_tankId,
_config
);
}
public void Dispose()
{
_warner.Deactivate();
}
[Fact]
public void Constructor_InitializesCorrectly()
{
Assert.Equal(_warnerId, _warner.Id);
Assert.False(_warner.IsWarning);
}
[Fact]
public void StartWarning_WhenNotWarning_StartsWarning()
{
// Act
_warner.StartWarning();
// Assert
Assert.True(_warner.IsWarning);
}
[Fact]
public void StopWarning_WhenWarning_StopsWarning()
{
// Arrange
_warner.StartWarning();
// Act
_warner.StopWarning();
// Assert
Assert.False(_warner.IsWarning);
}
[Fact]
public void Update_WhenWarningTimeExceeded_StopsWarning()
{
// Arrange
_warner.Activate();
_warner.StartWarning();
// Act
_warner.Update(6.0); // 超过警报持续时间
// Assert
Assert.False(_warner.IsWarning);
}
[Theory]
[InlineData(0.2, true)] // 高于阈值,应该触发
[InlineData(0.05, false)] // 低于阈值,不应该触发
public void OnUltravioletDetection_RespondsToIntensityThreshold(double intensity, bool shouldTrigger)
{
// Arrange
Action<UltravioletDetectionEvent>? eventHandler = null;
_mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny<Action<UltravioletDetectionEvent>>()))
.Callback<Action<UltravioletDetectionEvent>>(handler => eventHandler = handler);
_warner.Activate();
Assert.NotNull(eventHandler);
var detectionEvent = new UltravioletDetectionEvent
{
TargetId = _tankId,
Intensity = intensity
};
// Act
eventHandler?.Invoke(detectionEvent);
// Assert
Assert.Equal(shouldTrigger, _warner.IsWarning);
}
[Fact]
public void GetWarningStatus_ReturnsCorrectFormat()
{
// Arrange
_warner.StartWarning();
// Act
string status = _warner.GetWarningStatus();
// Assert
Assert.Contains("紫外告警器", status);
Assert.Contains("告警中", status);
Assert.Contains("200.00-280.00纳米", status);
}
[Fact]
public void Update_PublishesWarningEvent_WhenWarning()
{
// Arrange
_warner.StartWarning();
_warner.Activate();
// Act
_warner.Update(1.0);
// Assert
_mockSimManager.Verify(m => m.PublishEvent(It.Is<UltravioletWarnerAlarmEvent>(e =>
e.TargetId == _tankId
)), Times.Once);
}
[Fact]
public void Deactivate_UnsubscribesFromEvents()
{
// Act
_warner.Deactivate();
// Assert
_mockSimManager.Verify(
m => m.UnsubscribeFromEvent<UltravioletDetectionEvent>(
It.IsAny<Action<UltravioletDetectionEvent>>()),
Times.Once);
}
}
}