diff --git a/ActiveProtect.csproj b/ActiveProtect.csproj
index d579445..08dcf2c 100644
--- a/ActiveProtect.csproj
+++ b/ActiveProtect.csproj
@@ -4,8 +4,19 @@
Exe
net6.0
9.0
- disable
+ disable
enable
+ false
+ false
+
+
+
+
+
+
+
+
+
diff --git a/ActiveProtect.sln b/ActiveProtect.sln
index 99af9fe..98e9ec4 100644
--- a/ActiveProtect.sln
+++ b/ActiveProtect.sln
@@ -1,29 +1,33 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.5.002.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ActiveProtect", "ActiveProtect.csproj", "{7F47A2C7-1087-440B-82F7-A1BF50D30A1B}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {7F47A2C7-1087-440B-82F7-A1BF50D30A1B}.Release|Any CPU.Build.0 = Release|Any CPU
- {644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {644AFA6F-7DEE-4438-85C3-1D380B052E3D}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {73773200-50D4-40ED-803B-FC16B131FFD2}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.0.31903.59
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveProtect", "ActiveProtect.csproj", "{7138CDD4-EC75-4803-8050-D72A32B381CA}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{88B204F0-6636-4265-ACFB-52D889A56413}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActiveProtect.Tests", "src\Tests\ActiveProtect.Tests.csproj", "{55F5BDBE-025D-465B-BB34-2262369715BB}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {7138CDD4-EC75-4803-8050-D72A32B381CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7138CDD4-EC75-4803-8050-D72A32B381CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7138CDD4-EC75-4803-8050-D72A32B381CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7138CDD4-EC75-4803-8050-D72A32B381CA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {55F5BDBE-025D-465B-BB34-2262369715BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {55F5BDBE-025D-465B-BB34-2262369715BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {55F5BDBE-025D-465B-BB34-2262369715BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {55F5BDBE-025D-465B-BB34-2262369715BB}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {55F5BDBE-025D-465B-BB34-2262369715BB} = {88B204F0-6636-4265-ACFB-52D889A56413}
+ EndGlobalSection
+EndGlobal
diff --git a/src/Models/Wanner/InfraredWarner.cs b/src/Models/Wanner/InfraredWarner.cs
deleted file mode 100644
index d4ab7e9..0000000
--- a/src/Models/Wanner/InfraredWarner.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using ActiveProtect.Simulation;
-using ActiveProtect.Utility;
-
-namespace ActiveProtect.Models
-{
- ///
- /// 红外告警器类,用于检测红外辐射并发出警报
- ///
- public class InfraredWarner : WarnerBase
- {
- ///
- /// 红外告警器灵敏度阈值
- ///
- 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(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(OnInfraredDetection);
- }
-
- public override void Deactivate()
- {
- base.Deactivate();
- SimulationManager.UnsubscribeFromEvent(OnInfraredDetection);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Models/Wanner/LaserWarner.cs b/src/Models/Wanner/LaserWarner.cs
deleted file mode 100644
index e73f99b..0000000
--- a/src/Models/Wanner/LaserWarner.cs
+++ /dev/null
@@ -1,160 +0,0 @@
-using ActiveProtect.Simulation;
-using System;
-using ActiveProtect.Utility;
-
-namespace ActiveProtect.Models
-{
- ///
- /// 激光告警器类,用于检测激光照射并发出警报
- ///
- public class LaserWarner : SimulationElement
- {
- ///
- /// 是否正在发出警报
- ///
- public bool IsAlarming { get; private set; }
-
- ///
- /// 警报持续时间(秒)
- ///
- private readonly double alarmDuration;
-
- ///
- /// 当前警报计时器
- ///
- private double alarmTimer = 0;
-
- ///
- /// 被监视实体的ID
- ///
- public string MonitoredEntityId { get; private set; }
-
- ///
- /// 构造函数
- ///
- /// 激光告警器ID
- /// 初始位置
- /// 初始朝向
- /// 仿真管理器
- /// 被监视实体的ID
- /// 激光告警器配置
- 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(OnLaserIlluminationStart);
- base.SimulationManager.SubscribeToEvent(OnLaserIlluminationStop);
- }
-
- ///
- /// 更新激光告警器状态
- ///
- /// 时间步长
- 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();
- }
- }
- }
-
- ///
- /// 处理激光照射事件
- ///
- /// 激光照射事件
- private void OnLaserIlluminationStart(LaserIlluminationStartEvent evt)
- {
- if (evt?.Target != null && evt.Target.Id == MonitoredEntityId)
- {
- TriggerAlarm();
- }
- }
-
- ///
- /// 处理激光照射停止事件
- ///
- /// 激光照射停止事件
- private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
- {
- if (evt?.Target != null && evt.Target.Id == MonitoredEntityId)
- {
- StopAlarm();
- }
- }
-
- ///
- /// 触发警报
- ///
- public void TriggerAlarm()
- {
- if (!IsAlarming)
- {
- IsAlarming = true;
- alarmTimer = 0;
- PublishEvent(new LaserWarnerAlarmEvent() { TargetId = MonitoredEntityId });
- Console.WriteLine($"激光告警器 {Id} 发出警报!");
- }
- }
-
- ///
- /// 停止警报
- ///
- public void StopAlarm()
- {
- if (IsAlarming)
- {
- IsAlarming = false;
- alarmTimer = 0;
- PublishEvent(new LaserWarnerAlarmStopEvent() { TargetId = MonitoredEntityId });
- Console.WriteLine($"激光告警器 {Id} 停止警报");
- }
- }
-
- ///
- /// 获取激光告警器状态信息
- ///
- /// 状态信息字符串
- public override string GetStatus()
- {
- return $"激光告警器 {Id}:\n" +
- $" 监视实体: {MonitoredEntityId}\n" +
- $" 警报状态: {(IsAlarming ? "警报中" : "正常")}\n" +
- $" 警报持续时间: {(IsAlarming ? alarmTimer : 0):F2}/{alarmDuration:F2}";
- }
-
- ///
- /// 激活激光告警器
- ///
- public override void Activate()
- {
- base.Activate();
- SimulationManager.SubscribeToEvent(OnLaserIlluminationStart);
- SimulationManager.SubscribeToEvent(OnLaserIlluminationStop);
- }
-
- ///
- /// 停用激光告警器
- ///
- public override void Deactivate()
- {
- base.Deactivate();
- SimulationManager.UnsubscribeFromEvent(OnLaserIlluminationStart);
- SimulationManager.UnsubscribeFromEvent(OnLaserIlluminationStop);
- }
- }
-}
diff --git a/src/Models/Wanner/MillimeterWaveWarner.cs b/src/Models/Wanner/MillimeterWaveWarner.cs
deleted file mode 100644
index e8b65f3..0000000
--- a/src/Models/Wanner/MillimeterWaveWarner.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using ActiveProtect.Simulation;
-using ActiveProtect.Utility;
-
-namespace ActiveProtect.Models
-{
- ///
- /// 毫米波告警器类,用于检测毫米波辐射并发出警报
- ///
- public class MillimeterWaveWarner : WarnerBase
- {
- ///
- /// 毫米波告警器灵敏度阈值
- ///
- private readonly double sensitivityThreshold;
-
- ///
- /// 频率范围(GHz)
- ///
- 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(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(OnMillimeterWaveDetection);
- }
-
- public override void Deactivate()
- {
- base.Deactivate();
- SimulationManager.UnsubscribeFromEvent(OnMillimeterWaveDetection);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Models/Wanner/IWarner.cs b/src/Models/Warner/IWarner.cs
similarity index 100%
rename from src/Models/Wanner/IWarner.cs
rename to src/Models/Warner/IWarner.cs
diff --git a/src/Models/Warner/InfraredWarner.cs b/src/Models/Warner/InfraredWarner.cs
new file mode 100644
index 0000000..298d80c
--- /dev/null
+++ b/src/Models/Warner/InfraredWarner.cs
@@ -0,0 +1,71 @@
+using System;
+using ActiveProtect.Simulation;
+using ActiveProtect.Utility;
+
+namespace ActiveProtect.Models
+{
+ ///
+ /// 红外告警器类,用于检测红外辐射并发出警报
+ ///
+ 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(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(OnInfraredDetection);
+ }
+
+ public override void Deactivate()
+ {
+ base.Deactivate();
+ SimulationManager.UnsubscribeFromEvent(OnInfraredDetection);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Models/Warner/LaserWarner.cs b/src/Models/Warner/LaserWarner.cs
new file mode 100644
index 0000000..90fa9eb
--- /dev/null
+++ b/src/Models/Warner/LaserWarner.cs
@@ -0,0 +1,94 @@
+using System;
+using ActiveProtect.Simulation;
+using ActiveProtect.Utility;
+
+namespace ActiveProtect.Models
+{
+ ///
+ /// 激光告警器类,用于检测激光照射并发出警报
+ ///
+ public class LaserWarner : WarnerBase
+ {
+ ///
+ /// 构造函数
+ ///
+ 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(OnLaserIlluminationStart);
+ SimulationManager.SubscribeToEvent(OnLaserIlluminationStop);
+ }
+
+ ///
+ /// 处理激光照射事件
+ ///
+ private void OnLaserIlluminationStart(LaserIlluminationStartEvent evt)
+ {
+ // 添加日志以帮助调试
+ Console.WriteLine($"收到激光照射事件: TargetId={evt?.Target?.Id}, ExpectedId={TankId}");
+
+ if (evt?.Target != null && evt.Target.Id == TankId)
+ {
+ Console.WriteLine("开始告警");
+ StartWarning();
+ }
+ }
+
+ ///
+ /// 处理激光照射停止事件
+ ///
+ private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
+ {
+ // 添加日志以帮助调试
+ Console.WriteLine($"收到激光照射停止事件: TargetId={evt?.Target?.Id}, ExpectedId={TankId}");
+
+ if (evt?.Target != null && evt.Target.Id == TankId)
+ {
+ Console.WriteLine("停止告警");
+ StopWarning();
+ }
+ }
+
+ ///
+ /// 获取告警状态信息
+ ///
+ public override string GetWarningStatus()
+ {
+ return $"激光告警器 {Id}:\n" +
+ $" 告警状态: {(IsWarning ? "告警中" : "正常")}\n" +
+ $" 告警持续时间: {CurrentWarningTime:F2}/{WarningDuration:F2}";
+ }
+
+ public override void Activate()
+ {
+ base.Activate();
+ SimulationManager.SubscribeToEvent(OnLaserIlluminationStart);
+ SimulationManager.SubscribeToEvent(OnLaserIlluminationStop);
+ }
+
+ public override void Deactivate()
+ {
+ base.Deactivate();
+ SimulationManager.UnsubscribeFromEvent(OnLaserIlluminationStart);
+ SimulationManager.UnsubscribeFromEvent(OnLaserIlluminationStop);
+ }
+
+ public override void Update(double deltaTime)
+ {
+ base.Update(deltaTime);
+
+ if (IsWarning)
+ {
+ // 发布告警事件
+ SimulationManager.PublishEvent(new LaserWarnerAlarmEvent
+ {
+ SenderId = Id,
+ Timestamp = SimulationManager.CurrentTime,
+ TargetId = TankId
+ });
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Models/Warner/MillimeterWaveWarner.cs b/src/Models/Warner/MillimeterWaveWarner.cs
new file mode 100644
index 0000000..410ab2c
--- /dev/null
+++ b/src/Models/Warner/MillimeterWaveWarner.cs
@@ -0,0 +1,75 @@
+using System;
+using ActiveProtect.Simulation;
+using ActiveProtect.Utility;
+
+namespace ActiveProtect.Models
+{
+ ///
+ /// 毫米波告警器类,用于检测毫米波辐射并发出警报
+ ///
+ 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(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(OnMillimeterWaveDetection);
+ }
+
+ public override void Deactivate()
+ {
+ base.Deactivate();
+ SimulationManager.UnsubscribeFromEvent(OnMillimeterWaveDetection);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Models/Wanner/UltravioletWarner.cs b/src/Models/Warner/UltravioletWarner.cs
similarity index 50%
rename from src/Models/Wanner/UltravioletWarner.cs
rename to src/Models/Warner/UltravioletWarner.cs
index 4d169f3..f79945f 100644
--- a/src/Models/Wanner/UltravioletWarner.cs
+++ b/src/Models/Warner/UltravioletWarner.cs
@@ -1,3 +1,4 @@
+using System;
using ActiveProtect.Simulation;
using ActiveProtect.Utility;
@@ -8,22 +9,24 @@ namespace ActiveProtect.Models
///
public class UltravioletWarner : WarnerBase
{
- ///
- /// 紫外告警器灵敏度阈值
- ///
- private readonly double sensitivityThreshold;
+ private readonly double _sensitivityThreshold;
+ private readonly double _wavelengthMin;
+ private readonly double _wavelengthMax;
public UltravioletWarner(string id, Vector3D position, Orientation orientation,
- ISimulationManager manager, string tankId, double warningDuration, double sensitivityThreshold)
- : base(id, position, orientation, manager, tankId, warningDuration)
+ ISimulationManager manager, string tankId, UltravioletWarnerConfig config)
+ : 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(OnUltravioletDetection);
}
private void OnUltravioletDetection(UltravioletDetectionEvent evt)
{
- if (evt?.TargetId == TankId && evt.Intensity >= sensitivityThreshold)
+ if (evt?.TargetId == TankId && evt.Intensity >= _sensitivityThreshold)
{
StartWarning();
}
@@ -33,7 +36,24 @@ namespace ActiveProtect.Models
{
return $"紫外告警器 {Id}:\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()
diff --git a/src/Models/Wanner/WarnerBase.cs b/src/Models/Warner/WarnerBase.cs
similarity index 82%
rename from src/Models/Wanner/WarnerBase.cs
rename to src/Models/Warner/WarnerBase.cs
index 2e21a4c..093fdbf 100644
--- a/src/Models/Wanner/WarnerBase.cs
+++ b/src/Models/Warner/WarnerBase.cs
@@ -14,6 +14,16 @@ namespace ActiveProtect.Models
///
public bool IsWarning { get; protected set; }
+ ///
+ /// 告警灵敏度阈值
+ ///
+ protected double SensitivityThreshold { get; set; }
+
+ ///
+ /// 告警频率范围
+ ///
+ protected (double Min, double Max) FrequencyRange { get; set; }
+
///
/// 告警持续时间(秒)
///
@@ -30,10 +40,12 @@ namespace ActiveProtect.Models
protected string TankId { get; set; }
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)
{
TankId = tankId;
+ SensitivityThreshold = sensitivityThreshold;
+ FrequencyRange = frequencyRange;
WarningDuration = warningDuration;
CurrentWarningTime = 0;
IsWarning = false;
diff --git a/src/Simulation/SimulationConfig.cs b/src/Simulation/SimulationConfig.cs
index 754502c..e924cff 100644
--- a/src/Simulation/SimulationConfig.cs
+++ b/src/Simulation/SimulationConfig.cs
@@ -165,18 +165,165 @@ namespace ActiveProtect.Simulation
///
public string Id { get; set; }
+ ///
+ /// 告警灵敏度阈值
+ ///
+ public double SensitivityThreshold { get; set; }
+
///
/// 警报持续时间(秒)
///
public double AlarmDuration { get; set; }
+ ///
+ /// 最小波长(纳米)
+ ///
+ public double WavelengthMin { get; set; }
+
+ ///
+ /// 最大波长(纳米)
+ ///
+ public double WavelengthMax { get; set; }
+
///
/// 构造函数,设置默认值
///
public LaserWarnerConfig()
{
Id = "";
+ SensitivityThreshold = 0;
AlarmDuration = 5.0; // 默认警报持续5秒
+ WavelengthMin = 0;
+ WavelengthMax = 0;
+ }
+ }
+
+ ///
+ /// 紫外线告警器配置类
+ ///
+ public class UltravioletWarnerConfig
+ {
+ ///
+ /// 紫外线告警器ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 告警灵敏度阈值
+ ///
+ public double SensitivityThreshold { get; set; }
+
+ ///
+ /// 警报持续时间(秒)
+ ///
+ public double AlarmDuration { get; set; }
+
+ ///
+ /// 波长范围(纳米)
+ ///
+ public double WavelengthMin { get; set; }
+
+ ///
+ /// 最大波长(纳米)
+ ///
+ public double WavelengthMax { get; set; }
+
+ ///
+ /// 构造函数,设置默认值
+ ///
+ public UltravioletWarnerConfig()
+ {
+ Id = "";
+ SensitivityThreshold = 0;
+ AlarmDuration = 5.0; // 默认警报持续5秒
+ WavelengthMin = 0;
+ WavelengthMax = 0;
+ }
+ }
+
+ ///
+ /// 红外告警器配置类
+ ///
+ public class InfraredWarnerConfig
+ {
+ ///
+ /// 红外告警器ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 告警灵敏度阈值
+ ///
+ public double SensitivityThreshold { get; set; }
+
+ ///
+ /// 警报持续时间(秒)
+ ///
+ public double AlarmDuration { get; set; }
+
+ ///
+ /// 最小波长(纳米)
+ ///
+ public double WavelengthMin { get; set; }
+
+ ///
+ /// 最大波长(纳米)
+ ///
+ public double WavelengthMax { get; set; }
+
+ ///
+ /// 构造函数,设置默认值
+ ///
+ public InfraredWarnerConfig()
+ {
+ Id = "";
+ SensitivityThreshold = 0;
+ AlarmDuration = 5.0; // 默认警报持续5秒
+ WavelengthMin = 0;
+ WavelengthMax = 0;
+ }
+ }
+
+ ///
+ /// 毫米波告警器配置类
+ ///
+ public class MillimeterWaveWarnerConfig
+ {
+ ///
+ /// 毫米波告警器ID
+ ///
+ public string Id { get; set; }
+
+ ///
+ /// 告警灵敏度阈值
+ ///
+ public double SensitivityThreshold { get; set; }
+
+ ///
+ /// 警报持续时间(秒)
+ ///
+ public double AlarmDuration { get; set; }
+
+ ///
+ /// 波长范围(纳米)
+ ///
+ public double WavelengthMin { get; set; }
+
+ ///
+ /// 最大波长(纳米)
+ ///
+ public double WavelengthMax { get; set; }
+
+ ///
+ /// 构造函数,设置默认值
+ ///
+ public MillimeterWaveWarnerConfig()
+ {
+ Id = "";
+ SensitivityThreshold = 0;
+ AlarmDuration = 5.0; // 默认警报持续5秒
+ WavelengthMin = 0;
+ WavelengthMax = 0;
}
}
diff --git a/src/Tests/ActiveProtect.Tests.csproj b/src/Tests/ActiveProtect.Tests.csproj
new file mode 100644
index 0000000..c10d712
--- /dev/null
+++ b/src/Tests/ActiveProtect.Tests.csproj
@@ -0,0 +1,37 @@
+
+
+
+ net6.0
+ 9.0
+ disable
+ enable
+ false
+ false
+ false
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Tests/Unit/Models/Jammer/InfraredJammerTests.cs b/src/Tests/Unit/Models/Jammer/InfraredJammerTests.cs
new file mode 100644
index 0000000..8dae26e
--- /dev/null
+++ b/src/Tests/Unit/Models/Jammer/InfraredJammerTests.cs
@@ -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 _mockSimManager;
+ private readonly InfraredJammer _jammer;
+ private readonly string _jammerId = "TestJammer";
+ private readonly string _tankId = "TestTank";
+
+ public InfraredJammerTests()
+ {
+ // 设置测试环境
+ _mockSimManager = new Mock();
+ _mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
+
+ // 设置事件发布
+ _mockSimManager.Setup(m => m.PublishEvent(It.IsAny()));
+
+ _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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(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(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(It.IsAny>()),
+ Times.Once);
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(It.IsAny>()),
+ Times.Once);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Unit/Models/Warner/InfraredWarnerTests.cs b/src/Tests/Unit/Models/Warner/InfraredWarnerTests.cs
new file mode 100644
index 0000000..c6ec793
--- /dev/null
+++ b/src/Tests/Unit/Models/Warner/InfraredWarnerTests.cs
@@ -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 _mockSimManager;
+ private readonly InfraredWarner _warner;
+ private readonly string _warnerId = "TestInfraredWarner";
+ private readonly string _tankId = "TestTank";
+ private readonly InfraredWarnerConfig _config;
+
+ public InfraredWarnerTests()
+ {
+ _mockSimManager = new Mock();
+ _mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
+ _mockSimManager.Setup(m => m.PublishEvent(It.IsAny()));
+
+ _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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(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(e =>
+ e.TargetId == _tankId
+ )), Times.Once);
+ }
+
+ [Fact]
+ public void Deactivate_UnsubscribesFromEvents()
+ {
+ // Act
+ _warner.Deactivate();
+
+ // Assert
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(
+ It.IsAny>()),
+ Times.Once);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Unit/Models/Warner/LaserWarnerTests.cs b/src/Tests/Unit/Models/Warner/LaserWarnerTests.cs
new file mode 100644
index 0000000..27afe11
--- /dev/null
+++ b/src/Tests/Unit/Models/Warner/LaserWarnerTests.cs
@@ -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 _mockSimManager;
+ private readonly LaserWarner _warner;
+ private readonly string _warnerId = "TestLaserWarner";
+ private readonly string _tankId = "TestTank";
+ private readonly LaserWarnerConfig _config;
+
+ public LaserWarnerTests()
+ {
+ _mockSimManager = new Mock();
+ _mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
+ _mockSimManager.Setup(m => m.PublishEvent(It.IsAny()));
+
+ _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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(handler => eventHandler = handler);
+
+ _warner.Activate();
+ Assert.NotNull(eventHandler);
+
+ // 创建一个模拟的目标
+ var mockTarget = new Mock(_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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(handler => eventHandler = handler);
+
+ _warner.Activate();
+ _warner.StartWarning();
+ Assert.NotNull(eventHandler);
+
+ // 创建一个模拟的目标
+ var mockTarget = new Mock(_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(e =>
+ e.TargetId == _tankId
+ )), Times.Once);
+ }
+
+ [Fact]
+ public void Deactivate_UnsubscribesFromEvents()
+ {
+ // Act
+ _warner.Deactivate();
+
+ // Assert
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(
+ It.IsAny>()),
+ Times.Once);
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(
+ It.IsAny>()),
+ Times.Once);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Unit/Models/Warner/MillimeterWaveWarnerTests.cs b/src/Tests/Unit/Models/Warner/MillimeterWaveWarnerTests.cs
new file mode 100644
index 0000000..54007e1
--- /dev/null
+++ b/src/Tests/Unit/Models/Warner/MillimeterWaveWarnerTests.cs
@@ -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 _mockSimManager;
+ private readonly MillimeterWaveWarner _warner;
+ private readonly string _warnerId = "TestMMWWarner";
+ private readonly string _tankId = "TestTank";
+ private readonly MillimeterWaveWarnerConfig _config;
+
+ public MillimeterWaveWarnerTests()
+ {
+ _mockSimManager = new Mock();
+ _mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
+ _mockSimManager.Setup(m => m.PublishEvent(It.IsAny()));
+
+ _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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(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(e =>
+ e.TargetId == _tankId
+ )), Times.Once);
+ }
+
+ [Fact]
+ public void Deactivate_UnsubscribesFromEvents()
+ {
+ // Act
+ _warner.Deactivate();
+
+ // Assert
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(
+ It.IsAny>()),
+ Times.Once);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Tests/Unit/Models/Warner/UltravioletWarnerTests.cs b/src/Tests/Unit/Models/Warner/UltravioletWarnerTests.cs
new file mode 100644
index 0000000..52489e7
--- /dev/null
+++ b/src/Tests/Unit/Models/Warner/UltravioletWarnerTests.cs
@@ -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 _mockSimManager;
+ private readonly UltravioletWarner _warner;
+ private readonly string _warnerId = "TestUVWarner";
+ private readonly string _tankId = "TestTank";
+ private readonly UltravioletWarnerConfig _config;
+
+ public UltravioletWarnerTests()
+ {
+ _mockSimManager = new Mock();
+ _mockSimManager.Setup(m => m.CurrentTime).Returns(0.0);
+ _mockSimManager.Setup(m => m.PublishEvent(It.IsAny()));
+
+ _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? eventHandler = null;
+ _mockSimManager.Setup(m => m.SubscribeToEvent(It.IsAny>()))
+ .Callback>(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(e =>
+ e.TargetId == _tankId
+ )), Times.Once);
+ }
+
+ [Fact]
+ public void Deactivate_UnsubscribesFromEvents()
+ {
+ // Act
+ _warner.Deactivate();
+
+ // Assert
+ _mockSimManager.Verify(
+ m => m.UnsubscribeFromEvent(
+ It.IsAny>()),
+ Times.Once);
+ }
+ }
+}
\ No newline at end of file