From 537671ab3a6fa71483d5a60b6f97e45a38aabcbc Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Thu, 18 Jun 2026 16:36:25 +0800 Subject: [PATCH] =?UTF-8?q?DroneProfile=E2=86=92ScenarioDrone=20/=20Equipm?= =?UTF-8?q?entDeployment=E2=86=92ScenarioUnit=20=E9=87=8D=E5=91=BD?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 模型表: ScenarioDrone(原DroneProfile) / ScenarioUnit(原EquipmentDeployment) - ScenarioConfig.Equipment→Units - 数据库旧表清理 + scenariosVersion 4→5 - 238 测试全过 --- CHANGELOG.md | 16 ++--- docs/design/architecture/总体架构设计.md | 44 +++++++------- .../technical/DefensePlanner-技术方案.md | 4 +- docs/design/technical/仿真器实体与事件映射.md | 2 +- .../tasks/实施计划与任务跟踪.md | 4 +- .../changes/2026-06-15-探测驱动规划.md | 4 +- docs/对接文档_Unity前端.md | 28 ++++----- .../Algorithms/AlgorithmTypes.cs | 2 +- .../Algorithms/DefensePlanner.cs | 2 +- src/CounterDrone.Core/DatabaseManager.cs | 22 +++---- src/CounterDrone.Core/DefaultData.cs | 12 ++-- src/CounterDrone.Core/DefaultScenarios.cs | 38 ++++++------ .../Models/ScenarioConfig.cs | 4 +- .../{DroneProfile.cs => ScenarioDrone.cs} | 4 +- ...EquipmentDeployment.cs => ScenarioUnit.cs} | 4 +- ...pository.cs => ScenarioDroneRepository.cs} | 8 +-- ...epository.cs => ScenarioUnitRepository.cs} | 12 ++-- .../Services/IScenarioService.cs | 8 +-- .../Services/ReportGenerator.cs | 10 ++-- .../Services/ReportService.cs | 2 +- .../Services/ScenarioService.cs | 40 ++++++------- .../Simulation/DroneEntity.cs | 2 +- .../Simulation/PlatformEntity.cs | 2 +- .../Simulation/SimulationEngine.cs | 6 +- .../CounterDrone.Core/CounterDrone.Core.dll | Bin 177664 -> 177664 bytes .../Scripts/Managers/ManagerVerification.cs | 6 +- .../Scripts/Managers/ScenarioManager.cs | 8 +-- .../Scripts/Managers/SimulationBootstrap.cs | 10 ++-- .../Scripts/Managers/SimulationRunner.cs | 4 +- .../DatabaseManagerTests.cs | 2 +- .../DefensePlannerTests.cs | 4 +- .../DroneEntityTests.cs | 4 +- .../CounterDrone.Core.Tests/EdgeCaseTests.cs | 16 ++--- .../FullPipelineTests.cs | 18 +++--- .../ReportGeneratorTests.cs | 8 +-- .../ReportServiceTests.cs | 12 ++-- .../RepositoryEdgeTests.cs | 18 +++--- .../ScenarioServiceTests.cs | 56 +++++++++--------- .../SimulationEngineTests.cs | 26 ++++---- unity_plugins/CounterDrone.Core.dll | Bin 177664 -> 177664 bytes 40 files changed, 237 insertions(+), 235 deletions(-) rename src/CounterDrone.Core/Models/{DroneProfile.cs => ScenarioDrone.cs} (94%) rename src/CounterDrone.Core/Models/{EquipmentDeployment.cs => ScenarioUnit.cs} (97%) rename src/CounterDrone.Core/Repository/{DroneProfileRepository.cs => ScenarioDroneRepository.cs} (54%) rename src/CounterDrone.Core/Repository/{EquipmentDeploymentRepository.cs => ScenarioUnitRepository.cs} (55%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 261dff4..f18c33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - **空基/地基物理统一**:去除 DefensePlanner 所有平台类型分支,统一用 `InterceptCalculator.Compute`(删 `ComputeHorizontal`),统一用 `MuzzleVelocity`(删 `CruiseSpeed` 分支) - **抛物线选解策略**:两解逐一计算距离匹配 + `fireTime>0` 可行性,选最早拦截的解(非简单选高角/低角) - **`TargetType` → `DroneType`**:删 `Electric`/`Piston`,重编号 `HighSpeed=2`,`TargetConfig.DroneType` 属性同步改名 -- **`TargetConfig` → `DroneProfile`**:核心模型重命名 +- **`TargetConfig` → `ScenarioDrone`**:核心模型重命名 - **`SimTask` → `Scenario`**:想定主表重命名,`ScenarioNumber` 取代 `TaskNumber` - **`TaskFullConfig` → `ScenarioConfig`**:聚合配置重命名,`Task` 属性 → `Info` - **`TaskId` → `ScenarioId`**:所有 FK 重命名 @@ -17,9 +17,9 @@ ### Added — 模型业务属性 -- **`DroneProfile` / `DroneSpec`**:+`Model`(型号)、+`Description`(描述/用途) +- **`ScenarioDrone` / `DroneSpec`**:+`Model`(型号)、+`Description`(描述/用途) - **`Scenario`**:+`Description`(想定描述) -- **`EquipmentDeployment` / `FireUnitSpec`**:+`Description` +- **`ScenarioUnit` / `FireUnitSpec`**:+`Description` - **`ReportManager.Export(reportId, outputDir)`**:支持指定导出路径 ### Changed — 数据库迁移 @@ -96,7 +96,7 @@ - **EarliestDetection 采样法**:线段-圆求交改为沿航路采样(步长≤50m)调 `IsInCoverage` - **DetectionEntity**:运行时探测实体 + per-drone 状态机(Undetected ⇄ Detected),Tick 第5步扫描 - **实时探测事件**:`OnTargetDetected` / `SimEventType.TargetDetected` / `PlanningFailed` -- **EquipmentDeployment + DetectionSource**:+4 个 3D 球冠字段(MinElevation/MaxElevation/MinDetectAlt/MaxDetectAlt) +- **ScenarioUnit + DetectionSource**:+4 个 3D 球冠字段(MinElevation/MaxElevation/MinDetectAlt/MaxDetectAlt) - **默认数据全部加入 3D 球冠参数**(地基 -5°~85°、空基 -80°~30°) ### Added — Planner 诊断 + 拦截点计算 @@ -109,7 +109,7 @@ ### Changed — 消除硬编码 -- **速度从 waypoint.Speed 读取**:`DroneEntity.CruiseSpeed` / `GetDroneSpeedKph` 不再用 `DroneProfile.TypicalSpeed` +- **速度从 waypoint.Speed 读取**:`DroneEntity.CruiseSpeed` / `GetDroneSpeedKph` 不再用 `ScenarioDrone.TypicalSpeed` - **`AmmunitionSpec` +`Phase2Duration`**(30s):`CloudExpansionModel` 不再写死 `30f` - **`GetArrivalTime` 基于 DetectArc**:从探测点到中点算到达时间(而非航路起点) - **TestData 改为从 seeded 数据库读取**(不再内嵌 JSON 重复 defaults.json) @@ -137,7 +137,7 @@ ### Changed — 数据模型重命名 -- **`GroupId` → `WaveId`**DroneProfile 四表的编组外键重命名为批次外键 +- **`GroupId` → `WaveId`**ScenarioDrone 四表的编组外键重命名为批次外键 - **RoutePlan 索引重命名**:`(ScenarioId, GroupId)` → `(ScenarioId, WaveId)` - **RoutePlanRepository / WaypointRepository**:`GetByScenarioAndGroup` → `GetByScenarioAndWave` - **IScenarioService.SaveRoute**:`groupId` 参数 → `waveId` @@ -173,8 +173,8 @@ - **DetectionCalculator 静态工具类**:探测能力评估的唯一实现(与 Kinematics/RouteGeometry 同范式)。光电受 Visibility 衰减(`有效=基准×min(1,Visibility/基准)`),雷达/红外不受影响;统一信息网络找最早探测点(线段-圆求交) - **探测源模型**(DetectionSource):独立探测设备 + 火力单元自带探测统一表达(雷达/光电/红外三距离 + 精度) -- **EquipmentDeployment 扩展**:删单一 `DetectionRadius`,加 `RadarRange/EORange/IRange/DetectionAccuracy` 四字段 -- **FireUnit 探测字段激活**:BuildFireUnits 从 EquipmentDeployment 读取并赋值(原为死代码) +- **ScenarioUnit 扩展**:删单一 `DetectionRadius`,加 `RadarRange/EORange/IRange/DetectionAccuracy` 四字段 +- **FireUnit 探测字段激活**:BuildFireUnits 从 ScenarioUnit 读取并赋值(原为死代码) - **planner 接口扩展**:`IDefensePlanner.Plan` 加第 4 参数 `detectionSources` - **PlannerConfig 加 DefaultDetectionAccuracy**(无探测时回退精度) - 单元测试:DetectionCalculator 13 项、探测驱动规划 2 项 diff --git a/docs/design/architecture/总体架构设计.md b/docs/design/architecture/总体架构设计.md index 9731286..186bba1 100644 --- a/docs/design/architecture/总体架构设计.md +++ b/docs/design/architecture/总体架构设计.md @@ -6,7 +6,7 @@ > **变更**:V14 新增 DataService 基础数据 CRUD + FrameDataStore 内存回放架构 > **Unity 版本**:22.3.62 > **.NET 版本**:.NET Standard 2.1 -> **变更**:V13 探测行为决策固化——5 项规则确认(回退 Undetected / 融合取最早-同刻取精度高 / 不影响 FireSchedule / 盲区仅可视化 / 3D 球冠);新增 14.2.1 三维球冠几何(IsInCoverage)+ 14.7 数据模型变更清单;EquipmentDeployment 表加 4 个 3D 探测字段;DetectionSource 同步扩展 +> **变更**:V13 探测行为决策固化——5 项规则确认(回退 Undetected / 融合取最早-同刻取精度高 / 不影响 FireSchedule / 盲区仅可视化 / 3D 球冠);新增 14.2.1 三维球冠几何(IsInCoverage)+ 14.7 数据模型变更清单;ScenarioUnit 表加 4 个 3D 探测字段;DetectionSource 同步扩展 > **历史变更**:V12 探测双链路设计(静态规划+实时探测待实现);V11 文档校准 + 第十三章性能设计;V10 Group 表移除,批次+火力单元概念升级 --- @@ -207,13 +207,13 @@ enum FormationMode { Single = 0, Formation = 1, Swarm = 2 } // === 核心作战概念 === // 火力单元(FireUnit):能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。 // 包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环系统。 -// 配置层 = EquipmentDeployment(LaunchPlatform),含探测字段 + 打击字段。 +// 配置层 = ScenarioUnit(LaunchPlatform),含探测字段 + 打击字段。 // 独立探测节点(DetectionNode):只探测不打击的侦查节点(如前沿警戒雷达)。 -// 配置层 = EquipmentDeployment(Detection),仅含探测字段。 +// 配置层 = ScenarioUnit(Detection),仅含探测字段。 // 两者都往统一信息网络送探测信息,planner 基于融合后的探测边界规划。 // 无人机批次(DroneWave):有共同航路的一组无人机(攻击方)。 // 批次关联航路但不独占(多个批次可共享同一条航路)。 -// 配置层 = DroneProfile(含 WaveId) + RoutePlan(ScenarioId, WaveId) + Waypoints。 +// 配置层 = ScenarioDrone(含 WaveId) + RoutePlan(ScenarioId, WaveId) + Waypoints。 // === 运行时 === enum EntityType { Drone = 0, Platform = 1, DetectionEquip = 2, Cloud = 3, Munition = 4 } @@ -266,10 +266,10 @@ CounterDroneBackend_Data/ Scenario — 仿真任务主表 CombatScene — 步骤1:作战场景(含24h时间、天气) ControlZone — 步骤1+:管控区域/电子围栏(NEW) - DroneProfile — 步骤2:目标配置(含 WaveId 关联批次) - EquipmentDeployment — 步骤3:装备部署(火力单元含探测+打击,或独立探测节点) + ScenarioDrone — 步骤2:目标配置(含 WaveId 关联批次) + ScenarioUnit — 步骤3:装备部署(火力单元含探测+打击,或独立探测节点) CloudDispersal — 步骤4:云团抛撒配置 - RoutePlan — 步骤5:航路规划(1:N Scenario,多批次;批次关联但不独占航路,FK 关联 DroneProfile.WaveId) + RoutePlan — 步骤5:航路规划(1:N Scenario,多批次;批次关联但不独占航路,FK 关联 ScenarioDrone.WaveId) Waypoint — 航路点 SimulationReport — 仿真报告 SimEvent — 仿真事件(时序图数据源) @@ -279,9 +279,9 @@ CounterDroneBackend_Data/ ``` > **核心作战概念**: -> - **火力单元(FireUnit)**:能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环。配置层 = `EquipmentDeployment(LaunchPlatform)`。 -> - **独立探测节点(DetectionNode)**:只探测不打击的侦查节点(如前沿警戒雷达)。配置层 = `EquipmentDeployment(Detection)`。 -> - **无人机批次(DroneWave)**:有共同航路的一组无人机(攻击方)。批次关联航路但不独占(多个批次可共享同一条航路)。配置层 = `DroneProfile(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints`。 +> - **火力单元(FireUnit)**:能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环。配置层 = `ScenarioUnit(LaunchPlatform)`。 +> - **独立探测节点(DetectionNode)**:只探测不打击的侦查节点(如前沿警戒雷达)。配置层 = `ScenarioUnit(Detection)`。 +> - **无人机批次(DroneWave)**:有共同航路的一组无人机(攻击方)。批次关联航路但不独占(多个批次可共享同一条航路)。配置层 = `ScenarioDrone(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints`。 > - 原来的 `Group`(编组)表已移除:无人机编队概念升级为"批次",装备编组概念不再需要(火力单元是独立作战单位)。 ### 4.3 业务表详细设计 @@ -349,7 +349,7 @@ CounterDroneBackend_Data/ > 管控区域在场景设置(地形+天气)之后配置。无人机进入管控区域 → 触发 ZoneIntruded 事件 → 该无人机任务失败。 -#### DroneProfile — 步骤2:无人机批次参数 +#### ScenarioDrone — 步骤2:无人机批次参数 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| @@ -373,7 +373,7 @@ CounterDroneBackend_Data/ | 固定翼 | 3.5 | 150 | 1000 | | 高速目标 | 1.5 | 300 | 2000 | -#### EquipmentDeployment — 步骤3:装备部署(火力单元 + 独立探测节点) +#### ScenarioUnit — 步骤3:装备部署(火力单元 + 独立探测节点) | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| @@ -468,10 +468,10 @@ CounterDroneBackend_Data/ #### ~~Group~~ — 编组(已移除) > **概念升级**:原 Group 表的两种类型已分别升级: -> - `DroneFleet`(无人机编队)→ **无人机批次(DroneWave)**:由 `DroneProfile(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints` 隐式表达。批次关联航路但不独占(多个批次可共享同一条航路)。 +> - `DroneFleet`(无人机编队)→ **无人机批次(DroneWave)**:由 `ScenarioDrone(WaveId)` + `RoutePlan(ScenarioId, WaveId)` + `Waypoints` 隐式表达。批次关联航路但不独占(多个批次可共享同一条航路)。 > - `EquipmentGroup`(装备编组)→ **不再需要**。火力单元是独立作战的最小单位,不需要上层编组。 > -> 数据层:`Group` 表保留但不再主动使用(向后兼容)。`DroneProfile.WaveId` 语义为"批次 ID"。 +> 数据层:`Group` 表保留但不再主动使用(向后兼容)。`ScenarioDrone.WaveId` 语义为"批次 ID"。 ### 4.4 运行时表 @@ -1395,8 +1395,8 @@ interface IScenarioService // 步骤保存 void SaveScene(string scenarioId, CombatScene scene); void SaveControlZones(string scenarioId, List zones); // NEW - void SaveDroneProfile(string scenarioId, DroneProfile target); - void SaveDeployment(string scenarioId, List equips); // 重构 + void SaveScenarioDrone(string scenarioId, ScenarioDrone target); + void SaveDeployment(string scenarioId, List equips); // 重构 void SaveCloudDispersal(string scenarioId, CloudDispersal cloud); void SaveRoute(string scenarioId, RoutePlan route, List wps); void UpdateStep(string scenarioId, int step); @@ -1457,17 +1457,17 @@ Scenario "城市防御演习" ├── CloudDispersal(共享:云团参数,具体拦截方案由算法确定) │ ├── 批次 A(活塞×3) ← WaveId -│ ├── DroneProfile(WaveId=A,类型=活塞,数量=3) +│ ├── ScenarioDrone(WaveId=A,类型=活塞,数量=3) │ ├── RoutePlan(ScenarioId + WaveId=A,航路 A:北→南) │ └── Waypoint[] │ ├── 批次 B(喷气×2) ← WaveId -│ ├── DroneProfile(WaveId=B,类型=高速,数量=2) +│ ├── ScenarioDrone(WaveId=B,类型=高速,数量=2) │ ├── RoutePlan(ScenarioId + WaveId=B,航路 B:西→东) │ └── Waypoint[] │ └── 批次 C(旋翼×5) ← WaveId - ├── DroneProfile(WaveId=C,类型=旋翼,数量=5) + ├── ScenarioDrone(WaveId=C,类型=旋翼,数量=5) ├── RoutePlan(ScenarioId + WaveId=C,航路 C) └── Waypoint[] ``` @@ -1518,7 +1518,7 @@ engine.SetFireSchedule(merged); | 表 | 变更 | |------|------| | `RoutePlan` | PK 为 (ScenarioId, WaveId) 复合键 | -| `DroneProfile` | 不变(已有 WaveId) | +| `ScenarioDrone` | 不变(已有 WaveId) | | `Waypoint` | 关联 `(ScenarioId, WaveId)` 定位航路 | `IScenarioService` 的 `SaveRoute` 签名: @@ -1909,7 +1909,7 @@ FOR EACH 飞行无人机(维护"本帧已发现该目标的最优探测设备" | 改动项 | 位置 | 内容 | 状态 | |--------|------|------|------| -| **EquipmentDeployment 表加字段** | 4.3 | `MinElevation`/`MaxElevation`(度)、`MinDetectAlt`/`MaxDetectAlt`(m),均可空 | 待加列 | +| **ScenarioUnit 表加字段** | 4.3 | `MinElevation`/`MaxElevation`(度)、`MinDetectAlt`/`MaxDetectAlt`(m),均可空 | 待加列 | | **DetectionSource 类加字段** | 14.5.1 | 同上 4 个三维几何字段 | 待加属性 | | **DetectionCalculator.IsInCoverage** | 14.2.1 | 三维球冠判定(水平距离 + 俯仰角 + 高度门限),planner 与实时探测共用 | 待实现 | | **DetectionCalculator.EffectiveRange** | 14.2 | 现有签名不变(返回综合水平距离基准),3D 判定在其上层 | 已实现(基础) | @@ -1917,7 +1917,7 @@ FOR EACH 飞行无人机(维护"本帧已发现该目标的最优探测设备" | **SimulationEngine 加 _detectionEntities** | 5.1 | Initialize 时从配置构建 `List`,Tick 第5步遍历 | 待实现 | | **Tick 第5步实时探测** | 5.3 / 14.5.2 | 扫描 + 状态机 + 决策1/2/3/4 落地,触发 `TargetDetected` | 待实现 | -> **数据库迁移**:`EquipmentDeployment` 新增 4 列均为 NULLABLE,不影响存量数据;`SimulationEngine.BuildDetectionSources` 读取新字段构造 `DetectionSource`(缺失时按"无角度/高度限制"的退化球冠处理,等价当前 2D 行为,保证平滑过渡)。 +> **数据库迁移**:`ScenarioUnit` 新增 4 列均为 NULLABLE,不影响存量数据;`SimulationEngine.BuildDetectionSources` 读取新字段构造 `DetectionSource`(缺失时按"无角度/高度限制"的退化球冠处理,等价当前 2D 行为,保证平滑过渡)。 --- diff --git a/docs/design/technical/DefensePlanner-技术方案.md b/docs/design/technical/DefensePlanner-技术方案.md index c2a9996..1dc3c1a 100644 --- a/docs/design/technical/DefensePlanner-技术方案.md +++ b/docs/design/technical/DefensePlanner-技术方案.md @@ -59,7 +59,7 @@ class FireUnit { ```csharp class DroneWave { string WaveId; // 批次 ID - DroneProfile Profile; // 类型、数量、动力、翼展、速度、高度 + ScenarioDrone Profile; // 类型、数量、动力、翼展、速度、高度 List Waypoints; // 航路点 float ArrivalTime; // 预计算:到达防御区域中点的时间 s } @@ -344,7 +344,7 @@ PlannerResult.Best.MergedSchedule ### 🔒 不变 - `Kinematics` / `GaussianPuffDispersion` / `DamageModelRouter` -- 所有 Repository 和数据模型(EquipmentDeployment 新增 GunCount/ChannelsPerGun/ChannelInterval) +- 所有 Repository 和数据模型(ScenarioUnit 新增 GunCount/ChannelsPerGun/ChannelInterval) - `ScenarioService` 接口 --- diff --git a/docs/design/technical/仿真器实体与事件映射.md b/docs/design/technical/仿真器实体与事件映射.md index 644bafb..50df606 100644 --- a/docs/design/technical/仿真器实体与事件映射.md +++ b/docs/design/technical/仿真器实体与事件映射.md @@ -39,7 +39,7 @@ ## 数据流 ``` -想定配置(EquipmentDeployment + DroneProfile + RoutePlan + CombatScene) +想定配置(ScenarioUnit + ScenarioDrone + RoutePlan + CombatScene) │ ▼ SimulationEngine.Initialize() diff --git a/docs/implementation/tasks/实施计划与任务跟踪.md b/docs/implementation/tasks/实施计划与任务跟踪.md index e560463..ab7c29b 100644 --- a/docs/implementation/tasks/实施计划与任务跟踪.md +++ b/docs/implementation/tasks/实施计划与任务跟踪.md @@ -181,7 +181,7 @@ Phase 12 ✅ 空基/地基统一规划 + 编队轴 + LaneDivider | 8.1.1 | 探测设备搜索逻辑 | ✅ 事前规划:`DetectionCalculator` 算统一信息网络最早探测点,planner 基于探测边界算到达时间;天气衰减光电;精度影响散布。**实时探测设计完成**(总体架构设计第十四章),待 Phase 10 开发 | | 8.1.2 | 蜂群运动模型 | ⬜ `FormationMode.Swarm` 枚举已定义,需差异化行为(随机扰动、个体差异) | | 8.1.3 | 空基平台 + DefensePlanner | ✅ 五步规划引擎,通道模型,物理间隔错发,路径积分毁伤判定 | -| 8.1.4 | 预置典型目标库 | ⬜ 具体无人机型号 JSON 配置(如 DJI Mavic 3、Shahed-136 等),导入 `DroneProfile` 默认值 | +| 8.1.4 | 预置典型目标库 | ⬜ 具体无人机型号 JSON 配置(如 DJI Mavic 3、Shahed-136 等),导入 `ScenarioDrone` 默认值 | | 8.1.5 | 毁伤曲线参数校准 | ✅ RequiredExposureSeconds 替代硬编码,密度阈值统一在引擎检查 | | 8.1.6 | Fallback/default 清理 | ✅ 删除所有硬编码默认值和静默 fallback,参数缺失即报错 | @@ -241,7 +241,7 @@ Phase 12 ✅ 空基/地基统一规划 + 编队轴 + LaneDivider | # | 任务 | 预估 | 状态 | 说明 | |---|------|------|------|------| -| T1.1 | EquipmentDeployment 加 4 列 | 0.5h | ✅ | `MinElevation`/`MaxElevation`/`MinDetectAlt`/`MaxDetectAlt`,均 NULLABLE | +| T1.1 | ScenarioUnit 加 4 列 | 0.5h | ✅ | `MinElevation`/`MaxElevation`/`MinDetectAlt`/`MaxDetectAlt`,均 NULLABLE | | T1.2 | DetectionSource 类扩展 | 0.5h | ✅ | 同步加 4 个三维几何属性(float.MaxValue=无限制,退化 2D) | | T1.3 | BuildDetectionSources 读取新字段 | 0.5h | ✅ | 缺失时退化球冠(float.MaxValue),保证存量数据平滑过渡 | diff --git a/docs/requirements/changes/2026-06-15-探测驱动规划.md b/docs/requirements/changes/2026-06-15-探测驱动规划.md index 7c7a4d0..879e438 100644 --- a/docs/requirements/changes/2026-06-15-探测驱动规划.md +++ b/docs/requirements/changes/2026-06-15-探测驱动规划.md @@ -14,14 +14,14 @@ planner 是参谋,只能基于侦查信息制定方案。假设我方有统一 ### 变更内容 1. **新增 DetectionCalculator**:探测能力评估的唯一实现。光电受 Visibility 衰减,雷达/红外不受影响;统一信息网络找最早探测点 -2. **EquipmentDeployment 扩展**:删单一 DetectionRadius,加 RadarRange/EORange/IRange/DetectionAccuracy +2. **ScenarioUnit 扩展**:删单一 DetectionRadius,加 RadarRange/EORange/IRange/DetectionAccuracy 3. **FireUnit 探测字段激活**:BuildFireUnits 赋值(原为死代码) 4. **planner 接口扩展**:Plan 加第 4 参数 detectionSources;到达时间基于探测边界 5. **SimulationEngine 构建探测源**:独立探测 + 火力单元自带探测统一传入 planner ## 影响范围 -- [x] 接口变更:IDefensePlanner.Plan 加参数;EquipmentDeployment 字段变更(删 DetectionRadius) +- [x] 接口变更:IDefensePlanner.Plan 加参数;ScenarioUnit 字段变更(删 DetectionRadius) - [ ] 数据库变更:新字段为 nullable,SQLite 自动处理 - [ ] UI 变更 - [x] 文档变更:实施计划 8.1.1、CHANGELOG、VERSION diff --git a/docs/对接文档_Unity前端.md b/docs/对接文档_Unity前端.md index 6c699fa..d9c7a32 100644 --- a/docs/对接文档_Unity前端.md +++ b/docs/对接文档_Unity前端.md @@ -72,7 +72,7 @@ var scenarioId = demos.Items[0].Id; // 方式 B:自定义 var sc = scenario.CreateScenario("我的想定", ""); scenario.SaveScene(sc.Id, new CombatScene { WindSpeed = 5, WindDirection = (int)WindDirection.E }); -scenario.SaveDroneProfile(sc.Id, new DroneProfile { +scenario.SaveScenarioDrone(sc.Id, new ScenarioDrone { WaveId = "w1", Model = "Shahed-136", Description = "活塞巡飞弹", Quantity = 1, DroneType = (int)DroneType.FixedWing, TypicalSpeed = 200, TypicalAltitude = 500 @@ -81,7 +81,7 @@ scenario.SaveRoute(sc.Id, "w1", new RoutePlan(), new List { new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, new() { PosX = 10000, PosY = 500, PosZ = 0, Speed = 200 } }); -scenario.SaveDeployment(sc.Id, new List { +scenario.SaveDeployment(sc.Id, new List { new() { EquipmentRole = 1, PlatformType = 1, Quantity = 1, PositionX = 5000, AerosolType = 0, MunitionCount = 8, Description = "地面轻型平台" } @@ -150,7 +150,7 @@ mgr.DataService.GetAllEnvironments() / GetAllFormations() / GetAllRoutes() // 想定 CRUD mgr.CreateScenario(name, number) / mgr.DeleteScenario(id) mgr.Search(kw, from, to, page, size) -mgr.SaveScene / SaveDroneProfile / SaveDeployment / SaveCloud / SaveRoute / UpdateStep +mgr.SaveScene / SaveScenarioDrone / SaveDeployment / SaveCloud / SaveRoute / UpdateStep mgr.GetDetail(id) → ScenarioConfig ``` @@ -223,8 +223,8 @@ replay.TotalFrames / replay.GetFrame(index) |------|------|------| | `Scenario` | `Scenario` | 想定任务 | | `CombatScene` | `CombatScene` | 作战环境 | -| `DroneProfile` | `DroneProfile` | 无人机批次参数 | -| `EquipmentDeployment` | `EquipmentDeployment` | 装备部署 | +| `ScenarioDrone` | `ScenarioDrone` | 无人机批次参数 | +| `ScenarioUnit` | `ScenarioUnit` | 装备部署 | | `CloudDispersal` | `CloudDispersal` | 云团抛撒配置 | | `RoutePlan` | `RoutePlan` | 航路规划 | | `Waypoint` | `Waypoint` | 航路点 | @@ -243,17 +243,17 @@ replay.TotalFrames / replay.GetFrame(index) | 模型 | 属性 | 类型 | 说明 | |------|------|------|------| -| `DroneProfile` | `Model` | string | 型号 | -| `DroneProfile` | `Description` | string | 描述/用途 | -| `DroneProfile` | `DroneType` | DroneType | Rotor / FixedWing / HighSpeed | -| `DroneProfile` | `PowerType` | PowerType | Electric / Piston / Jet | -| `DroneProfile` | `TypicalSpeed` | double | 典型速度 (km/h) | -| `DroneProfile` | `TypicalAltitude` | double | 典型高度 (m) | +| `ScenarioDrone` | `Model` | string | 型号 | +| `ScenarioDrone` | `Description` | string | 描述/用途 | +| `ScenarioDrone` | `DroneType` | DroneType | Rotor / FixedWing / HighSpeed | +| `ScenarioDrone` | `PowerType` | PowerType | Electric / Piston / Jet | +| `ScenarioDrone` | `TypicalSpeed` | double | 典型速度 (km/h) | +| `ScenarioDrone` | `TypicalAltitude` | double | 典型高度 (m) | | `Scenario` | `ScenarioNumber` | string | 想定编号(唯一) | | `Scenario` | `Description` | string | 想定描述 | -| `EquipmentDeployment` | `Description` | string | 部署备注 | -| `EquipmentDeployment` | `MuzzleVelocity` | double? | 初速 (m/s) | -| `EquipmentDeployment` | `CruiseSpeed` | double? | 巡航速度 (m/s),空基专用 | +| `ScenarioUnit` | `Description` | string | 部署备注 | +| `ScenarioUnit` | `MuzzleVelocity` | double? | 初速 (m/s) | +| `ScenarioUnit` | `CruiseSpeed` | double? | 巡航速度 (m/s),空基专用 | | `DroneSpec` | `Model` | string | 型号 | | `DroneSpec` | `Description` | string | 描述/用途 | | `FireUnitSpec` | `Description` | string | 描述/用途 | diff --git a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs index 8bc0c70..b9a2d3a 100644 --- a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs +++ b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs @@ -51,7 +51,7 @@ namespace CounterDrone.Core.Algorithms public class DroneWave { public string WaveId { get; set; } = string.Empty; - public DroneProfile Profile { get; set; } = new(); + public ScenarioDrone Profile { get; set; } = new(); public RoutePlan Route { get; set; } = new(); public List Waypoints { get; set; } = new(); diff --git a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs index e182b24..93f87e2 100644 --- a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs @@ -90,7 +90,7 @@ namespace CounterDrone.Core.Algorithms // Step 1: 威胁指数 // ═══════════════════════════════════════════════ - private static float CalcThreatIndex(PlannerConfig config, DroneProfile target) + private static float CalcThreatIndex(PlannerConfig config, ScenarioDrone target) { float typeCoef = config.TypeCoefficient.GetValueOrDefault((DroneType)target.DroneType, 1f); float speedCoef = (float)target.TypicalSpeed / 60f; diff --git a/src/CounterDrone.Core/DatabaseManager.cs b/src/CounterDrone.Core/DatabaseManager.cs index 975b621..706b1d9 100644 --- a/src/CounterDrone.Core/DatabaseManager.cs +++ b/src/CounterDrone.Core/DatabaseManager.cs @@ -82,34 +82,36 @@ namespace CounterDrone.Core .FirstOrDefault(m => m.Key == "scenariosVersion")?.Value; // 版本未变 → 跳过 - if (currentVersion == "4") return; + if (currentVersion == "5") return; var defaults = DefaultData.Load(_paths); var scenario = new ScenarioService( new ScenarioRepository(db), new CombatSceneRepository(db), - new ControlZoneRepository(db), new DroneProfileRepository(db), - new EquipmentDeploymentRepository(db), new CloudDispersalRepository(db), + new ControlZoneRepository(db), new ScenarioDroneRepository(db), + new ScenarioUnitRepository(db), new CloudDispersalRepository(db), new RoutePlanRepository(db), new WaypointRepository(db)); DefaultScenarios.Seed(scenario, defaults); - db.InsertOrReplace(new MetaEntry { Key = "scenariosVersion", Value = "4" }); + db.InsertOrReplace(new MetaEntry { Key = "scenariosVersion", Value = "5" }); } private void CreateMainTables(SQLiteConnection db) { - // 删除旧版本残留表(V1.6 重命名) + // 删除旧版本残留表 db.Execute("DROP TABLE IF EXISTS SimTask"); db.Execute("DROP TABLE IF EXISTS TargetConfig"); - db.Execute("DROP TABLE IF EXISTS SimulationReport"); // TaskNumber→ScenarioNumber + db.Execute("DROP TABLE IF EXISTS DroneProfile"); + db.Execute("DROP TABLE IF EXISTS EquipmentDeployment"); + db.Execute("DROP TABLE IF EXISTS SimulationReport"); db.CreateTable(); db.CreateTable(); db.CreateTable(); db.CreateTable(); db.CreateTable(); - db.CreateTable(); - db.CreateTable(); + db.CreateTable(); + db.CreateTable(); db.CreateTable(); db.CreateTable(); db.CreateIndex("RoutePlan", new[] { "ScenarioId", "WaveId" }, true); @@ -124,8 +126,8 @@ namespace CounterDrone.Core db.CreateIndex("Scenario", "ScenarioNumber"); db.CreateIndex("ControlZone", "ScenarioId"); - db.CreateIndex("DroneProfile", "ScenarioId"); - db.CreateIndex("EquipmentDeployment", "ScenarioId"); + db.CreateIndex("ScenarioDrone", "ScenarioId"); + db.CreateIndex("ScenarioUnit", "ScenarioId"); db.CreateIndex("Waypoint", "ScenarioId"); db.CreateIndex("SimulationReport", "ScenarioId"); } diff --git a/src/CounterDrone.Core/DefaultData.cs b/src/CounterDrone.Core/DefaultData.cs index 6f54dd1..8b17ca5 100644 --- a/src/CounterDrone.Core/DefaultData.cs +++ b/src/CounterDrone.Core/DefaultData.cs @@ -125,10 +125,10 @@ namespace CounterDrone.Core public double? MinDetectAlt { get; set; } public double? MaxDetectAlt { get; set; } - public EquipmentDeployment ToEquipmentDeployment(AerosolType ammoType, int quantity, + public ScenarioUnit ToScenarioUnit(AerosolType ammoType, int quantity, double posX, double posY, double posZ) { - return new EquipmentDeployment + return new ScenarioUnit { EquipmentRole = (int)Models.EquipmentRole.LaunchPlatform, PlatformType = PlatformType, @@ -171,9 +171,9 @@ namespace CounterDrone.Core public double TypicalSpeed { get; set; } public double TypicalAltitude { get; set; } - public DroneProfile ToDroneProfile(string waveId = "default") + public ScenarioDrone ToScenarioDrone(string waveId = "default") { - return new DroneProfile + return new ScenarioDrone { WaveId = waveId, DroneType = DroneType, @@ -205,9 +205,9 @@ namespace CounterDrone.Core public double? MinDetectAlt { get; set; } public double? MaxDetectAlt { get; set; } - public EquipmentDeployment ToEquipmentDeployment(double posX, double posY, double posZ) + public ScenarioUnit ToScenarioUnit(double posX, double posY, double posZ) { - return new EquipmentDeployment + return new ScenarioUnit { EquipmentRole = (int)Models.EquipmentRole.Detection, Quantity = 1, diff --git a/src/CounterDrone.Core/DefaultScenarios.cs b/src/CounterDrone.Core/DefaultScenarios.cs index 42f0529..e0be922 100644 --- a/src/CounterDrone.Core/DefaultScenarios.cs +++ b/src/CounterDrone.Core/DefaultScenarios.cs @@ -37,10 +37,10 @@ namespace CounterDrone.Core { var t = s.CreateScenario("[Demo] 无防御-无人机抵达目标", ""); s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveDroneProfile(t.Id, d.Drones.First(p => p.Id == "shahed").ToDroneProfile()); + s.SaveScenarioDrone(t.Id, d.Drones.First(p => p.Id == "shahed").ToScenarioDrone()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), R("3km-h400", 600, d)); s.SaveCloudDispersal(t.Id, new CloudDispersal()); - s.SaveDeployment(t.Id, new List()); + s.SaveDeployment(t.Id, new List()); s.UpdateStep(t.Id, 5); } @@ -48,10 +48,10 @@ namespace CounterDrone.Core { var t = s.CreateScenario("[Demo] 管控区域侵入", ""); s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveDroneProfile(t.Id, d.Drones.First(p => p.Id == "electric-scout").ToDroneProfile()); + s.SaveScenarioDrone(t.Id, d.Drones.First(p => p.Id == "electric-scout").ToScenarioDrone()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), R("3km-h300", 300, d)); s.SaveCloudDispersal(t.Id, new CloudDispersal()); - s.SaveDeployment(t.Id, new List()); + s.SaveDeployment(t.Id, new List()); s.SaveControlZones(t.Id, new List { new ControlZone @@ -71,15 +71,15 @@ namespace CounterDrone.Core scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.W; s.SaveScene(t.Id, scene); - s.SaveDroneProfile(t.Id, d.Drones.First(p => p.Id == "shahed").ToDroneProfile()); + s.SaveScenarioDrone(t.Id, d.Drones.First(p => p.Id == "shahed").ToScenarioDrone()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), new List { new() { PosX = 6600, PosY = 500, PosZ = 0, Speed = 200 }, new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, }); - s.SaveDeployment(t.Id, new List + s.SaveDeployment(t.Id, new List { - d.FireUnits.First(f => f.Id == "ground-light").ToEquipmentDeployment(AerosolType.InertGas, 1, 0, 0, 50), + d.FireUnits.First(f => f.Id == "ground-light").ToScenarioUnit(AerosolType.InertGas, 1, 0, 0, 50), }); s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); s.UpdateStep(t.Id, 5); @@ -89,15 +89,15 @@ namespace CounterDrone.Core { var t = s.CreateScenario("[Demo] 喷气式拦截-活性材料", ""); s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveDroneProfile(t.Id, d.Drones.First(p => p.Id == "cruise-missile").ToDroneProfile()); + s.SaveScenarioDrone(t.Id, d.Drones.First(p => p.Id == "cruise-missile").ToScenarioDrone()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), new List { new() { PosX = 18000, PosY = 500, PosZ = 0, Speed = 200 }, new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, }); - s.SaveDeployment(t.Id, new List + s.SaveDeployment(t.Id, new List { - d.FireUnits.First(f => f.Id == "ground-standard").ToEquipmentDeployment(AerosolType.ActiveMaterial, 1, 0, 0, 50), + d.FireUnits.First(f => f.Id == "ground-standard").ToScenarioUnit(AerosolType.ActiveMaterial, 1, 0, 0, 50), }); s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.ActiveMaterial, DisperseHeight = 500 }); s.UpdateStep(t.Id, 5); @@ -110,15 +110,15 @@ namespace CounterDrone.Core scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.E; s.SaveScene(t.Id, scene); - s.SaveDroneProfile(t.Id, d.Drones.First(p => p.Id == "shahed").ToDroneProfile()); + s.SaveScenarioDrone(t.Id, d.Drones.First(p => p.Id == "shahed").ToScenarioDrone()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), new List { new() { PosX = 9500, PosY = 500, PosZ = 0, Speed = 200 }, new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, }); - s.SaveDeployment(t.Id, new List + s.SaveDeployment(t.Id, new List { - d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 3, 5000, 1000, 0), + d.FireUnits.First(f => f.Id == "air-standard").ToScenarioUnit(AerosolType.InertGas, 3, 5000, 1000, 0), }); s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); s.UpdateStep(t.Id, 5); @@ -131,19 +131,19 @@ namespace CounterDrone.Core scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.E; s.SaveScene(t.Id, scene); - var target = d.Drones.First(p => p.Id == "shahed").ToDroneProfile(); + var target = d.Drones.First(p => p.Id == "shahed").ToScenarioDrone(); target.Quantity = 3; - s.SaveDroneProfile(t.Id, target); + s.SaveScenarioDrone(t.Id, target); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "line-3").ToRoutePlan(), new List { new() { PosX = 10000, PosY = 500, PosZ = 0, Speed = 200 }, new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, }); - s.SaveDeployment(t.Id, new List + s.SaveDeployment(t.Id, new List { - d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 5000, 1000, 0), - d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 5300, 1000, 0), - d.FireUnits.First(f => f.Id == "air-standard").ToEquipmentDeployment(AerosolType.InertGas, 1, 5600, 1000, 0), + d.FireUnits.First(f => f.Id == "air-standard").ToScenarioUnit(AerosolType.InertGas, 1, 5000, 1000, 0), + d.FireUnits.First(f => f.Id == "air-standard").ToScenarioUnit(AerosolType.InertGas, 1, 5300, 1000, 0), + d.FireUnits.First(f => f.Id == "air-standard").ToScenarioUnit(AerosolType.InertGas, 1, 5600, 1000, 0), }); s.SaveCloudDispersal(t.Id, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); s.UpdateStep(t.Id, 5); diff --git a/src/CounterDrone.Core/Models/ScenarioConfig.cs b/src/CounterDrone.Core/Models/ScenarioConfig.cs index a7e9200..5d408bb 100644 --- a/src/CounterDrone.Core/Models/ScenarioConfig.cs +++ b/src/CounterDrone.Core/Models/ScenarioConfig.cs @@ -8,8 +8,8 @@ namespace CounterDrone.Core.Models public Scenario Info { get; set; } = new(); public CombatScene Scene { get; set; } = new(); public List ControlZones { get; set; } = new(); - public List Drones { get; set; } = new(); - public List Equipment { get; set; } = new(); + public List Drones { get; set; } = new(); + public List Units { get; set; } = new(); public CloudDispersal Cloud { get; set; } = new(); /// 多批次航路(多批次支持) public List Routes { get; set; } = new(); diff --git a/src/CounterDrone.Core/Models/DroneProfile.cs b/src/CounterDrone.Core/Models/ScenarioDrone.cs similarity index 94% rename from src/CounterDrone.Core/Models/DroneProfile.cs rename to src/CounterDrone.Core/Models/ScenarioDrone.cs index 8e4fdfc..ff82294 100644 --- a/src/CounterDrone.Core/Models/DroneProfile.cs +++ b/src/CounterDrone.Core/Models/ScenarioDrone.cs @@ -4,8 +4,8 @@ using SQLite; namespace CounterDrone.Core.Models { /// 步骤2:目标配置 - [Table("DroneProfile")] - public class DroneProfile + [Table("ScenarioDrone")] + public class ScenarioDrone { [PrimaryKey] public string Id { get; set; } = Guid.NewGuid().ToString(); diff --git a/src/CounterDrone.Core/Models/EquipmentDeployment.cs b/src/CounterDrone.Core/Models/ScenarioUnit.cs similarity index 97% rename from src/CounterDrone.Core/Models/EquipmentDeployment.cs rename to src/CounterDrone.Core/Models/ScenarioUnit.cs index 50a1ec6..50230b2 100644 --- a/src/CounterDrone.Core/Models/EquipmentDeployment.cs +++ b/src/CounterDrone.Core/Models/ScenarioUnit.cs @@ -4,8 +4,8 @@ using SQLite; namespace CounterDrone.Core.Models { /// 步骤3:装备部署 - [Table("EquipmentDeployment")] - public class EquipmentDeployment + [Table("ScenarioUnit")] + public class ScenarioUnit { [PrimaryKey] public string Id { get; set; } = Guid.NewGuid().ToString(); diff --git a/src/CounterDrone.Core/Repository/DroneProfileRepository.cs b/src/CounterDrone.Core/Repository/ScenarioDroneRepository.cs similarity index 54% rename from src/CounterDrone.Core/Repository/DroneProfileRepository.cs rename to src/CounterDrone.Core/Repository/ScenarioDroneRepository.cs index 5ba539b..eb80f09 100644 --- a/src/CounterDrone.Core/Repository/DroneProfileRepository.cs +++ b/src/CounterDrone.Core/Repository/ScenarioDroneRepository.cs @@ -5,13 +5,13 @@ using SQLite; namespace CounterDrone.Core.Repository { - public class DroneProfileRepository : BaseRepository + public class ScenarioDroneRepository : BaseRepository { - public DroneProfileRepository(SQLiteConnection db) : base(db) { } + public ScenarioDroneRepository(SQLiteConnection db) : base(db) { } - public List GetByScenarioId(string scenarioId) + public List GetByScenarioId(string scenarioId) { - return Db.Table().Where(t => t.ScenarioId == scenarioId).ToList(); + return Db.Table().Where(t => t.ScenarioId == scenarioId).ToList(); } public void DeleteByScenarioId(string scenarioId) diff --git a/src/CounterDrone.Core/Repository/EquipmentDeploymentRepository.cs b/src/CounterDrone.Core/Repository/ScenarioUnitRepository.cs similarity index 55% rename from src/CounterDrone.Core/Repository/EquipmentDeploymentRepository.cs rename to src/CounterDrone.Core/Repository/ScenarioUnitRepository.cs index 4500f27..08a4556 100644 --- a/src/CounterDrone.Core/Repository/EquipmentDeploymentRepository.cs +++ b/src/CounterDrone.Core/Repository/ScenarioUnitRepository.cs @@ -5,19 +5,19 @@ using SQLite; namespace CounterDrone.Core.Repository { - public class EquipmentDeploymentRepository : BaseRepository + public class ScenarioUnitRepository : BaseRepository { - public EquipmentDeploymentRepository(SQLiteConnection db) : base(db) { } + public ScenarioUnitRepository(SQLiteConnection db) : base(db) { } - public List GetByScenarioId(string scenarioId) + public List GetByScenarioId(string scenarioId) { - return Db.Table().Where(e => e.ScenarioId == scenarioId).ToList(); + return Db.Table().Where(e => e.ScenarioId == scenarioId).ToList(); } /// 按任务和角色查询装备(EquipmentRole: 0=Detection, 1=LaunchPlatform) - public List GetByScenarioIdAndRole(string scenarioId, int equipmentRole) + public List GetByScenarioIdAndRole(string scenarioId, int equipmentRole) { - return Db.Table() + return Db.Table() .Where(e => e.ScenarioId == scenarioId && e.EquipmentRole == equipmentRole) .ToList(); } diff --git a/src/CounterDrone.Core/Services/IScenarioService.cs b/src/CounterDrone.Core/Services/IScenarioService.cs index 743f871..de605cd 100644 --- a/src/CounterDrone.Core/Services/IScenarioService.cs +++ b/src/CounterDrone.Core/Services/IScenarioService.cs @@ -13,15 +13,15 @@ namespace CounterDrone.Core.Services void SaveScene(string scenarioId, CombatScene scene); void SaveControlZones(string scenarioId, List zones); - void SaveDroneProfile(string scenarioId, DroneProfile target); - void SaveDeployment(string scenarioId, List equips); + void SaveScenarioDrone(string scenarioId, ScenarioDrone target); + void SaveDeployment(string scenarioId, List equips); /// 添加单个探测设备(独立于 SaveDeployment,不覆盖火力单元) - void AddDetection(string scenarioId, EquipmentDeployment detection); + void AddDetection(string scenarioId, ScenarioUnit detection); /// 删除探测设备 void DeleteDetection(string detectionId); /// 获取任务的所有探测设备(EquipmentRole.Detection) - List GetDetections(string scenarioId); + List GetDetections(string scenarioId); void SaveCloudDispersal(string scenarioId, CloudDispersal cloud); void SaveRoute(string scenarioId, string waveId, RoutePlan route, List waypoints); diff --git a/src/CounterDrone.Core/Services/ReportGenerator.cs b/src/CounterDrone.Core/Services/ReportGenerator.cs index 926d227..096c5a9 100644 --- a/src/CounterDrone.Core/Services/ReportGenerator.cs +++ b/src/CounterDrone.Core/Services/ReportGenerator.cs @@ -13,7 +13,7 @@ namespace CounterDrone.Core.Services Simulation.DroneStatus finalDroneStatus, float simulationDuration) { var sb = new StringBuilder(); - var platforms = config.Equipment.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform).ToList(); + var platforms = config.Units.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform).ToList(); var target = config.Drones.FirstOrDefault(); var scene = config.Scene; var destroyed = events.Count(e => e.Type == SimEventType.DroneDestroyed); @@ -106,9 +106,9 @@ namespace CounterDrone.Core.Services var platToEquip = new Dictionary(); var unitLaunchCounts = new Dictionary(); int platIdx = 1, unitNum = 0; - for (int ei = 0; ei < config.Equipment.Count; ei++) + for (int ei = 0; ei < config.Units.Count; ei++) { - var eq = config.Equipment[ei]; + var eq = config.Units[ei]; if (eq.EquipmentRole != (int)EquipmentRole.LaunchPlatform) continue; int channels = (eq.GunCount ?? 1) * (eq.ChannelsPerGun ?? 1); for (int j = 0; j < eq.Quantity; j++) @@ -132,7 +132,7 @@ namespace CounterDrone.Core.Services sb.AppendLine($"| # | 平台类型 | 位置 (X,Y,Z) | 弹药类型 | 挂载量 | 发射数 |"); sb.AppendLine($"|---|---------|-------------|---------|:------:|:------:|"); int unitNum2 = 0; - foreach (var eq in config.Equipment.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform)) + foreach (var eq in config.Units.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform)) { int channels = (eq.GunCount ?? 1) * (eq.ChannelsPerGun ?? 1); for (int j = 0; j < eq.Quantity; j++) @@ -146,7 +146,7 @@ namespace CounterDrone.Core.Services } sb.AppendLine(); // 探测设备 - var detections = config.Equipment.Where(e => e.EquipmentRole == (int)EquipmentRole.Detection).ToList(); + var detections = config.Units.Where(e => e.EquipmentRole == (int)EquipmentRole.Detection).ToList(); if (detections.Count > 0) { sb.AppendLine($"### 探测设备"); diff --git a/src/CounterDrone.Core/Services/ReportService.cs b/src/CounterDrone.Core/Services/ReportService.cs index d45f199..3b161c5 100644 --- a/src/CounterDrone.Core/Services/ReportService.cs +++ b/src/CounterDrone.Core/Services/ReportService.cs @@ -36,7 +36,7 @@ namespace CounterDrone.Core.Services ScenarioNumber = config.Info.ScenarioNumber, CompletedAt = DateTime.UtcNow.ToString("o"), TargetCount = total, - EquipmentCount = config.Equipment.Count, + EquipmentCount = config.Units.Count, InterceptResult = (int)result, Content = _generator.Generate(config, events, Simulation.DroneStatus.Flying, duration), }; diff --git a/src/CounterDrone.Core/Services/ScenarioService.cs b/src/CounterDrone.Core/Services/ScenarioService.cs index 7a759ab..d04dea2 100644 --- a/src/CounterDrone.Core/Services/ScenarioService.cs +++ b/src/CounterDrone.Core/Services/ScenarioService.cs @@ -12,8 +12,8 @@ namespace CounterDrone.Core.Services private readonly ScenarioRepository _scenarioRepo; private readonly CombatSceneRepository _sceneRepo; private readonly ControlZoneRepository _zoneRepo; - private readonly DroneProfileRepository _targetRepo; - private readonly EquipmentDeploymentRepository _equipRepo; + private readonly ScenarioDroneRepository _droneRepo; + private readonly ScenarioUnitRepository _unitRepo; private readonly CloudDispersalRepository _cloudRepo; private readonly RoutePlanRepository _routeRepo; private readonly WaypointRepository _waypointRepo; @@ -22,8 +22,8 @@ namespace CounterDrone.Core.Services ScenarioRepository scenarioRepo, CombatSceneRepository sceneRepo, ControlZoneRepository zoneRepo, - DroneProfileRepository targetRepo, - EquipmentDeploymentRepository equipRepo, + ScenarioDroneRepository targetRepo, + ScenarioUnitRepository equipRepo, CloudDispersalRepository cloudRepo, RoutePlanRepository routeRepo, WaypointRepository waypointRepo) @@ -31,8 +31,8 @@ namespace CounterDrone.Core.Services _scenarioRepo = scenarioRepo; _sceneRepo = sceneRepo; _zoneRepo = zoneRepo; - _targetRepo = targetRepo; - _equipRepo = equipRepo; + _droneRepo = targetRepo; + _unitRepo = equipRepo; _cloudRepo = cloudRepo; _routeRepo = routeRepo; _waypointRepo = waypointRepo; @@ -96,8 +96,8 @@ namespace CounterDrone.Core.Services Info = scenario, Scene = _sceneRepo.GetById(id) ?? new CombatScene { ScenarioId = id }, ControlZones = _zoneRepo.GetByScenarioId(id), - Drones = _targetRepo.GetByScenarioId(id), - Equipment = _equipRepo.GetByScenarioId(id), + Drones = _droneRepo.GetByScenarioId(id), + Units = _unitRepo.GetByScenarioId(id), Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { ScenarioId = id }, Routes = _routeRepo.GetByScenarioId(id), WaypointGroups = _waypointRepo.GetByScenarioId(id) @@ -131,51 +131,51 @@ namespace CounterDrone.Core.Services TouchScenario(scenarioId); } - public void SaveDroneProfile(string scenarioId, DroneProfile target) + public void SaveScenarioDrone(string scenarioId, ScenarioDrone target) { target.ScenarioId = scenarioId; if (string.IsNullOrEmpty(target.Id)) target.Id = Guid.NewGuid().ToString(); - var existing = _targetRepo.GetById(target.Id); + var existing = _droneRepo.GetById(target.Id); if (existing != null) - _targetRepo.Update(target); + _droneRepo.Update(target); else - _targetRepo.Insert(target); + _droneRepo.Insert(target); TouchScenario(scenarioId); } - public void SaveDeployment(string scenarioId, List equips) + public void SaveDeployment(string scenarioId, List equips) { - _equipRepo.DeleteByScenarioId(scenarioId); + _unitRepo.DeleteByScenarioId(scenarioId); foreach (var e in equips) { e.ScenarioId = scenarioId; if (string.IsNullOrEmpty(e.Id)) e.Id = Guid.NewGuid().ToString(); } - _equipRepo.InsertAll(equips); + _unitRepo.InsertAll(equips); TouchScenario(scenarioId); } - public void AddDetection(string scenarioId, EquipmentDeployment detection) + public void AddDetection(string scenarioId, ScenarioUnit detection) { detection.ScenarioId = scenarioId; detection.EquipmentRole = (int)EquipmentRole.Detection; if (string.IsNullOrEmpty(detection.Id)) detection.Id = Guid.NewGuid().ToString(); - _equipRepo.Insert(detection); + _unitRepo.Insert(detection); TouchScenario(scenarioId); } public void DeleteDetection(string detectionId) { - _equipRepo.Delete(detectionId); + _unitRepo.Delete(detectionId); } - public List GetDetections(string scenarioId) + public List GetDetections(string scenarioId) { - return _equipRepo.GetByScenarioIdAndRole(scenarioId, (int)EquipmentRole.Detection); + return _unitRepo.GetByScenarioIdAndRole(scenarioId, (int)EquipmentRole.Detection); } public void SaveCloudDispersal(string scenarioId, CloudDispersal cloud) diff --git a/src/CounterDrone.Core/Simulation/DroneEntity.cs b/src/CounterDrone.Core/Simulation/DroneEntity.cs index cfee381..d386430 100644 --- a/src/CounterDrone.Core/Simulation/DroneEntity.cs +++ b/src/CounterDrone.Core/Simulation/DroneEntity.cs @@ -38,7 +38,7 @@ namespace CounterDrone.Core.Simulation /// 查找弧长 s 所在段:最大的 i 使 _cumArc[i] >= s。 private readonly float[] _cumArc; - public DroneEntity(string id, string waveId, DroneProfile config, List route, + public DroneEntity(string id, string waveId, ScenarioDrone config, List route, int formationIndex, float lateralSpacing, int longitudinalIndex, float longitudinalSpacing, FormationMode mode, int lateralAxis = 2, int longitudinalAxis = 0) { diff --git a/src/CounterDrone.Core/Simulation/PlatformEntity.cs b/src/CounterDrone.Core/Simulation/PlatformEntity.cs index f9e1b2c..85d3c04 100644 --- a/src/CounterDrone.Core/Simulation/PlatformEntity.cs +++ b/src/CounterDrone.Core/Simulation/PlatformEntity.cs @@ -46,7 +46,7 @@ namespace CounterDrone.Core.Simulation /// Ready 包含:冷却就绪 + 有弹药 + 非飞行中 public bool Ready => CooldownRemaining <= 0 && MunitionCount > 0 && State != PlatformState.FlyingToTarget; - public PlatformEntity(string id, EquipmentDeployment config) + public PlatformEntity(string id, ScenarioUnit config) { Id = id; Role = (EquipmentRole)config.EquipmentRole; diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index 4e539be..b1af7b0 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -127,7 +127,7 @@ namespace CounterDrone.Core.Simulation _entityCounter = 0; _platforms.Clear(); - foreach (var equip in config.Equipment) + foreach (var equip in config.Units) if (equip.EquipmentRole != (int)EquipmentRole.Detection) for (int i = 0; i < equip.Quantity; i++) { @@ -430,7 +430,7 @@ namespace CounterDrone.Core.Simulation private static List BuildFireUnits(ScenarioConfig config) { var units = new List(); - foreach (var eq in config.Equipment.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform)) + foreach (var eq in config.Units.Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform)) { int qty = Math.Max(1, eq.Quantity); int gunCount = eq.GunCount ?? 1; @@ -467,7 +467,7 @@ namespace CounterDrone.Core.Simulation private static List BuildDetectionSources(ScenarioConfig config) { var sources = new List(); - foreach (var eq in config.Equipment) + foreach (var eq in config.Units) { // 只要有任何探测字段非 null 且 > 0,就是有效探测源(无论是否独立探测设备) bool hasDetection = (eq.RadarRange ?? 0) > 0 || (eq.EORange ?? 0) > 0 || (eq.IRRange ?? 0) > 0; diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll index f469d2a975418b10a2da81ba6efe5444be5e37ae..b84f69556f9e70380e798c8b270c98dcd0835212 100644 GIT binary patch delta 51794 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 T3qNVOd%QoQMV*rCldb;+zlIE& delta 51958 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 + var equips = new List(); foreach (var u in fireUnits) - equips.Add(new EquipmentDeployment + equips.Add(new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)u.Type, Quantity = 1, diff --git a/src/Unity/Assets/Scripts/Managers/ScenarioManager.cs b/src/Unity/Assets/Scripts/Managers/ScenarioManager.cs index 4835d16..c19eeb4 100644 --- a/src/Unity/Assets/Scripts/Managers/ScenarioManager.cs +++ b/src/Unity/Assets/Scripts/Managers/ScenarioManager.cs @@ -27,8 +27,8 @@ namespace CounterDrone.Unity SqliteConnectionTracker.Track(_db); _service = new ScenarioService( new ScenarioRepository(_db), new CombatSceneRepository(_db), - new ControlZoneRepository(_db), new DroneProfileRepository(_db), - new EquipmentDeploymentRepository(_db), new CloudDispersalRepository(_db), + new ControlZoneRepository(_db), new ScenarioDroneRepository(_db), + new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db), new RoutePlanRepository(_db), new WaypointRepository(_db)); _dataService = new DataService( new AmmunitionSpecRepository(_db), new FireUnitSpecRepository(_db), @@ -52,8 +52,8 @@ namespace CounterDrone.Unity => _service.SearchScenarios(kw, from, to, page, size); public void SaveScene(string scenarioId, CombatScene s) => _service.SaveScene(scenarioId, s); - public void SaveDroneProfile(string scenarioId, DroneProfile t) => _service.SaveDroneProfile(scenarioId, t); - public void SaveDeployment(string scenarioId, List e) => _service.SaveDeployment(scenarioId, e); + public void SaveScenarioDrone(string scenarioId, ScenarioDrone t) => _service.SaveScenarioDrone(scenarioId, t); + public void SaveDeployment(string scenarioId, List e) => _service.SaveDeployment(scenarioId, e); public void SaveCloud(string scenarioId, CloudDispersal c) => _service.SaveCloudDispersal(scenarioId, c); public void SaveRoute(string scenarioId, string waveId, RoutePlan r, List w) => _service.SaveRoute(scenarioId, waveId, r, w); public void UpdateStep(string scenarioId, int step) => _service.UpdateStep(scenarioId, step); diff --git a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs index e57fe95..a8ee2e2 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs @@ -43,8 +43,8 @@ namespace CounterDrone.Unity var scenario = new ScenarioService( new ScenarioRepository(db), new CombatSceneRepository(db), - new ControlZoneRepository(db), new DroneProfileRepository(db), - new EquipmentDeploymentRepository(db), new CloudDispersalRepository(db), + new ControlZoneRepository(db), new ScenarioDroneRepository(db), + new ScenarioUnitRepository(db), new CloudDispersalRepository(db), new RoutePlanRepository(db), new WaypointRepository(db)); string scenarioId; @@ -70,7 +70,7 @@ namespace CounterDrone.Unity scenarioId = sc.Id; scenario.SaveScene(scenarioId, defaults.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); - scenario.SaveDroneProfile(scenarioId, defaults.Drones.First(p => p.Id == "shahed").ToDroneProfile()); + scenario.SaveScenarioDrone(scenarioId, defaults.Drones.First(p => p.Id == "shahed").ToScenarioDrone()); scenario.SaveRoute(scenarioId, "default", defaults.Formations.First(f => f.Id == "single").ToRoutePlan(), defaults.Routes.First(r => _routeLength > 6000 ? r.Id == "10km-h500" : r.Id == "5km-h500") @@ -102,10 +102,10 @@ namespace CounterDrone.Unity DisperseHeight = detail.Drones[0].TypicalAltitude, Source = "Algorithm", }); - scenario.SaveDeployment(scenarioId, new List + scenario.SaveDeployment(scenarioId, new List { defaults.FireUnits.First(f => f.Id == "ground-light") - .ToEquipmentDeployment(AerosolType.InertGas, 1, _routeLength / 2f, 0, 50), + .ToScenarioUnit(AerosolType.InertGas, 1, _routeLength / 2f, 0, 50), }); scenario.UpdateStep(scenarioId, 5); diff --git a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs index 0991bc1..2422b81 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationRunner.cs @@ -44,8 +44,8 @@ namespace CounterDrone.Unity _scenario = new ScenarioService( new ScenarioRepository(_db), new CombatSceneRepository(_db), - new ControlZoneRepository(_db), new DroneProfileRepository(_db), - new EquipmentDeploymentRepository(_db), new CloudDispersalRepository(_db), + new ControlZoneRepository(_db), new ScenarioDroneRepository(_db), + new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db), new RoutePlanRepository(_db), new WaypointRepository(_db)); _frameStore = new FrameDataStore(_paths); diff --git a/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs b/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs index 98f0210..2e06e47 100644 --- a/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs @@ -35,7 +35,7 @@ namespace CounterDrone.Core.Tests var tables = new[] { "ModelInfo", "AmmunitionSpec", "Scenario", "CombatScene", - "ControlZone", "DroneProfile", "EquipmentDeployment", + "ControlZone", "ScenarioDrone", "ScenarioUnit", "CloudDispersal", "RoutePlan", "Waypoint", "SimulationReport" }; diff --git a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs index 0be1670..f9e91b4 100644 --- a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs @@ -43,7 +43,7 @@ namespace CounterDrone.Core.Tests var t = new DroneWave { WaveId = "default", - Profile = new DroneProfile + Profile = new ScenarioDrone { DroneType = (int)DroneType.FixedWing, PowerType = (int)power, Quantity = 1, TypicalSpeed = speed, TypicalAltitude = alt, @@ -378,7 +378,7 @@ namespace CounterDrone.Core.Tests var threat = new DroneWave { WaveId = "default", - Profile = new DroneProfile + Profile = new ScenarioDrone { DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, diff --git a/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs b/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs index 2329c13..c7748f4 100644 --- a/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DroneEntityTests.cs @@ -8,9 +8,9 @@ namespace CounterDrone.Core.Tests { public class DroneEntityTests { - private DroneProfile CreateConfig(float speed = 120, float altitude = 300) + private ScenarioDrone CreateConfig(float speed = 120, float altitude = 300) { - return new DroneProfile + return new ScenarioDrone { DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, diff --git a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs index f20202f..c68f686 100644 --- a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs +++ b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs @@ -28,8 +28,8 @@ namespace CounterDrone.Core.Tests _scenario = new ScenarioService( new ScenarioRepository(_db), new CombatSceneRepository(_db), - new ControlZoneRepository(_db), new DroneProfileRepository(_db), - new EquipmentDeploymentRepository(_db), new CloudDispersalRepository(_db), + new ControlZoneRepository(_db), new ScenarioDroneRepository(_db), + new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db), new RoutePlanRepository(_db), new WaypointRepository(_db)); } @@ -46,7 +46,7 @@ namespace CounterDrone.Core.Tests _scenarioId = scenario.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenario.SaveDroneProfile(_scenarioId, new DroneProfile + _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, @@ -54,7 +54,7 @@ namespace CounterDrone.Core.Tests TypicalSpeed = 100, TypicalAltitude = 300, }); - _scenario.SaveDeployment(_scenarioId, new List()); + _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, new List @@ -89,14 +89,14 @@ namespace CounterDrone.Core.Tests WindSpeed = 30, // 最大风速 WindDirection = (int)WindDirection.E, }); - _scenario.SaveDroneProfile(_scenarioId, new DroneProfile + _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, Quantity = 1, TypicalSpeed = 600, // 高速对抗强风 }); - _scenario.SaveDeployment(_scenarioId, new List()); + _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); _scenario.SaveRoute(_scenarioId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, new List @@ -128,14 +128,14 @@ namespace CounterDrone.Core.Tests _scenarioId = scenario.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenario.SaveDroneProfile(_scenarioId, new DroneProfile + _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.HighSpeed, Quantity = 1, TypicalSpeed = 500, }); - _scenario.SaveDeployment(_scenarioId, new List()); + _scenario.SaveDeployment(_scenarioId, new List()); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal()); // 500 km/h, 100km 航程 diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index fe72add..17f80b0 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -33,8 +33,8 @@ namespace CounterDrone.Core.Tests _scenario = new ScenarioService( new ScenarioRepository(_mainDb), new CombatSceneRepository(_mainDb), - new ControlZoneRepository(_mainDb), new DroneProfileRepository(_mainDb), - new EquipmentDeploymentRepository(_mainDb), new CloudDispersalRepository(_mainDb), + new ControlZoneRepository(_mainDb), new ScenarioDroneRepository(_mainDb), + new ScenarioUnitRepository(_mainDb), new CloudDispersalRepository(_mainDb), new RoutePlanRepository(_mainDb), new WaypointRepository(_mainDb)); _frameStore = new FrameDataStore(_paths); @@ -185,7 +185,7 @@ namespace CounterDrone.Core.Tests var scenarioA = _scenario.CreateScenario("无探测远航路", ""); _scenarioId = scenarioA.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenario.SaveDroneProfile(_scenarioId, new DroneProfile + _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, @@ -196,9 +196,9 @@ namespace CounterDrone.Core.Tests new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, new Waypoint { PosX = 20000, PosY = 500, PosZ = 0, Speed = 200 }, }); - _scenario.SaveDeployment(_scenarioId, new List + _scenario.SaveDeployment(_scenarioId, new List { - TestData.All.FireUnits.First(f => f.Id == "ground-light").ToEquipmentDeployment(AerosolType.InertGas, 1, 8000, 0, 50), + TestData.All.FireUnits.First(f => f.Id == "ground-light").ToScenarioUnit(AerosolType.InertGas, 1, 8000, 0, 50), }); _scenario.SaveCloudDispersal(_scenarioId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 }); var engA = RunSimulation(8000); @@ -210,7 +210,7 @@ namespace CounterDrone.Core.Tests var scenarioB = _scenario.CreateScenario("有探测远航路", ""); _scenarioId = scenarioB.Id; _scenario.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenario.SaveDroneProfile(_scenarioId, new DroneProfile + _scenario.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, @@ -221,11 +221,11 @@ namespace CounterDrone.Core.Tests new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, new Waypoint { PosX = 20000, PosY = 500, PosZ = 0, Speed = 200 }, }); - _scenario.SaveDeployment(_scenarioId, new List + _scenario.SaveDeployment(_scenarioId, new List { - TestData.All.FireUnits.First(f => f.Id == "ground-light").ToEquipmentDeployment(AerosolType.InertGas, 1, 8000, 0, 50), + TestData.All.FireUnits.First(f => f.Id == "ground-light").ToScenarioUnit(AerosolType.InertGas, 1, 8000, 0, 50), // 独立探测设备 - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, diff --git a/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs b/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs index b227b6b..0bbaa45 100644 --- a/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ReportGeneratorTests.cs @@ -36,11 +36,11 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "测试任务", ScenarioNumber = "SIM-001" }, - Drones = new List { new DroneProfile { Quantity = 1 } }, - Equipment = new List + Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Units = new List { - new EquipmentDeployment { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 3 }, - new EquipmentDeployment { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1 }, + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 3 }, + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1 }, }, Cloud = new CloudDispersal { AerosolType = (int)AerosolType.InertGas }, }; diff --git a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs index bc48058..9287be3 100644 --- a/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ReportServiceTests.cs @@ -37,8 +37,8 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "Test", ScenarioNumber = "SIM-001" }, - Drones = new List { new DroneProfile { Quantity = 1 } }, - Equipment = new List(), + Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Units = new List(), Cloud = new CloudDispersal(), }; @@ -59,8 +59,8 @@ namespace CounterDrone.Core.Tests var config = new ScenarioConfig { Info = new Scenario { Name = "Del", ScenarioNumber = "SIM-DEL" }, - Drones = new List { new DroneProfile { Quantity = 1 } }, - Equipment = new List(), + Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Units = new List(), Cloud = new CloudDispersal(), }; @@ -75,8 +75,8 @@ namespace CounterDrone.Core.Tests var cfg = new ScenarioConfig { Info = new Scenario { Name = "城市防御", ScenarioNumber = "SIM-A" }, - Drones = new List { new DroneProfile { Quantity = 1 } }, - Equipment = new List(), + Drones = new List { new ScenarioDrone { Quantity = 1 } }, + Units = new List(), Cloud = new CloudDispersal(), }; diff --git a/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs b/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs index b5f01a9..7cc6e15 100644 --- a/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs +++ b/test/unit/CounterDrone.Core.Tests/RepositoryEdgeTests.cs @@ -22,12 +22,12 @@ namespace CounterDrone.Core.Tests public void Dispose() { _db?.Close(); if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true); } [Fact] - public void DroneProfile_DeleteByScenarioId_RemovesAll() + public void ScenarioDrone_DeleteByScenarioId_RemovesAll() { - var repo = new DroneProfileRepository(_db); - repo.Insert(new DroneProfile { Id = "t1", ScenarioId = "scenarioA", Quantity = 1 }); - repo.Insert(new DroneProfile { Id = "t2", ScenarioId = "scenarioA", Quantity = 2 }); - repo.Insert(new DroneProfile { Id = "t3", ScenarioId = "scenarioB", Quantity = 1 }); + var repo = new ScenarioDroneRepository(_db); + repo.Insert(new ScenarioDrone { Id = "t1", ScenarioId = "scenarioA", Quantity = 1 }); + repo.Insert(new ScenarioDrone { Id = "t2", ScenarioId = "scenarioA", Quantity = 2 }); + repo.Insert(new ScenarioDrone { Id = "t3", ScenarioId = "scenarioB", Quantity = 1 }); repo.DeleteByScenarioId("scenarioA"); @@ -50,11 +50,11 @@ namespace CounterDrone.Core.Tests } [Fact] - public void EquipmentDeployment_DeleteByScenarioId_Works() + public void ScenarioUnit_DeleteByScenarioId_Works() { - var repo = new EquipmentDeploymentRepository(_db); - repo.Insert(new EquipmentDeployment { Id = "e1", ScenarioId = "eqA" }); - repo.Insert(new EquipmentDeployment { Id = "e2", ScenarioId = "eqA" }); + var repo = new ScenarioUnitRepository(_db); + repo.Insert(new ScenarioUnit { Id = "e1", ScenarioId = "eqA" }); + repo.Insert(new ScenarioUnit { Id = "e2", ScenarioId = "eqA" }); repo.DeleteByScenarioId("eqA"); Assert.Empty(repo.GetByScenarioId("eqA")); diff --git a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs index 71faada..731ca3a 100644 --- a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs @@ -27,8 +27,8 @@ namespace CounterDrone.Core.Tests new ScenarioRepository(_db), new CombatSceneRepository(_db), new ControlZoneRepository(_db), - new DroneProfileRepository(_db), - new EquipmentDeploymentRepository(_db), + new ScenarioDroneRepository(_db), + new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db), new RoutePlanRepository(_db), new WaypointRepository(_db)); @@ -101,7 +101,7 @@ namespace CounterDrone.Core.Tests Temperature = 25.0, }); - _service.SaveDroneProfile(scenario.Id, new DroneProfile + _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, @@ -196,10 +196,10 @@ namespace CounterDrone.Core.Tests } [Fact] - public void SaveDroneProfile_SavesCorrectly() + public void SaveScenarioDrone_SavesCorrectly() { var scenario = _service.CreateScenario("Drone Test", ""); - _service.SaveDroneProfile(scenario.Id, new DroneProfile + _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, @@ -221,9 +221,9 @@ namespace CounterDrone.Core.Tests public void SaveDeployment_SavesMultiple() { var scenario = _service.CreateScenario("Equip Test", ""); - var equips = new List + var equips = new List { - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, @@ -232,7 +232,7 @@ namespace CounterDrone.Core.Tests MuzzleVelocity = 800.0, MunitionCount = 3, }, - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, @@ -243,9 +243,9 @@ namespace CounterDrone.Core.Tests _service.SaveDeployment(scenario.Id, equips); var detail = _service.GetScenarioDetail(scenario.Id); - Assert.Equal(2, detail.Equipment.Count); + Assert.Equal(2, detail.Units.Count); - var platform = detail.Equipment.Find(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform); + var platform = detail.Units.Find(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform); Assert.NotNull(platform); Assert.Equal(2, platform.Quantity); Assert.Equal(800.0, platform.MuzzleVelocity); @@ -392,7 +392,7 @@ namespace CounterDrone.Core.Tests }); // 4. 步骤2:目标配置 - _service.SaveDroneProfile(scenario.Id, new DroneProfile + _service.SaveScenarioDrone(scenario.Id, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.HighSpeed, @@ -403,15 +403,15 @@ namespace CounterDrone.Core.Tests _service.UpdateStep(scenario.Id, 2); // 5. 步骤3:装备部署 - _service.SaveDeployment(scenario.Id, new List + _service.SaveDeployment(scenario.Id, new List { - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, RadarRange = 6000.0, }, - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, @@ -469,7 +469,7 @@ namespace CounterDrone.Core.Tests Assert.Equal(300.0, detail.Drones[0].TypicalSpeed); // 装备 - Assert.Equal(2, detail.Equipment.Count); + Assert.Equal(2, detail.Units.Count); // 云团 Assert.Equal((int)AerosolType.ActiveMaterial, detail.Cloud.AerosolType); @@ -493,7 +493,7 @@ namespace CounterDrone.Core.Tests public void AddDetection_PersistsAndQueryable() { var scenario = _service.CreateScenario("探测设备测试", ""); - _service.AddDetection(scenario.Id, new EquipmentDeployment + _service.AddDetection(scenario.Id, new ScenarioUnit { Quantity = 1, PositionX = 5000, PositionY = 0, PositionZ = 0, @@ -513,17 +513,17 @@ namespace CounterDrone.Core.Tests { var scenario = _service.CreateScenario("探测过滤测试", ""); // 一个火力单元 - _service.SaveDeployment(scenario.Id, new List + _service.SaveDeployment(scenario.Id, new List { - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 1, PositionX = 1000, }, }); // 两个探测设备(独立添加) - _service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 5000 }); - _service.AddDetection(scenario.Id, new EquipmentDeployment { EORange = 3000 }); + _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 5000 }); + _service.AddDetection(scenario.Id, new ScenarioUnit { EORange = 3000 }); var detections = _service.GetDetections(scenario.Id); Assert.Equal(2, detections.Count); // 只有探测设备,不含火力单元 @@ -533,8 +533,8 @@ namespace CounterDrone.Core.Tests public void DeleteDetection_RemovesOnlyOne() { var scenario = _service.CreateScenario("探测删除测试", ""); - _service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 5000 }); - var det2 = new EquipmentDeployment { EORange = 3000 }; + _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 5000 }); + var det2 = new ScenarioUnit { EORange = 3000 }; _service.AddDetection(scenario.Id, det2); _service.DeleteDetection(det2.Id); @@ -548,17 +548,17 @@ namespace CounterDrone.Core.Tests public void GetScenarioDetail_IncludesDetections() { var scenario = _service.CreateScenario("想定含探测", ""); - _service.SaveDeployment(scenario.Id, new List + _service.SaveDeployment(scenario.Id, new List { - new EquipmentDeployment { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 1 }, + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, Quantity = 1 }, }); - _service.AddDetection(scenario.Id, new EquipmentDeployment { RadarRange = 10000 }); + _service.AddDetection(scenario.Id, new ScenarioUnit { RadarRange = 10000 }); var detail = _service.GetScenarioDetail(scenario.Id); // Equipment 包含火力单元 + 探测设备 - Assert.Equal(2, detail.Equipment.Count); - Assert.Contains(detail.Equipment, e => e.EquipmentRole == (int)EquipmentRole.Detection); - Assert.Contains(detail.Equipment, e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform); + Assert.Equal(2, detail.Units.Count); + Assert.Contains(detail.Units, e => e.EquipmentRole == (int)EquipmentRole.Detection); + Assert.Contains(detail.Units, e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform); } } } diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs index 871aa3a..6d4d543 100644 --- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs @@ -30,8 +30,8 @@ namespace CounterDrone.Core.Tests _scenarioService = new ScenarioService( new ScenarioRepository(_mainDb), new CombatSceneRepository(_mainDb), - new ControlZoneRepository(_mainDb), new DroneProfileRepository(_mainDb), - new EquipmentDeploymentRepository(_mainDb), new CloudDispersalRepository(_mainDb), + new ControlZoneRepository(_mainDb), new ScenarioDroneRepository(_mainDb), + new ScenarioUnitRepository(_mainDb), new CloudDispersalRepository(_mainDb), new RoutePlanRepository(_mainDb), new WaypointRepository(_mainDb)); _engine = new SimulationEngine(_scenarioService, new FrameDataStore(paths), @@ -50,7 +50,7 @@ namespace CounterDrone.Core.Tests var scenario = _scenarioService.CreateScenario("Test", ""); _scenarioId = scenario.Id; _scenarioService.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0 }); - _scenarioService.SaveDroneProfile(_scenarioId, new DroneProfile + _scenarioService.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "default", DroneType = (int)DroneType.FixedWing, @@ -58,9 +58,9 @@ namespace CounterDrone.Core.Tests PowerType = (int)PowerType.Piston, TypicalSpeed = 60, }); - _scenarioService.SaveDeployment(_scenarioId, new List + _scenarioService.SaveDeployment(_scenarioId, new List { - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, @@ -151,7 +151,7 @@ namespace CounterDrone.Core.Tests var scenario = _scenarioService.CreateScenario("Detect", ""); _scenarioId = scenario.Id; _scenarioService.SaveScene(_scenarioId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveDroneProfile(_scenarioId, new DroneProfile + _scenarioService.SaveScenarioDrone(_scenarioId, new ScenarioDrone { WaveId = "w1", DroneType = (int)DroneType.FixedWing, @@ -161,15 +161,15 @@ namespace CounterDrone.Core.Tests TypicalAltitude = 300, }); // 探测设备在 (5000, 0, 0),雷达 3000m,无角度/高度限制 - _scenarioService.SaveDeployment(_scenarioId, new List + _scenarioService.SaveDeployment(_scenarioId, new List { - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 5000, PositionY = 0, PositionZ = 0, RadarRange = 3000, }, - new EquipmentDeployment + new ScenarioUnit { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, @@ -224,11 +224,11 @@ namespace CounterDrone.Core.Tests // 探测设备在 X=5000 半径 1000 → 入口 X=4000,出口 X=6000 var scenario = _scenarioService.CreateScenario("Reenter", ""); _scenarioService.SaveScene(scenario.Id, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveDroneProfile(scenario.Id, new DroneProfile + _scenarioService.SaveScenarioDrone(scenario.Id, new ScenarioDrone { WaveId = "w1", Quantity = 1, TypicalSpeed = 120, PowerType = (int)PowerType.Piston, TypicalAltitude = 300, }); - _scenarioService.SaveDeployment(scenario.Id, new List + _scenarioService.SaveDeployment(scenario.Id, new List { new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 5000, PositionY = 0, PositionZ = 0, RadarRange = 1000 }, new() { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.GroundBased, @@ -275,11 +275,11 @@ namespace CounterDrone.Core.Tests // D1 边界 X=1000,D2 边界 X=3000 → D1 更早发现 var scenario = _scenarioService.CreateScenario("Fuse", ""); _scenarioService.SaveScene(scenario.Id, new CombatScene { WindSpeed = 0, Visibility = 10000 }); - _scenarioService.SaveDroneProfile(scenario.Id, new DroneProfile + _scenarioService.SaveScenarioDrone(scenario.Id, new ScenarioDrone { WaveId = "w1", Quantity = 1, TypicalSpeed = 60, PowerType = (int)PowerType.Piston, TypicalAltitude = 300, }); - _scenarioService.SaveDeployment(scenario.Id, new List + _scenarioService.SaveDeployment(scenario.Id, new List { new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 3000, PositionY = 0, PositionZ = 0, RadarRange = 2000, DetectionAccuracy = 50 }, new() { EquipmentRole = (int)EquipmentRole.Detection, Quantity = 1, PositionX = 7000, PositionY = 0, PositionZ = 0, RadarRange = 4000, DetectionAccuracy = 30 }, diff --git a/unity_plugins/CounterDrone.Core.dll b/unity_plugins/CounterDrone.Core.dll index f469d2a975418b10a2da81ba6efe5444be5e37ae..b84f69556f9e70380e798c8b270c98dcd0835212 100644 GIT binary patch delta 51794 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 T3qNVOd%QoQMV*rCldb;+zlIE& delta 51958 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