Phase 7: TTL清理 + 边界测试(空部署/强风/超长航路), 122测试9秒完成

This commit is contained in:
tian 2026-06-11 16:32:37 +08:00
parent 6a86a33e79
commit 60b5e8d952
7 changed files with 448 additions and 0 deletions

View File

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using CounterDrone.Core.Models;
@ -8,6 +9,8 @@ namespace CounterDrone.Core.Simulation
public class FrameDataStore
{
private readonly IPathProvider _paths;
/// <summary>帧数据保留天数,默认 180 天6 个月)</summary>
public int RetentionDays { get; set; } = 180;
public FrameDataStore(IPathProvider paths)
{
@ -58,5 +61,28 @@ namespace CounterDrone.Core.Simulation
var dbPath = Path.Combine(_paths.GetFramesDir(), $"{taskId}.db");
if (File.Exists(dbPath)) File.Delete(dbPath);
}
/// <summary>清理过期的帧数据库文件</summary>
/// <returns>被删除的文件数量</returns>
public int CleanupExpired()
{
var dir = _paths.GetFramesDir();
if (!Directory.Exists(dir)) return 0;
var cutoff = DateTime.Now.AddDays(-RetentionDays);
var deleted = 0;
foreach (var file in Directory.GetFiles(dir, "*.db"))
{
var creationTime = File.GetCreationTime(file);
if (creationTime < cutoff)
{
File.Delete(file);
deleted++;
}
}
return deleted;
}
}
}

View File

@ -0,0 +1,47 @@
# 仿真评估报告
**任务名称**:喷气拦截
**任务编号**SIM-20260611-004
**仿真耗时**106.4 秒
## 一、仿真前 — 我方配置
| 配置项 | 值 |
|--------|-----|
| 目标数量 | 1 |
| 发射平台 | 6 台 |
| 探测设备 | 1 台 |
| 气溶胶类型 | ActiveMaterial |
## 二、仿真中 — 关键事件时序
| 时间(s) | 事件 | 描述 |
|---------|------|------|
| 92.2 | MunitionLaunched | 计划发射 mun_1 |
| 92.2 | MunitionLaunched | 计划发射 mun_2 |
| 92.2 | MunitionLaunched | 计划发射 mun_3 |
| 92.2 | MunitionLaunched | 计划发射 mun_4 |
| 92.2 | MunitionLaunched | 计划发射 mun_5 |
| 92.2 | MunitionLaunched | 计划发射 mun_6 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 106.4 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 106.4 | SimulationEnd | |
## 三、仿真后 — 数据统计
| 统计项 | 值 |
|--------|-----|
| 无人机总数 | 1 |
| 被摧毁 | 1 |
| 到达目标 | 0 |
| 侵入管控区 | 0 |
| 弹药发射次数 | 6 |
| 云团生成次数 | 6 |
**对抗结果**:成功拦截

View File

@ -0,0 +1,47 @@
# 仿真评估报告
**任务名称**:喷气拦截
**任务编号**SIM-20260611-003
**仿真耗时**106.4 秒
## 一、仿真前 — 我方配置
| 配置项 | 值 |
|--------|-----|
| 目标数量 | 1 |
| 发射平台 | 6 台 |
| 探测设备 | 1 台 |
| 气溶胶类型 | ActiveMaterial |
## 二、仿真中 — 关键事件时序
| 时间(s) | 事件 | 描述 |
|---------|------|------|
| 92.2 | MunitionLaunched | 计划发射 mun_1 |
| 92.2 | MunitionLaunched | 计划发射 mun_2 |
| 92.2 | MunitionLaunched | 计划发射 mun_3 |
| 92.2 | MunitionLaunched | 计划发射 mun_4 |
| 92.2 | MunitionLaunched | 计划发射 mun_5 |
| 92.2 | MunitionLaunched | 计划发射 mun_6 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 101.8 | CloudGenerated | 云团生成 |
| 106.4 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 106.4 | SimulationEnd | |
## 三、仿真后 — 数据统计
| 统计项 | 值 |
|--------|-----|
| 无人机总数 | 1 |
| 被摧毁 | 1 |
| 到达目标 | 0 |
| 侵入管控区 | 0 |
| 弹药发射次数 | 6 |
| 云团生成次数 | 6 |
**对抗结果**:成功拦截

View File

@ -0,0 +1,46 @@
# 仿真评估报告
**任务名称**:活塞拦截
**任务编号**SIM-20260611-041
**仿真耗时**185.6 秒
## 一、仿真前 — 我方配置
| 配置项 | 值 |
|--------|-----|
| 目标数量 | 1 |
| 发射平台 | 5 台 |
| 探测设备 | 1 台 |
| 气溶胶类型 | InertGas |
## 二、仿真中 — 关键事件时序
| 时间(s) | 事件 | 描述 |
|---------|------|------|
| 167.4 | MunitionLaunched | 计划发射 mun_1 |
| 167.4 | MunitionLaunched | 计划发射 mun_2 |
| 167.4 | MunitionLaunched | 计划发射 mun_3 |
| 167.4 | MunitionLaunched | 计划发射 mun_4 |
| 167.4 | MunitionLaunched | 计划发射 mun_5 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 185.6 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 185.6 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 185.6 | SimulationEnd | |
## 三、仿真后 — 数据统计
| 统计项 | 值 |
|--------|-----|
| 无人机总数 | 1 |
| 被摧毁 | 2 |
| 到达目标 | 0 |
| 侵入管控区 | 0 |
| 弹药发射次数 | 5 |
| 云团生成次数 | 5 |
**对抗结果**:成功拦截

View File

@ -0,0 +1,46 @@
# 仿真评估报告
**任务名称**:活塞拦截
**任务编号**SIM-20260611-041
**仿真耗时**185.6 秒
## 一、仿真前 — 我方配置
| 配置项 | 值 |
|--------|-----|
| 目标数量 | 1 |
| 发射平台 | 5 台 |
| 探测设备 | 1 台 |
| 气溶胶类型 | InertGas |
## 二、仿真中 — 关键事件时序
| 时间(s) | 事件 | 描述 |
|---------|------|------|
| 167.4 | MunitionLaunched | 计划发射 mun_1 |
| 167.4 | MunitionLaunched | 计划发射 mun_2 |
| 167.4 | MunitionLaunched | 计划发射 mun_3 |
| 167.4 | MunitionLaunched | 计划发射 mun_4 |
| 167.4 | MunitionLaunched | 计划发射 mun_5 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 177.0 | CloudGenerated | 云团生成 |
| 185.6 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 185.6 | DroneDestroyed | 无人机 drone_1 被摧毁 |
| 185.6 | SimulationEnd | |
## 三、仿真后 — 数据统计
| 统计项 | 值 |
|--------|-----|
| 无人机总数 | 1 |
| 被摧毁 | 2 |
| 到达目标 | 0 |
| 侵入管控区 | 0 |
| 弹药发射次数 | 5 |
| 云团生成次数 | 5 |
**对抗结果**:成功拦截

View File

@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.IO;
using CounterDrone.Core;
using CounterDrone.Core.Algorithms;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
using CounterDrone.Core.Services;
using CounterDrone.Core.Simulation;
using Xunit;
namespace CounterDrone.Core.Tests
{
/// <summary>边界场景测试</summary>
public class EdgeCaseTests : IDisposable
{
private readonly string _testDir;
private readonly IScenarioService _scenario;
private readonly SQLite.SQLiteConnection _db;
private string _taskId = string.Empty;
public EdgeCaseTests()
{
_testDir = Path.Combine(Path.GetTempPath(), $"cd_edge_{Guid.NewGuid():N}");
var paths = new TestPathProvider(_testDir);
var dbm = new DatabaseManager(paths);
_db = dbm.OpenMainDb();
_db.Insert(new AmmunitionSpec
{
AerosolType = (int)AerosolType.InertGas, InitialRadius = 50,
CoreDensity = 1.0, DispersionRateBase = 5, MaxRadius = 500,
MaxDuration = 120, EffectiveConcentration = 0.05,
});
_scenario = new ScenarioService(
new SimTaskRepository(_db), new CombatSceneRepository(_db),
new ControlZoneRepository(_db), new TargetConfigRepository(_db),
new EquipmentDeploymentRepository(_db), new CloudDispersalRepository(_db),
new RoutePlanRepository(_db), new WaypointRepository(_db));
}
public void Dispose()
{
_db?.Close();
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
}
[Fact]
public void EmptyDeployment_EngineRunsWithoutCrash()
{
var task = _scenario.CreateTask("空部署", "");
_taskId = task.Id;
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
_scenario.SaveTarget(_taskId, new TargetConfig
{
TargetType = (int)TargetType.Piston, Quantity = 1,
TypicalSpeed = 100, TypicalAltitude = 300,
});
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 300, PosZ = 0 },
new Waypoint { PosX = 1000, PosY = 300, PosZ = 0 },
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir));
engine.Initialize(_taskId);
engine.TimeScale = 4f;
for (int i = 0; i < 500; i++)
{
var r = engine.Tick(1f / 20f);
if (r.State == SimulationState.Completed) break;
}
engine.Stop();
Assert.Equal(DroneStatus.ReachedTarget, engine.Drones[0].Status);
}
[Fact]
public void ExtremeWind_DroneStillFlies()
{
var task = _scenario.CreateTask("飓风", "");
_taskId = task.Id;
_scenario.SaveScene(_taskId, new CombatScene
{
WindSpeed = 30, // 最大风速
WindDirection = (int)WindDirection.E,
});
_scenario.SaveTarget(_taskId, new TargetConfig
{
TargetType = (int)TargetType.Piston, Quantity = 1,
TypicalSpeed = 600, // 高速对抗强风
});
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 300, PosZ = 0 },
new Waypoint { PosX = 5000, PosY = 300, PosZ = 0 },
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir));
engine.Initialize(_taskId);
engine.TimeScale = 4f;
for (int i = 0; i < 500; i++)
{
var r = engine.Tick(1f / 20f);
if (r.State == SimulationState.Completed) break;
}
engine.Stop();
// 强风下应偏移到东方(+X
Assert.True(engine.Drones[0].PosX > 0);
}
[Fact]
public void LongRoute_CompletesWithoutError()
{
var task = _scenario.CreateTask("超长航路", "");
_taskId = task.Id;
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
_scenario.SaveTarget(_taskId, new TargetConfig
{
TargetType = (int)TargetType.HighSpeed, Quantity = 1,
TypicalSpeed = 500,
});
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
// 500 km/h, 100km 航程
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
new List<Waypoint>
{
new Waypoint { PosX = 0, PosY = 500, PosZ = 0 },
new Waypoint { PosX = 100000, PosY = 500, PosZ = 0 },
});
var engine = new SimulationEngine(_scenario, new FrameDataStore(new TestPathProvider(_testDir)),
new DamageModelRouter(), new TestPathProvider(_testDir));
engine.Initialize(_taskId);
engine.TimeScale = 4f;
for (int i = 0; i < 5000; i++)
{
var r = engine.Tick(1f / 20f);
if (r.State == SimulationState.Completed) break;
}
engine.Stop();
Assert.Equal(DroneStatus.ReachedTarget, engine.Drones[0].Status);
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.IO;
using CounterDrone.Core;
using CounterDrone.Core.Simulation;
using Xunit;
namespace CounterDrone.Core.Tests
{
public class FrameDataStoreTests : IDisposable
{
private readonly string _testDir;
private readonly FrameDataStore _store;
public FrameDataStoreTests()
{
_testDir = Path.Combine(Path.GetTempPath(), $"cd_frames_{Guid.NewGuid():N}");
var paths = new TestPathProvider(_testDir);
_store = new FrameDataStore(paths);
}
public void Dispose()
{
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
}
[Fact]
public void CleanupExpired_DeletesOldFiles()
{
// 设置为 0 天保留,所有文件都是"过期"的
_store.RetentionDays = 0;
// 创建一些假帧数据库
var db1 = _store.CreateOrOpen("task_old_1");
db1.Close();
var db2 = _store.CreateOrOpen("task_old_2");
db2.Close();
var deleted = _store.CleanupExpired();
Assert.Equal(2, deleted);
// 文件应该被删除
Assert.False(File.Exists(Path.Combine(_testDir, "frames", "task_old_1.db")));
Assert.False(File.Exists(Path.Combine(_testDir, "frames", "task_old_2.db")));
}
[Fact]
public void CleanupExpired_KeepsRecentFiles()
{
_store.RetentionDays = 365; // 保留 1 年
var db = _store.CreateOrOpen("task_new");
db.Close();
// 手动修改创建时间为"最近"
var filePath = Path.Combine(_testDir, "frames", "task_new.db");
File.SetCreationTime(filePath, DateTime.Now);
var deleted = _store.CleanupExpired();
Assert.Equal(0, deleted);
// 文件应该保留
Assert.True(File.Exists(filePath));
}
[Fact]
public void CleanupExpired_EmptyDir_ReturnsZero()
{
var deleted = _store.CleanupExpired();
Assert.Equal(0, deleted);
}
}
}