CounterDroneBackend/docs/对接文档_Unity前端.md
tian 36ab4fad7b 配置运行时重载 IConfigService + 防御推荐 + 文档更新
新增:
- IConfigService / ConfigService:读写 defaults.json / planner_config.json
- SavePlannerConfig / SaveDefaults / Reload,无需重启 Unity
- PlannerConfigReloaded 事件 → ScenarioManager 自动重建 planner
- SimulationEngine.UpdatePlanner / ScenarioService.UpdatePlanner
- SimulationRunner.ReloadPlanner 桥接
- DefenseRecommendation / RecommendOption 返回类型
- IScenarioService.GetDefenseRecommendation(配置阶段生成最佳抛撒参数)
- ConfigServiceTests 5 个测试 + DefenseRecommendationTests 3 个测试

修复:
- 云团间距半径基准不一致(间距用 R30、穿越用 R27 → 统一用 effectiveRadius)
- 消除硬编码 30f(DefensePlanner + GaussianPuffDispersion 读 Phase2Duration)
- TryGenerateFireEvents 复用 ComputeEffectiveRadius,消除重复计算
- 概率计算间距加重叠系数
- DatabaseManager 不再 DROP SimulationReport(保留历史报告)
- 清理迁移代码(MetaEntry / 旧表 DROP / 版本检查)

文档:
- 对接文档 V2.1:加 IConfigService API + 配置重载用法 + 修正 DLL 更新说明
- 实施跟踪 V1.8:8.2.1 更新 + 新增 8.2.3/8.2.4 + M14 里程碑
- 270 测试通过,Unity 编译通过
2026-06-25 22:10:22 +08:00

956 lines
39 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

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

# 后端对接文档Unity 前端)
> **版本**V2.1
> **日期**2026-06-22
> **Unity 版本**2022.3.62f3c1
---
## 一、交付物与部署
### 1.1 需要取的文件
```
src/Unity/Assets/Plugins/CounterDrone.Core/ ← 22 个 DLL拖入 Unity 项目的 Plugins 目录)
src/Unity/Assets/Scripts/Managers/ ← 9 个桥接脚本
```
StreamingAssets 资源文件(放到 Unity 项目 `Assets/StreamingAssets/` 下):
```
StreamingAssets/
defaults.json ← 默认数据(首次运行自动复制到 persistentDataPath
planner_config.json ← 规划器配置
fonts/CJK-Font.ttf ← PDF 导出用的中文字体(仅 PDF 导出需要)
```
> 以上资源文件可在仓库的 `data/` 目录找到。字体文件名硬编码为 `CJK-Font.ttf`,可替换为任意 CJK TrueType 字体但不可改名。
### 1.2 DLL 清单22 个)
全部为纯托管 DLL无原生库依赖Unity IL2CPP/Mono 全平台兼容。
| DLL | 用途 |
|-----|------|
| `CounterDrone.Core.dll` | 核心库 |
| `SQLite-net.dll` | SQLite ORM |
| `SQLitePCLRaw.core.dll` / `batteries_v2.dll` / `provider.e_sqlite3.dll` | SQLite 原生提供程序 |
| `System.Text.Json.dll` / `Encodings.Web.dll` / `Microsoft.Bcl.AsyncInterfaces.dll` | JSON 序列化 |
| `System.Memory.dll` / `Buffers.dll` / `Numerics.Vectors.dll` / `Runtime.CompilerServices.Unsafe.dll` / `IO.Pipelines.dll` / `Threading.Tasks.Extensions.dll` | 基础库传递依赖 |
| `PdfSharpCore.dll` | PDF 生成 |
| `ICSharpCode.SharpZipLib.dll` | PdfSharpCore 依赖ZIP |
| `SixLabors.Fonts.dll` / `SixLabors.ImageSharp.dll` | PdfSharpCore 依赖(字体/图像) |
| `System.Collections.Concurrent.dll` / `IO.UnmanagedMemoryStream.dll` / `Threading.Tasks.Parallel.dll` / `Threading.dll` / `IO.FileSystem.Primitives.dll` | SixLabors 传递依赖 |
### 1.3 DLL 导入设置
DLL 拖入 `Assets/Plugins/CounterDrone.Core/` 后,在 Inspector 中确认所有 DLL 的平台勾选:**Editor + Standalone + Android + iOS 全部勾选**(所有 DLL 均全平台兼容)。
> **IL2CPP 注意**:若遇 `TypeLoadException`,检查 `Player Settings → Managed Stripping Level`,建议设为 **Low** 或 **Medium**。
### 1.4 更新后端 DLL
```bash
# 方式 A一键脚本自动编译 + 复制 DLL + Unity 编译验证)
pwsh scripts/check_unity_build.ps1
# 方式 B手动
cd src/CounterDrone.Core
dotnet publish -c Release -o ../../unity_plugins
# 然后将 unity_plugins/*.dll 全部复制到 src/Unity/Assets/Plugins/CounterDrone.Core/
```
> **重要**:更新 DLL 后首次运行 Unity 会建表+种子默认数据。**历史仿真报告不再丢失**SimulationReport 表不再被 DROP
---
## 二、快速开始
1. 按 1.1 部署 DLL、脚本和 StreamingAssets 资源
2. 创建空 GameObject`SimulationBootstrap`
3. 运行 — 自动种子数据库,选一个 `[Demo]` 想定,启动仿真
> `SimulationBootstrap` 是参考模板展示预设模式一行代码和自定义模式两种路径。Inspector 勾选 `_usePreset` 切换。
---
## 三、完整工作流5 步)
```
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ ┌──────────┐
│ 基础数据 CRUD │ → │ 创建想定 │ → │ 运行仿真 │ → │ 生成报告 │ → │ 回放 │
│ DataService │ │ ScenarioMgr │ │ SimRunner │ │ ReportMgr │ │ Replay │
└─────────────┘ └─────────────┘ └─────────────┘ └──────────┘ └──────────┘
```
### 3.1 管理基础数据
```csharp
var ds = GetComponent<ScenarioManager>().DataService;
var drones = ds.GetAllDrones();
ds.SaveDrone(new DroneSpec { Id = "custom", Name = "自定义", TypicalSpeed = 100, ... });
ds.DeleteDrone("custom");
// 7 类基础数据均提供 GetAll/Get/Save/Delete 四个方法
```
### 3.2 创建想定
```csharp
var scenario = GetComponent<ScenarioManager>();
// 方式 A预设想定
var demos = scenario.Search("Demo", null, null, 1, 100);
var scenarioId = demos.Items[0].Id;
// 方式 B自定义5 步配置)
var sc = scenario.CreateScenario("我的想定", "");
scenario.SaveScene(sc.Id, new CombatScene { WindSpeed = 5, WindDirection = (int)WindDirection.E });
scenario.SaveScenarioDrone(sc.Id, new ScenarioDrone { DroneSpecId = "shahed", WaveId = "w1", Quantity = 3 });
scenario.SaveDeployment(sc.Id, new List<ScenarioUnit> {
new() { FireUnitSpecId = "ground-light", Quantity = 2,
PositionX = 5000, PositionY = 0, PositionZ = 50, AerosolType = 0, MunitionCount = 8 }
});
scenario.SaveRoute(sc.Id, "w1", new RoutePlan(), new List<Waypoint> {
new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 },
new() { PosX = 10000, PosY = 500, PosZ = 0, Speed = 200 }
});
scenario.SaveCloud(sc.Id, new CloudDispersal { AerosolType = 0, DisperseHeight = 500 });
scenario.UpdateStep(sc.Id, 5);
```
### 3.3 运行仿真
```csharp
var runner = GetComponent<SimulationRunner>();
runner.LoadAndStart(scenarioId);
runner.Engine.TimeScale = 4f;
// 事件订阅
runner.Engine.OnMunitionLaunched += m => { };
runner.Engine.OnCloudGenerated += c => { };
runner.Engine.OnDroneDestroyed += d => { };
runner.Engine.OnSimulationEnded += () => { };
// 逐帧驱动(在 Update 中调用)
void Update() {
var result = runner.Engine.Tick(Time.deltaTime);
foreach (var snap in result.EntitySnapshots) {
// snap.EntityType, PosX/Y/Z, VelX/Y/Z, Hp, CloudRadius...
}
}
runner.Stop();
```
### 3.4 生成报告
```csharp
var rm = GetComponent<ReportManager>();
var detail = scenario.GetDetail(scenarioId);
var report = rm.Generate(scenarioId, detail, runner.Engine.Events.ToList(),
runner.Engine.Drones[0].Status.ToString(), runner.Engine.SimulationTime);
// Generate() 自动导出 .md 文件到 {persistentDataPath}/reports/
// PDF 按需导出:
byte[] pdf = rm.ExportReport(report.Id, "pdf"); // → byte[]
string path = rm.Export(report.Id, dir, "pdf"); // → 文件路径
```
**Generate 参数**
| 参数 | 类型 | 来源 |
|------|------|------|
| `scenarioId` | string | 创建想定时获得 |
| `config` | ScenarioConfig | `scenario.GetDetail(id)` |
| `events` | List\<SimEvent\> | `runner.Engine.Events.ToList()`(必须 `.ToList()` 拷贝) |
| `finalDroneStatus` | string | `runner.Engine.Drones[0].Status.ToString()` |
| `duration` | float | `runner.Engine.SimulationTime`(秒) |
**导出方法**
- `ExportReport(id, "pdf")``byte[]`,实时渲染 PDF需字体文件
- `ExportReport(id, "md")``byte[]`,从数据库直接读取,快
- `Export(id, dir, "pdf"/"md")` → 文件路径,自动创建目录
- `Export(id)` → 默认导出 MD 到 `{persistentDataPath}/reports/`
> PDF 导出依赖 `StreamingAssets/fonts/CJK-Font.ttf`,缺失抛 `FileNotFoundException`。PDF 渲染同步阻塞主线程(小报告 ~100ms大报告可达 2sUI 建议显示 loading。
### 3.5 回放
```csharp
var replay = GetComponent<ReplayController>();
replay.LoadReplay(scenarioId);
for (int i = 0; i < replay.TotalFrames; i++) {
var frame = replay.GetFrame(i);
// frame.SimulationTime, frame.EntitySnapshots...
}
```
---
## 四、坐标系与 3D 可视化
### 4.1 坐标系约定
| 项目 | 约定 |
|------|------|
| **向上轴** | **Y 轴 = 高度**(与 Unity 默认一致) |
| **水平面** | X 轴 = 纵深航路方向Z 轴 = 横向 |
| **单位** | 米m |
| **原点** | 场景一角(非中心),航路通常从 X=0 沿 X 正方向延伸 |
| **典型场景尺寸** | 4000m × 10000m × 1000mW × L × H`CombatScene.SceneWidth/Length/Height` |
| **典型无人机高度** | 300~500mPosY = 500 即 500m 高空) |
| **速度单位** | 航路点 `Waypoint.Speed` = **km/h**;平台 `CruiseSpeed` = **m/s**(引擎内部自动换算) |
> `SimulationRunner` 直接 `transform.position = new Vector3(snap.PosX, snap.PosY, snap.PosZ)`,无需轴变换。
### 4.2 Prefab 搭建
`SimulationRunner` 有 3 个 `[SerializeField]` prefab 字段Inspector 中拖入:
| 字段 | 用途 | null 时回退 | 建议缩放 |
|------|------|------------|---------|
| `_dronePrefab` | 无人机 | Cube | Runner 自动设 `localScale = Vector3.one * 50f` |
| `_cloudPrefab` | 云团 | Sphere | Runner 按云团半径动态设 `localScale = Vector3.one * CloudRadius` |
| `_platformPrefab` | 发射平台 | Capsule | Runner 自动设 `localScale = (30, 80, 30)` |
| —(无字段) | 弹药 | Cylinder | Runner 自动设 `localScale = (2, 10, 2)` |
**Prefab 组件要求**
| 组件 | 必需 | 说明 |
|------|------|------|
| `Renderer` | ✅ | Runner 通过 `GetComponent<Renderer>().material` 设置颜色 |
| `Collider` | 可选 | Runner 不依赖 Collider |
| 脚本组件 | 不需要 | 位置由 Runner 每帧驱动prefab 不需要自带移动脚本 |
**云团可视化建议**prefab 用 Sphere 即可Runner 设红色半透明材质。如需粒子效果可自行扩展——Runner 每帧更新 `CloudRadius`(缩放)和 `CloudOpacity`(材质 alpha粒子系统可读取这两个值驱动参数。
### 4.3 SimulationRunner 可视化机制
Runner 的 `Update()` 每帧执行:
1. 调用 `engine.Tick(Time.deltaTime)` → 获取 `SimulationFrameResult`
2. 遍历 `result.EntitySnapshots`
- 新实体EntityId 未见过)→ Instantiate prefab
- 已有实体 → 更新 `transform.position`
- Cloud 类型 → 更新 `localScale`(按 `CloudRadius`)和材质 alpha`CloudOpacity`
3. 遍历 `engine.Munitions` 同步炮弹位置,清理已消失的弹药 GameObject
**实体颜色约定**Runner 硬编码,可自行修改):
- 无人机prefab 原始材质
- 云团:红色半透明 `(1, 0, 0, CloudOpacity)`
- 平台:蓝色半透明 `(0.2, 0.5, 1, 0.8)`
- 弹药:黄色 `(1, 0.92, 0.016, 1)`
### 4.4 相机定位参考
`SimulationBootstrap` 中的相机设置(可供参考):
```csharp
// 俯瞰视角
cam.transform.position = new Vector3(midX, 2000, -3000); // midX = 场景中点
cam.transform.LookAt(new Vector3(midX, 500, 0)); // 看向 500m 高度
```
### 4.5 探测范围可视化
`DetectionEntity` 提供 `IsInRange(x, y, z, visibility, out effectiveRange)` 方法。如需绘制 3D 球冠范围,可从 `SensorSpec` 读取 `RadarRange`/`EORange`/`IRRange` 和 `MinElevation`/`MaxElevation`/`MinDetectAlt`/`MaxDetectAlt`,用 wireframe sphere 或 mesh 表示。
### 4.6 管控区域可视化
`ControlZone``VerticesJson` 存储多边形顶点 JSON`[{"x":0,"y":0,"z":0},...]`。配合 `MinAltitude`/`MaxAltitude` 构成 3D 棱柱区域。前端可用 `LineRenderer` 画底面多边形 + 垂直线段表示高度范围。
---
## 五、Manager API 完整签名
所有 Manager 继承 `MonoBehaviour`,在 `Awake()` 中初始化内部服务。**必须在 `Awake` 之后(如 `Start`)才能调用**`OnDisable` 后不可调用。
### ScenarioManager
```csharp
public IScenarioService Service { get; }
public IDataService DataService { get; } // 基础数据 CRUD7 类)
public IConfigService Config { get; } // 配置读写 + 运行时重载
// Scenario CRUD
public Scenario CreateScenario(string name, string number);
public Scenario UpdateScenario(Scenario s);
public void DeleteScenario(string id);
public ScenarioConfig GetDetail(string id);
public PagedResult<Scenario> Search(string kw, string from, string to, int page, int size);
// CombatScene
public void SaveScene(string scenarioId, CombatScene s);
public CombatScene GetScene(string scenarioId);
// ControlZone
public void SaveControlZones(string scenarioId, List<ControlZone> z);
public List<ControlZone> GetControlZones(string scenarioId);
public void DeleteControlZone(string id);
// ScenarioDrone
public ScenarioDrone SaveScenarioDrone(string scenarioId, ScenarioDrone d);
public ScenarioDrone AddScenarioDrone(string scenarioId, ScenarioDrone d);
public void UpdateScenarioDrone(ScenarioDrone d);
public void DeleteScenarioDrone(string id);
public List<ScenarioDrone> GetScenarioDrones(string scenarioId);
// ScenarioUnit部署
public void SaveDeployment(string scenarioId, List<ScenarioUnit> e);
public ScenarioUnit AddDeploymentUnit(string scenarioId, ScenarioUnit u);
public void UpdateDeploymentUnit(ScenarioUnit u);
public void DeleteDeploymentUnit(string id);
public List<ScenarioUnit> GetDeploymentUnits(string scenarioId);
// Detection探测设备复用 ScenarioUnit
public void AddDetection(string scenarioId, ScenarioUnit d);
public void DeleteDetection(string id);
public List<ScenarioUnit> GetDetections(string scenarioId);
// CloudDispersal
public void SaveCloud(string scenarioId, CloudDispersal c);
public CloudDispersal GetCloud(string scenarioId);
public void DeleteCloud(string scenarioId);
// Route & Waypoint
public void SaveRoute(string scenarioId, string waveId, RoutePlan r, List<Waypoint> w);
public List<RoutePlan> GetRoutes(string scenarioId);
public void DeleteRoute(string id);
public List<Waypoint> GetWaypoints(string scenarioId);
public List<Waypoint> GetWaypointsByWave(string scenarioId, string waveId);
public void DeleteWaypoint(string id);
// Misc
public void UpdateStep(string scenarioId, int step);
public EnumMetadata GetEnums(); // 枚举中英文对照(下拉框数据源)
// 防御推荐(配置阶段调用,仿真前生成最优抛撒参数)
public DefenseRecommendation GetDefenseRecommendation(string scenarioId);
```
**防御推荐用法**"一键应用"功能):
```csharp
var rec = scenario.GetDefenseRecommendation(scenarioId);
if (rec.Success)
{
// rec.Best.PosX/Y/Z — 推荐抛撒位置
// rec.Best.Timing — 推荐抛撒时机(仿真秒)
// rec.Best.SalvoRounds — 推荐弹药数
// rec.Best.EstimatedProbability — 估计拦截概率0~1
// rec.Best.ThreatsEngaged / ThreatsUnengaged — 已拦截/未拦截威胁数
// 用户点"一键应用" → 写入 CloudDispersal
var cloud = scenario.GetCloud(scenarioId);
cloud.PositionX = rec.Best.PosX;
cloud.PositionY = rec.Best.PosY;
cloud.PositionZ = rec.Best.PosZ;
cloud.Source = "Algorithm";
cloud.PositionMode = (int)PositionMode.AlgorithmRecommended;
scenario.SaveCloud(scenarioId, cloud);
}
else
{
Debug.Log($"推荐失败: {rec.Summary}");
}
```
### IConfigService通过 `ScenarioManager.Config` 访问)
配置文件读写 + 运行时重载,**无需重启 Unity**
```csharp
public interface IConfigService
{
string GetPlannerConfigJson(); // 读取 planner_config.json 原文
void SavePlannerConfig(string json); // 保存并立即重载planner 自动重建)
string GetDefaultsJson(); // 读取 defaults.json 原文
void SaveDefaults(string json); // 保存并重载种子数据到数据库
void Reload(); // 从磁盘重新加载(前端直接改文件后通知后端刷新)
}
```
**用法 1通过 API 修改配置**(推荐)
```csharp
var cfg = GetComponent<ScenarioManager>().Config;
// 读当前 planner 配置
string json = cfg.GetPlannerConfigJson();
// 修改后保存 — 自动重载planner 立即用新参数
cfg.SavePlannerConfig(modifiedJson);
// 修改 defaults.json — 自动重载,数据库 InsertOrReplace 覆盖同 Id 记录
cfg.SaveDefaults(modifiedDefaultsJson);
```
**用法 2直接改文件后通知后端重载**
```csharp
// 前端直接修改 persistentDataPath 下的 JSON 文件
// 然后通知后端重新加载
cfg.Reload(); // 重新读磁盘 → 重建 planner → 刷新种子数据
```
> `SavePlannerConfig` 触发后,`ScenarioManager` 内部的 `DefensePlanner` 会自动重建。`SimulationRunner` 中的 planner 在下次 `LoadAndStart` 时自动用新配置。运行中的仿真不受影响planner 在 `Initialize` 时调用,不在运行中调用)。
### IDataService通过 `ScenarioManager.DataService` 访问)
7 类基础数据,每类 4 个方法(`GetAll*` / `Get*(id)` / `Save*(spec)` / `Delete*(id)`
| 基础数据类型 | 方法前缀 | 模型类 |
|---|---|---|
| 弹药 | `Ammo` | `AmmunitionSpec` |
| 火力单元 | `FireUnit` | `FireUnitSpec` |
| 无人机 | `Drone` | `DroneSpec` |
| 传感器 | `Sensor` | `SensorSpec` |
| 环境 | `Environment` | `EnvironmentSpec` |
| 编队 | `Formation` | `FormationTemplate` |
| 航线 | `Route` | `RouteTemplate` |
```csharp
List<DroneSpec> GetAllDrones();
DroneSpec GetDrone(string id);
void SaveDrone(DroneSpec spec);
void DeleteDrone(string id);
// 其余 6 类签名相同,替换前缀即可
```
### SimulationRunner
```csharp
public FrameDataStore FrameStore { get; }
public SimulationEngine Engine { get; } // 核心引擎
public IDataService DataService { get; set; }
public void LoadAndStart(string scenarioId);
public void Stop();
```
通过 `runner.Engine` 访问的成员:
```csharp
// ── 状态 ──
runner.Engine.State // SimulationState: Idle/Running/Paused/Stopped/Completed
runner.Engine.TimeScale // float时间倍率默认 1设 4 = 4 倍速)
runner.Engine.SimulationTime // float仿真累计时间
runner.Engine.FrameIndex // int当前帧序号
runner.Engine.RecordFrames // bool是否录制帧数据回放用默认 true
// ── 实体列表IReadOnlyList只读 ──
runner.Engine.Drones // IReadOnlyList<DroneEntity>
runner.Engine.Clouds // IReadOnlyList<CloudEntity>
runner.Engine.Munitions // IReadOnlyList<MunitionEntity>
runner.Engine.Platforms // IReadOnlyList<PlatformEntity>
runner.Engine.DetectionEntities // IReadOnlyList<DetectionEntity>
runner.Engine.Events // IReadOnlyList<SimEvent>(仿真期间持续追加)
// ── 方法 ──
runner.Engine.Tick(float dt) // → SimulationFrameResult推进一帧
runner.Engine.Pause() // Running → Paused
runner.Engine.Resume() // Paused → Running
runner.Engine.Stop() // → Stopped + Flush 帧数据到数据库
// ── 事件Action 委托) ──
runner.Engine.OnMunitionLaunched // Action<MunitionEntity>
runner.Engine.OnCloudGenerated // Action<CloudEntity>
runner.Engine.OnDroneDestroyed // Action<DroneEntity>
runner.Engine.OnDroneReachedTarget // Action<DroneEntity>
runner.Engine.OnZoneIntruded // Action<DroneEntity, ControlZoneEntity>
runner.Engine.OnTargetDetected // Action<DroneEntity, DetectionEntity>
runner.Engine.OnSimulationEnded // Action
```
### ReportManager
```csharp
public IReportService Service { get; }
public SimulationReport Generate(string scenarioId, ScenarioConfig cfg,
List<SimEvent> events, string status, float duration);
public SimulationReport Get(string id);
public void Delete(string id);
public PagedResult<SimulationReport> Search(string kw, string from, string to, int page, int size);
public byte[] ExportReport(string reportId, string format); // "pdf" | "md"
public string Export(string reportId, string outputDir, string format); // "pdf" | "md"
public string Export(string reportId); // 默认 MD 到 reports 目录
```
### ReplayController
```csharp
public int TotalFrames { get; }
public int CurrentFrameIndex { get; }
public List<SimFrameRecord> CurrentFrames { get; }
public void LoadReplay(string scenarioId, FrameDataStore frameStore = null);
public List<SimFrameRecord> GetFrame(int frameIndex);
```
> `LoadReplay` 优先用内存帧数据(仿真刚结束),无则从 SQLite 帧库加载(历史回放)。
### ModelManager
```csharp
public IModelService Service { get; }
public ModelInfo Import(string filePath, string name);
public ModelInfo Import(string filePath, string name, int entityType, string desc);
public ModelInfo Add(ModelInfo model);
public ModelInfo UpdateModel(ModelInfo model);
public void Delete(string id);
public List<ModelInfo> GetAll();
public ModelInfo Get(string id);
```
---
## 六、枚举值清单
### 6.1 有官方中文翻译的枚举GetEnums() 返回)
这 13 个枚举通过 `scenario.GetEnums()` 获取中英文对照,可直接用于下拉框:
```csharp
var enums = scenario.GetEnums();
// enums.DroneType, enums.PowerType, enums.PlatformType, enums.AerosolType,
// enums.WeatherType, enums.WindDirection, enums.SceneType, enums.FormationMode,
// enums.TriggerMode, enums.ReleaseMode, enums.EquipmentRole, enums.EntityType,
// enums.SensorType
```
| 枚举 | 成员 = 值 | 中文 |
|------|----------|------|
| **DroneType** | Rotor=0, FixedWing=1, HighSpeed=2 | 旋翼/固定翼/高速目标 |
| **PowerType** | Electric=0, Piston=1, Jet=2 | 电推/活塞/喷气 |
| **PlatformType** | AirBased=0, GroundBased=1 | 空基/地基 |
| **AerosolType** | InertGas=0, ActiveMaterial=1, ActiveFuel=2 | 惰性气体(吸入式灭火)/活性材料(爆燃式)/活性燃料(吸入式爆炸) |
| **WeatherType** | Sunny=0, Overcast=1, Fog=2, Rain=3, Night=4 | 晴/阴/雾/雨/夜 |
| **WindDirection** | N=0, NE=1, E=2, SE=3, S=4, SW=5, W=6, NW=7 | 北/东北/东/东南/南/西南/西/西北 |
| **SceneType** | Urban=0, Plain=1, Mountain=2, Coast=3 | 城市/平原/山区/海岸 |
| **FormationMode** | Single=0, Formation=1, Swarm=2 | 单机/编队/蜂群 |
| **TriggerMode** | Time=0, Area=1, Manual=2 | 时间触发/区域触发/手动触发 |
| **ReleaseMode** | Single=0, Continuous=1, Pulse=2 | 单次齐射/连续释放/脉冲释放 |
| **EquipmentRole** | Detection=0, LaunchPlatform=1 | 探测设备/发射平台 |
| **EntityType** | Drone=0, Platform=1, DetectionEquip=2, Cloud=3, Munition=4 | 无人机/发射平台/探测设备/云团/弹药 |
| **SensorType** | Radar=0, EO=1, IR=2 | 雷达/光电/红外 |
### 6.2 无官方翻译的枚举(前端需自行映射中文)
| 枚举 | 命名空间 | 成员 = 值 | 含义 |
|------|---------|----------|------|
| **SimulationState** | `Simulation` | Idle=0, Running=1, Paused=2, Stopped=3, Completed=4 | 空闲/运行/暂停/停止/完成 |
| **DroneStatus** | `Simulation` | Flying=0, Destroyed=1, ReachedTarget=2, ZoneIntruded=3 | 飞行中/已摧毁/到达目标/侵入管控区 |
| **PlatformState** | `Simulation` | Idle=0, FlyingToTarget=1, ReadyToRelease=2 | 空闲/飞向目标/准备投放 |
| **SimEventType** | `Models` | TargetDetected=0, MunitionLaunched=1, CloudGenerated=2, DroneEnteredCloud=3, DroneDestroyed=4, DroneReachedTarget=5, ZoneIntruded=6, WaypointReached=7, SimulationEnd=8, PlanningFailed=9 | 目标发现/弹药发射/云团生成/进入云团/摧毁/到达目标/侵入管控区/到达航点/仿真结束/规划失败 |
| **InterceptResult** | `Models` | Success=0, Partial=1, Failed=2 | 成功拦截/部分拦截/拦截失败 |
| **DamageStage** | `Models` | Normal=0, EngineAnomaly=1, AttitudeLoss=2, Destroyed=3 | 正常/引擎异常/姿态丧失/摧毁 |
| **ScenarioStatus** | `Models` | Draft=0, Configuring=1, Completed=2 | 草稿/配置中/已完成 |
> **EntitySnapshot.EntityType** 用的是 `EntityType` 枚举int判断实体类型时用 `(EntityType)snap.EntityType` 或直接比较 int 值。
---
## 七、关键数据结构
### EntitySnapshot每帧推送
| 字段 | 类型 | 说明 |
|------|------|------|
| `EntityId` | string | 实体唯一 ID属性 |
| `EntityType` | EntityType | Drone=0/Platform=1/DetectionEquip=2/Cloud=3/Munition=4属性 |
| `PosX/Y/Z` | float | 三维位置Y=高度,米) |
| `RotX/Y/Z` | float | 旋转 |
| `VelX/Y/Z` | float | 瞬时速度 (m/s) |
| `Hp` | float | 无人机血量 (0~1) |
| `DamageStage` | int | 毁伤阶段0正常/1引擎异常/2姿态丧失/3摧毁 |
| `PlatformStateStr` | string? | 平台状态Idle/FlyingToTarget/ReadyToRelease |
| `CloudRadius` | float | 云团有效半径(米) |
| `CloudOpacity` | float | 不透明度 (0~1) |
| `CloudPhase` | int | 扩散阶段 (1/2/3) |
| `CloudElapsed` | float | 云团已存在时间 (s) |
| `ModelId` | string? | 3D 模型 IDUnity 实例化用) |
> 除 `EntityId`/`EntityType` 是属性外,其余均为**公共字段**(非属性)。
### SimEvent仿真事件
```csharp
public class SimEvent
{
public SimEventType Type { get; set; } // 事件类型(见枚举表)
public float OccurredAt { get; set; } // 发生时刻(仿真秒)
public string SourceId { get; set; } // 触发实体 ID
public string TargetId { get; set; } // 目标实体 ID
public string DataJson { get; set; } // 扩展数据JSON
public string Description { get; set; } // 人类可读描述
}
```
### SimulationFrameResultTick 返回值)
```csharp
public class SimulationFrameResult
{
public List<EntitySnapshot> EntitySnapshots { get; set; } // 本帧所有实体快照
public List<SimEvent> NewEvents { get; set; } // 本帧新事件
public SimulationState State { get; set; } // 当前仿真状态
public int FrameIndex { get; set; } // 帧序号
public float SimulationTime { get; set; } // 仿真累计时间
}
```
### PagedResult&lt;T&gt;(分页查询返回值)
```csharp
public class PagedResult<T>
{
public List<T> Items { get; set; } // 当前页数据
public int TotalCount { get; set; } // 总记录数
public int Page { get; set; } // 当前页码(从 1 开始)
public int PageSize { get; set; } // 每页条数
public int TotalPages { get; } // 总页数(计算属性)
}
```
### EnumMetadata / EnumItemGetEnums 返回值)
```csharp
public class EnumItem
{
public string Name { get; set; } // 英文名
public string ChineseName { get; set; } // 中文名
public int Value { get; set; } // 枚举值
}
public class EnumMetadata
{
public List<EnumItem> DroneType { get; set; }
public List<EnumItem> PowerType { get; set; }
public List<EnumItem> PlatformType { get; set; }
public List<EnumItem> AerosolType { get; set; }
public List<EnumItem> WeatherType { get; set; }
public List<EnumItem> WindDirection { get; set; }
public List<EnumItem> SceneType { get; set; }
public List<EnumItem> FormationMode { get; set; }
public List<EnumItem> TriggerMode { get; set; }
public List<EnumItem> ReleaseMode { get; set; }
public List<EnumItem> EquipmentRole { get; set; }
public List<EnumItem> EntityType { get; set; }
public List<EnumItem> SensorType { get; set; }
}
```
### SimulationReport
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 报告 ID主键 |
| `ScenarioId` | string | 想定 ID |
| `ScenarioName` | string | 想定名称 |
| `ScenarioNumber` | string | 想定编号 |
| `CompletedAt` | string | 完成时间UTC ISO 8601 |
| `DroneCount` | int | 无人机总数 |
| `UnitCount` | int | 装备总数 |
| `InterceptResult` | int | 拦截结果0成功/1部分/2失败 |
| `Content` | string | Markdown 报告文本(快速展示用) |
| `ReportDataJson` | string? | 结构化数据 JSONPDF 导出用) |
### DefenseRecommendation防御推荐方案
| 字段 | 类型 | 说明 |
|------|------|------|
| `Success` | bool | 是否规划成功 |
| `Summary` | string | 方案摘要(失败时含原因) |
| `Best` | RecommendOption | 最佳推荐参数 |
### RecommendOption推荐参数
| 字段 | 类型 | 说明 |
|------|------|------|
| `PosX/Y/Z` | double | 推荐抛撒位置Y=高度,米) |
| `Timing` | double | 推荐抛撒时机(仿真秒) |
| `SalvoRounds` | int | 推荐弹药数(齐射发数) |
| `SalvoSpacing` | double | 云团展开间距(米) |
| `EstimatedProbability` | double | 估计拦截概率0~1 |
| `ThreatsEngaged` | int | 已拦截威胁数 |
| `ThreatsUnengaged` | int | 未拦截威胁数 |
---
## 八、运行时实体属性
仿真运行时通过 `runner.Engine.Drones` / `Clouds` / `Munitions` / `Platforms` / `DetectionEntities` 直接访问。
| 实体 | 关键属性 |
|------|---------|
| **DroneEntity** | Id, PosX/Y/Z, Hp, Status(DroneStatus), DroneType, PowerType, Wingspan, CruiseSpeed, Route, TraveledArc, TotalArc, Progress, ModelId |
| **CloudEntity** | Id, PosX/Y/Z, Radius, Density, Phase(1/2/3), Elapsed, IsDissipated, AerosolType |
| **MunitionEntity** | Id, PosX/Y/Z, Velocity(Vx,Vy,Vz), StartX/Y/Z, LaunchTime, ElapsedTime, LaunchAngle, Azimuth, MuzzleVelocity, FlightDuration, TargetX/Y/Z, HasArrived |
| **PlatformEntity** | Id, PosX/Y/Z, CurrentVelocity, State(PlatformState), MunitionCount, CooldownRemaining, MuzzleVelocity, CruiseSpeed, ReleaseAltitude, TargetX/Y/Z, FlightDistance, Ready, CanRelease |
| **DetectionEntity** | Id, PosX/Y/Z, Source, IsInRange(x,y,z,visibility,out range), IsDetected(droneId), ModelId |
| **ControlZoneEntity** | Id, Name, Vertices(List\<Vector3\>), MinAltitude, MaxAltitude, ContainsPoint(x,y,z) |
---
## 九、核心模型完整字段
### 9.1 ScenarioConfig想定配置聚合根 — GetDetail 返回值)
```
ScenarioConfig
Info: Scenario // 想定主表
Scene: CombatScene // 步骤1作战环境
ControlZones: List<ControlZone> // 步骤1管控区域
Drones: List<ScenarioDrone> // 步骤2威胁目标
Units: List<ScenarioUnit> // 步骤3我方部署含探测+发射平台)
Cloud: CloudDispersal // 步骤4云团抛撒配置
Routes: List<RoutePlan> // 步骤5航路规划多批次
WaypointGroups: Dictionary<string, List<Waypoint>> // 按 WaveId 分组的航路点
```
### 9.2 Scenario想定主表
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Id` | string | GUID | 主键 |
| `Name` | string | "" | 想定名称 |
| `ScenarioNumber` | string | "" | 想定编号(唯一) |
| `Description` | string | "" | 描述 |
| `Status` | int | 0 | 0=草稿 1=配置中 2=已完成 |
| `CurrentStep` | int | 1 | 当前配置步骤1~5 |
| `TickRate` | int | 20 | 仿真帧率 |
| `CreatedAt` | string | UTC ISO | 创建时间 |
| `UpdatedAt` | string | UTC ISO | 更新时间 |
### 9.3 CombatScene步骤 1作战环境
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `ScenarioId` | string | "" | 主键 = 想定 ID |
| `SceneType` | int | 1(Plain) | 0=城市 1=平原 2=山区 3=海岸 |
| `TimeOfDay` | string | "12:00" | 时间段 |
| `WeatherType` | int | 0(Sunny) | 0=晴 1=阴 2=雾 3=雨 4=夜 |
| `WindSpeed` | double | 5.0 | 风速 m/s |
| `WindDirection` | int | 0(N) | 0~7 = N/NE/E/SE/S/SW/W/NW |
| `Temperature` | double | 20.0 | 温度 °C |
| `Humidity` | double | 60.0 | 湿度 %RH |
| `Pressure` | double | 1013.0 | 气压 hPa |
| `Visibility` | double | 5000.0 | 能见度 m |
| `SceneWidth` | double | 4000.0 | 场景宽 m |
| `SceneLength` | double | 10000.0 | 场景长 m |
| `SceneHeight` | double | 1000.0 | 场景高 m |
### 9.4 ControlZone管控区域
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `ScenarioId` | string | 想定 ID |
| `Name` | string | 区域名称 |
| `VerticesJson` | string | 多边形顶点 JSON`[{"x":0,"y":0,"z":0},...]` |
| `MinAltitude` | double | 高度下限 m |
| `MaxAltitude` | double | 高度上限 m |
| `OrderIndex` | int | 排序序号 |
### 9.5 ScenarioDrone步骤 2威胁目标
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `ScenarioId` | string | 想定 ID |
| `DroneSpecId` | string | FK → DroneSpec |
| `WaveId` | string | 批次 ID如 "w1" |
| `Quantity` | int | 数量 |
### 9.6 ScenarioUnit步骤 3我方部署
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Id` | string | GUID | 主键 |
| `ScenarioId` | string | "" | 想定 ID |
| `EquipmentRole` | int | 1 | 0=探测设备 1=发射平台 |
| `LaunchPlatformSpecId` | string | "" | FK → LaunchPlatformSpec发射平台时填 |
| `SensorSpecId` | string | "" | FK → SensorSpec探测设备时填 |
| `Quantity` | int | 1 | 数量 |
| `PositionX/Y/Z` | double | 0 | 部署位置Y=高度,米) |
| `AerosolType` | int? | null | 挂载弹药类型0/1/2 |
| `AmmunitionSpecId` | string | "" | FK → AmmunitionSpec挂载的弹药规格前端配置用 |
| `MunitionCount` | int? | null | 弹药数量 |
| `WaveId` | string | "" | 关联批次 |
| `Description` | string | "" | 备注 |
| `Source` | int | 0 | 0=手动 1=算法推荐 |
### 9.7 CloudDispersal步骤 4云团抛撒配置
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `ScenarioId` | string | "" | 主键 = 想定 ID |
| `AerosolType` | int | 0 | 0=惰性气体 1=活性材料 2=活性燃料 |
| `DisperseHeight` | double | 300.0 | 抛撒高度 m |
| `TriggerMode` | int | 0 | 0=时间 1=区域 2=手动 |
| `SpreadAngle` | double | 0 | 扩散方向 ° |
| `ReleaseMode` | int | 0 | 0=单次 1=连续 2=脉冲 |
| `InitialScale` | double | 1000.0 | 初始规模 m³ |
| `Duration` | double | 60.0 | 持续时间 s |
| `ReleaseInterval` | double | 5.0 | 释放间隔 s |
| `TotalAmount` | double | 50.0 | 释放总量 kg |
| `PositionX/Y/Z` | double | 0 | 抛撒位置 |
| `PositionMode` | int | 1 | 0=点选 1=手动输入 2=算法推荐 |
| `Source` | string | "Manual" | "Manual" 或 "Algorithm" |
| `SalvoRounds` | int | 0 | 推荐弹药数(算法填充) |
| `SalvoSpacing` | double | 0 | 云团间距 m算法填充 |
| `EstimatedProbability` | double? | null | 算法估计拦截概率 |
| `RecommendedTiming` | double? | null | 推荐抛撒时机 s |
| `RecommendedPosX/Y/Z` | double? | null | 推荐抛撒位置 |
| `CriticalPosX/Y/Z` | double? | null | 关键拦截位置 |
| `CriticalTiming` | double? | null | 关键拦截时机 s |
### 9.8 RoutePlan步骤 5航路规划
| 字段 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `Id` | string | GUID | 主键 |
| `ScenarioId` | string | "" | 想定 ID |
| `WaveId` | string | "" | 批次 ID |
| `FormationMode` | int | 0 | 0=单机 1=编队 2=蜂群 |
| `LateralSpacing` | double | 50.0 | 横向间距 m |
| `LateralCount` | int? | null | 横向架数null=全部) |
| `LongitudinalCount` | int? | null | 纵深架数null=1 |
| `LongitudinalSpacing` | double | 50.0 | 纵向间距 m |
| `LateralAxis` | int | 2 | 横向展开轴0=X 1=Y(垂直) 2=Z(默认) |
| `LongitudinalAxis` | int | 0 | 纵向串列轴0=X(默认) 1=Y 2=Z |
| `ETA` | string | "" | 预计到达时间 |
### 9.9 Waypoint航路点
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `ScenarioId` | string | 想定 ID |
| `WaveId` | string | 批次 ID |
| `OrderIndex` | int | 顺序索引(从 0 开始) |
| `PosX` | double | X 坐标 m |
| `PosY` | double | Y 坐标 = 高度 m |
| `PosZ` | double | Z 坐标 m |
| `Altitude` | double | 高度 m与 PosY 冗余) |
| `Speed` | double | 该段速度 **km/h** |
---
## 十、基础数据模型完整字段
### 10.1 DroneSpec无人机模板
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `Name` | string | 名称 |
| `DroneType` | int | 0=旋翼 1=固定翼 2=高速 |
| `Model` | string | 型号名称 |
| `Description` | string | 描述 |
| `PowerType` | int | 0=电推 1=活塞 2=喷气 |
| `Wingspan` | double | 翼展 m |
| `TypicalSpeed` | double | 典型速度 km/h |
| `TypicalAltitude` | double | 典型高度 m |
| `ModelId` | string | 3D 模型 IDFK → ModelInfo |
### 10.2 LaunchPlatformSpec发射平台模板
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `Name` | string | 名称 |
| `Description` | string | 描述 |
| `PlatformType` | int | 0=空基 1=地基 |
| `GunCount` | int | 火炮数量 |
| `ChannelsPerGun` | int | 每炮通道数 |
| `ChannelInterval` | double | 同通道发射间隔 s |
| `Cooldown` | double | 射击冷却 s |
| `AmmoChangeTime` | double | 换弹时间 s |
| `MuzzleVelocity` | double | 初速 m/s |
| `CruiseSpeed` | double | 巡航速度 m/s空基 |
| `ReleaseAltitude` | double | 释放高度 m空基 |
| `ModelId` | string | 3D 模型 ID |
### 10.3 SensorSpec传感器模板
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `Name` | string | 名称 |
| `RadarRange` | double | 雷达探测距离 m0=无) |
| `EORange` | double | 光电探测距离 m |
| `IRRange` | double | 红外探测距离 m |
| `HasRadar` | bool | 是否含雷达探测能力(由 RadarRange>0 派生) |
| `HasEO` | bool | 是否含光电探测能力(由 EORange>0 派生) |
| `HasIR` | bool | 是否含红外探测能力(由 IRRange>0 派生) |
| `Accuracy` | double | 探测精度(位置误差 m |
| `MinElevation` | double? | 最小仰角 |
| `MaxElevation` | double? | 最大仰角 |
| `MinDetectAlt` | double? | 最小可探测高度 m |
| `MaxDetectAlt` | double? | 最大可探测高度 m |
| `ModelId` | string | 3D 模型 ID |
### 10.4 AmmunitionSpec弹药模板
| 字段 | 类型 | 说明 |
|------|------|------|
| `Id` | string | 主键 |
| `AerosolType` | int | 0=惰性气体 1=活性材料 2=活性燃料 |
| `Name` | string | 名称 |
| `InitialRadius` | double | 初始半径 m |
| `InitialVolume` | double | 初始体积 m³ |
| `CoreDensity` | double | 核心密度 |
| `EdgeDensity` | double | 边缘密度 |
| `InitialTemperature` | double | 初始温度 |
| `BuoyancyFactor` | double | 浮力系数 |
| `EffectiveConcentration` | double | 有效浓度 |
| `SourceStrength` | double | 源强 Q (kg) |
| `DispersionRateBase` | double | 基础扩散速率 m/s |
| `BurstChargeKg` | double | 爆发药 TNT 当量 kg |
| `TurbulentExpansionK` | double | 湍流扩散系数 |
| `Phase2Duration` | double | Phase 2 持续时间 s |
| `MaxRadius` | double | 最大半径 m |
| `MaxDuration` | double | 最大持续时间 s |
| `ParticlesJson` | string | 粒子渲染参数 JSON |
### 10.5 EnvironmentSpec / FormationTemplate / RouteTemplate / ModelInfo
| 模型 | 关键字段 |
|------|---------|
| **EnvironmentSpec** | Id, Name, WeatherType, WindSpeed, WindDirection, Temperature, Humidity, Pressure, Visibility |
| **FormationTemplate** | Id, Name, FormationMode, LateralCount, LongitudinalCount, LateralSpacing, LongitudinalSpacing, LateralAxis, LongitudinalAxis |
| **RouteTemplate** | Id, Name, WaypointsJson(`[{"x":0,"y":0,"z":0},...]`) |
| **ModelInfo** | Id, Name, EntityType(0=Drone/1=Platform/2=DetectionEquip), Description, FilePath, FileSize, PreviewPath, HasHighResTexture, CreatedAt |
---
## 十一、默认数据
首次运行自动种子,含 6 个 `[Demo]` 预设想定:
| 类别 | 示例 ID |
|------|---------|
| 弹药 | `inert` / `active` / `fuel` |
| 编队 | `single` / `line-3` / `swarm-10` |
| 航线 | `5km-h500` / `10km-h500` / `20km-h500` |
| 火力单元 | `ground-light` / `air-standard` |
| 无人机 | `shahed` / `quadcopter` / `cruise-missile` |
| 传感器 | `radar-mr` / `eo-station` |
| 环境 | `sunny-calm` / `fog` / `night` |
---
## 十二、参考
| 文档 | 位置 |
|------|------|
| 架构设计 | `docs/design/architecture/总体架构设计.md` |
| 实体事件映射 | `docs/design/technical/仿真器实体与事件映射.md` |
| 测试状态 | **270 测试14 秒(全部通过)** |