From 56d345189eb4f87d689127bf1dc4f773a934e2a5 Mon Sep 17 00:00:00 2001 From: tian <11429339@qq.com> Date: Mon, 15 Jun 2026 15:13:48 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=BC=96=E7=BB=84=E6=A6=82?= =?UTF-8?q?=E5=BF=B5=E6=8B=86=E5=88=86=E4=B8=BA=E6=89=B9=E6=AC=A1(WaveId)+?= =?UTF-8?q?=E7=81=AB=E5=8A=9B=E5=8D=95=E5=85=83(FireUnit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Breaking: Group表/枚举/Service/Repository移除; GroupId->WaveId; DroneGroup->DroneWave; DroneGroupId->DroneWaveId; GroupManager删除 Docs: CHANGELOG 0.7.0; 总体架构 V10; DefensePlanner V4; 实施计划 V1.4; 对接文档 V1.2 Tests: 204/204 pass --- CHANGELOG.md | 41 +++++ docs/design/architecture/总体架构设计.md | 158 +++++++++--------- .../technical/DefensePlanner-技术方案.md | 34 ++-- docs/design/technical/仿真器实体与事件映射.md | 2 +- .../tasks/实施计划与任务跟踪.md | 8 +- docs/对接文档_Unity前端.md | 28 ++-- .../Algorithms/AlgorithmTypes.cs | 8 +- .../Algorithms/DefensePlanner.cs | 24 +-- .../Algorithms/IDefensePlanner.cs | 4 +- src/CounterDrone.Core/DatabaseManager.cs | 3 +- src/CounterDrone.Core/Models/Enums.cs | 8 +- .../Models/EquipmentDeployment.cs | 2 +- src/CounterDrone.Core/Models/Group.cs | 22 --- src/CounterDrone.Core/Models/RoutePlan.cs | 4 +- src/CounterDrone.Core/Models/TargetConfig.cs | 2 +- .../Models/TaskFullConfig.cs | 4 +- src/CounterDrone.Core/Models/Waypoint.cs | 4 +- .../Repository/GroupRepository.cs | 19 --- .../Repository/RoutePlanRepository.cs | 4 +- .../Repository/WaypointRepository.cs | 4 +- .../Services/GroupService.cs | 50 ------ .../Services/IGroupService.cs | 14 -- .../Services/IScenarioService.cs | 2 +- .../Services/ScenarioService.cs | 14 +- .../Simulation/DroneEntity.cs | 6 +- .../Simulation/SimulationEngine.cs | 20 +-- .../CounterDrone.Core/CounterDrone.Core.dll | Bin 134144 -> 133120 bytes .../Assets/Scripts/Managers/GroupManager.cs | 45 ----- .../Scripts/Managers/GroupManager.cs.meta | 11 -- .../Scripts/Managers/ManagerVerification.cs | 8 +- .../Scripts/Managers/SimulationBootstrap.cs | 8 +- .../DatabaseManagerTests.cs | 4 +- .../DefensePlannerTests.cs | 16 +- .../CounterDrone.Core.Tests/EdgeCaseTests.cs | 6 +- .../FullPipelineTests.cs | 22 +-- .../GroupRepositoryTests.cs | 75 --------- .../GroupServiceTests.cs | 83 --------- .../ScenarioServiceTests.cs | 6 +- .../SimulationEngineTests.cs | 4 +- 39 files changed, 252 insertions(+), 525 deletions(-) delete mode 100644 src/CounterDrone.Core/Models/Group.cs delete mode 100644 src/CounterDrone.Core/Repository/GroupRepository.cs delete mode 100644 src/CounterDrone.Core/Services/GroupService.cs delete mode 100644 src/CounterDrone.Core/Services/IGroupService.cs delete mode 100644 src/Unity/Assets/Scripts/Managers/GroupManager.cs delete mode 100644 src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta delete mode 100644 test/unit/CounterDrone.Core.Tests/GroupRepositoryTests.cs delete mode 100644 test/unit/CounterDrone.Core.Tests/GroupServiceTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index cde938a..6ebefcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,47 @@ --- +## [0.7.0] - 2026-06-15 + +### Breaking — 概念升级:编组拆分 + Group 表移除 + +- **Group 表移除**:原 `Group` 表(DroneFleet / EquipmentGroup)不再创建。向后兼容保留旧库中的 Group 表,但不再主动读写 +- **GroupType 枚举删除**:不再区分 DroneFleet / EquipmentGroup +- **GroupService / IGroupService / GroupRepository 删除**:不再需要编组管理服务 +- **Unity GroupManager 删除**:不再需要编组管理桥接 + +### Changed — 数据模型重命名 + +- **`GroupId` → `WaveId`**:TargetConfig、EquipmentDeployment、RoutePlan、Waypoint 四表的编组外键重命名为批次外键 +- **RoutePlan 索引重命名**:`(TaskId, GroupId)` → `(TaskId, WaveId)` +- **RoutePlanRepository / WaypointRepository**:`GetByTaskAndGroup` → `GetByTaskAndWave` +- **IScenarioService.SaveRoute**:`groupId` 参数 → `waveId` + +### Changed — 算法类型重命名 + +- **`DroneGroup` → `DroneWave`**:AlgorithmTypes、IDefensePlanner、DefensePlanner、SimulationEngine 全部使用新类型名 +- **`DroneGroupId` → `DroneWaveId`**:UnitAssignment 中的编队引用 → 批次引用 +- **`BuildDroneGroups()` → `BuildDroneWaves()`**:SimulationEngine 构建方法重命名 + +### Changed — 文档同步 + +- 总体架构设计:编组概念升级说明 +- DefensePlanner 技术方案:DroneGroup → DroneWave +- 实施计划:GroupService 移除、多编队 → 多批次 +- Unity 前端对接文档:DefaultDefenseAdvisor → DefensePlanner,SaveRoute 签名更新 +- 仿真器实体事件映射:BuildDroneGroups → BuildDroneWaves + +### Removed + +- 7 个文件:Group.cs / GroupRepository.cs / IGroupService.cs / GroupService.cs / GroupManager.cs / GroupServiceTests.cs / GroupRepositoryTests.cs + +### Metrics + +- 测试 208 → **204**(−4,删除 Group 相关测试),全量通过 63s +- 0 编译错误,0 编译警告 + +--- + ## [0.6.0] - 2026-06-15 ### Added — 探测驱动的规划(8.1.1) diff --git a/docs/design/architecture/总体架构设计.md b/docs/design/architecture/总体架构设计.md index 6876635..f41a4da 100644 --- a/docs/design/architecture/总体架构设计.md +++ b/docs/design/architecture/总体架构设计.md @@ -1,11 +1,11 @@ # 反无人机仿真系统 — 总体架构设计 -> **版本**:V9 -> **日期**:2026-06-13 +> **版本**:V10 +> **日期**:2026-06-15 > **状态**:已实现 > **Unity 版本**:22.3.62 > **.NET 版本**:.NET Standard 2.1 -> **变更**:DefensePlanner 五步规划引擎;FireUnit 通道模型;空基平台飞控;SQLite domain reload 修复;删除所有 fallback 默认值 +> **变更**:Group 表移除;GroupId → WaveId;DroneGroup → DroneWave;编组概念升级为批次+火力单元 --- @@ -17,7 +17,7 @@ | 模块 | 核心功能 | |------|----------| -| **模型管理** | 3D 模型导入/预览/删除,编组管理 | +| **模型管理** | 3D 模型导入/预览/删除 | | **想定管理** | 仿真任务 CRUD,步骤化配置向导,管控区域设置,搜索分页 | | **仿真报告** | 报告列表,含时序图的完整报告预览,导出 PDF/Word,删除 | @@ -41,12 +41,12 @@ ├──────────────────────────────────────────────────────────┤ │ Unity Application Layer │ │ ModelPanel | ScenarioWizard | SimulationRunner | │ -│ ReplayController | ReportPanel | GroupManager │ +│ ReplayController | ReportPanel │ ├──────────────────────────────────────────────────────────┤ │ Service Layer (Pure C#) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ ModelService │ │ScenarioService│ │ SimulationEngine │ │ -│ │ GroupService │ │ DefensePlanner │ │ ├ DroneEntity │ │ +│ │ │ │ DefensePlanner │ │ ├ DroneEntity │ │ │ └──────────────┘ └──────────────┘ │ ├ PlatformEntity │ │ │ ┌──────────────┐ ┌──────────────┐ │ ├ MunitionEntity │ │ │ │FrameDataStore│ │ReportService │ │ ├ CloudEntity │ │ @@ -199,9 +199,19 @@ enum TriggerMode { Time = 0, Area = 1, Manual = 2 } // === 毁伤状态阶段(输出到 StateData 供 Unity 渲染)=== enum DamageStage { Normal = 0, EngineAnomaly = 1, AttitudeLoss = 2, Destroyed = 3 } -// === 编队 & 编组 === +// === 编队 === enum FormationMode { Single = 0, Formation = 1, Swarm = 2 } -enum GroupType { DroneFleet = 0, EquipmentGroup = 1 } + +// === 核心作战概念 === +// 火力单元(FireUnit):能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。 +// 包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环系统。 +// 配置层 = EquipmentDeployment(LaunchPlatform),含探测字段 + 打击字段。 +// 独立探测节点(DetectionNode):只探测不打击的侦查节点(如前沿警戒雷达)。 +// 配置层 = EquipmentDeployment(Detection),仅含探测字段。 +// 两者都往统一信息网络送探测信息,planner 基于融合后的探测边界规划。 +// 无人机批次(DroneWave):有共同航路的一组无人机(攻击方)。 +// 批次关联航路但不独占(多个批次可共享同一条航路)。 +// 配置层 = TargetConfig(含 WaveId) + RoutePlan(TaskId, WaveId) + Waypoints。 // === 运行时 === enum EntityType { Drone = 0, Platform = 1, DetectionEquip = 2, Cloud = 3, Munition = 4 } @@ -254,12 +264,11 @@ CounterDroneBackend_Data/ SimTask — 仿真任务主表 CombatScene — 步骤1:作战场景(含24h时间、天气) ControlZone — 步骤1+:管控区域/电子围栏(NEW) - TargetConfig — 步骤2:目标配置 - EquipmentDeployment — 步骤3:装备部署(搭载平台+探测设备) + TargetConfig — 步骤2:目标配置(含 WaveId 关联批次) + EquipmentDeployment — 步骤3:装备部署(火力单元含探测+打击,或独立探测节点) CloudDispersal — 步骤4:云团抛撒配置 - RoutePlan — 步骤5:航路规划(1:N SimTask,多编队) + RoutePlan — 步骤5:航路规划(1:N SimTask,多批次;批次关联但不独占航路,FK 关联 TargetConfig.WaveId) Waypoint — 航路点 - Group — 编组(独立管理) SimulationReport — 仿真报告 SimEvent — 仿真事件(时序图数据源) @@ -267,6 +276,12 @@ CounterDroneBackend_Data/ SimFrameRecord — 逐帧位置记录(回放用) ``` +> **核心作战概念**: +> - **火力单元(FireUnit)**:能独立完成搜索、跟踪、瞄准并实施打击的最小作战实体。包含探测(雷达/光电/红外)+ 打击(发射架/火炮)的完整闭环。配置层 = `EquipmentDeployment(LaunchPlatform)`。 +> - **独立探测节点(DetectionNode)**:只探测不打击的侦查节点(如前沿警戒雷达)。配置层 = `EquipmentDeployment(Detection)`。 +> - **无人机批次(DroneWave)**:有共同航路的一组无人机(攻击方)。批次关联航路但不独占(多个批次可共享同一条航路)。配置层 = `TargetConfig(WaveId)` + `RoutePlan(TaskId, WaveId)` + `Waypoints`。 +> - 原来的 `Group`(编组)表已移除:无人机编队概念升级为"批次",装备编组概念不再需要(火力单元是独立作战单位)。 + ### 4.3 业务表详细设计 #### ModelInfo — 模型元数据 @@ -337,7 +352,7 @@ CounterDroneBackend_Data/ |------|------|------|------| | Id | TEXT | PK | GUID | | TaskId | TEXT | FK | | -| GroupId | TEXT | FK | 关联无人机编队 Group | +| WaveId | TEXT | FK | 关联无人机批次(同一 WaveId 的无人机共享航路) | | TargetType | INTEGER | | 0旋翼 1固定翼 2电推 3活塞 4高速 | | Quantity | INTEGER | DEFAULT 1 | 目标数量 | | PowerType | INTEGER | | 0电推 1活塞 2喷吸气 | @@ -355,28 +370,31 @@ CounterDroneBackend_Data/ | 活塞 | 2.5 | 120 | 800 | | 高速目标 | 1.5 | 300 | 2000 | -#### EquipmentDeployment — 步骤3:装备部署(重构) +#### EquipmentDeployment — 步骤3:装备部署(火力单元 + 独立探测节点) | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | Id | TEXT | PK | GUID | | TaskId | TEXT | FK | | -| EquipmentRole | INTEGER | | 0=探测设备 1=发射平台 | +| EquipmentRole | INTEGER | | 0=独立探测节点 1=火力单元(发射平台) | | Quantity | INTEGER | DEFAULT 1 | 部署数量 | -| GroupId | TEXT | FK | 关联装备编组 Group(火力单元,多批次时用于分配) | -| // 以下为发射平台专用字段 | -| PlatformType | INTEGER | NULLABLE | 0=空基(大型无人机) 1=地基(炮弹),探测设备为 NULL | +| WaveId | TEXT | NULLABLE | 可选关联批次(用于 UI 分组,火力单元是独立作战单位,不强依赖批次) | +| // 以下为火力单元(发射平台)专用字段 | +| PlatformType | INTEGER | NULLABLE | 0=空基(大型无人机) 1=地基(炮弹),独立探测节点为 NULL | | PositionX | REAL | | 部署位置 | | PositionY | REAL | | | | PositionZ | REAL | | | -| AerosolType | INTEGER | NULLABLE | 挂载气溶胶类型,探测设备为 NULL | +| AerosolType | INTEGER | NULLABLE | 挂载气溶胶类型,独立探测节点为 NULL | | MunitionCount | INTEGER | NULLABLE | 挂载弹药数量 | | MuzzleVelocity | REAL | NULLABLE | **NEW** 弹药初速 m/s(地基炮弹用) | | ReleaseAltitude | REAL | NULLABLE | **NEW** 弹药释放高度 m(空基平台用) | | Cooldown | REAL | DEFAULT 5 | 发射冷却时间 s(同弹种连发间隔) | | AmmoChangeTime | REAL | DEFAULT 300 | **NEW** 更换弹种所需时间 s(默认 5 分钟) | -| // 以下为探测设备专用字段 | -| DetectionRadius | REAL | NULLABLE | 探测半径 m | +| // 探测能力(火力单元自带 + 独立探测节点均有) | +| RadarRange | REAL | NULLABLE | 雷达探测距离 m(雨雾不衰减) | +| EORange | REAL | NULLABLE | 光电探测距离 m(受 Visibility 衰减) | +| IRRange | REAL | NULLABLE | 红外探测距离 m(不受 Visibility 影响) | +| DetectionAccuracy | REAL | NULLABLE | 探测精度 m(位置误差,影响抛撒散布范围) | > **装备体系说明**: > - **搭载平台**(2 种): @@ -415,12 +433,12 @@ CounterDroneBackend_Data/ #### RoutePlan — 步骤5:航路规划 -> **支持多批次**:通过 `TaskId + GroupId` 唯一标识一条航路,一个任务可有多个编队各自独立航路。 +> **支持多批次**:通过 `TaskId + WaveId` 唯一标识一条航路,一个任务可有多个批次各自独立航路。 | 字段 | 类型 | 约束 | 说明 | |------|------|------|------| | TaskId | TEXT | PK(复合) | | -| GroupId | TEXT | PK(复合), FK → Group | 关联编队:同一任务不同编队可有独立航路 | +| WaveId | TEXT | PK(复合) | 关联批次:同一任务不同批次可有独立航路(多个批次可共享同一条航路) | | FormationMode | INTEGER | | 0单机 1编队 2蜂群 | | FormationSpacing | REAL | DEFAULT 50 | m | | ETA | TEXT | | 预计到达时间 | @@ -438,15 +456,13 @@ CounterDroneBackend_Data/ | Altitude | REAL | | 该航点飞行高度 m | | Speed | REAL | | 该段速度 km/h | -#### Group — 编组 +#### ~~Group~~ — 编组(已移除) -| 字段 | 类型 | 约束 | 说明 | -|------|------|------|------| -| Id | TEXT | PK | GUID | -| Name | TEXT | NOT NULL | 编组名称 | -| GroupType | INTEGER | | 0=无人机编队 1=装备编组 | -| Description | TEXT | | 备注 | -| CreatedAt | TEXT | | | +> **概念升级**:原 Group 表的两种类型已分别升级: +> - `DroneFleet`(无人机编队)→ **无人机批次(DroneWave)**:由 `TargetConfig(WaveId)` + `RoutePlan(TaskId, WaveId)` + `Waypoints` 隐式表达。批次关联航路但不独占(多个批次可共享同一条航路)。 +> - `EquipmentGroup`(装备编组)→ **不再需要**。火力单元是独立作战的最小单位,不需要上层编组。 +> +> 数据层:`Group` 表保留但不再主动使用(向后兼容)。`TargetConfig.WaveId` 语义为"批次 ID"。 ### 4.4 运行时表 @@ -507,7 +523,7 @@ SimulationEngine ├── SimulationState ← 状态(运行/暂停/结束) ├── SceneConfig ← 配置快照(含场景边界、管控区域) ├── List ← 无人机(目标方) -│ └── 按编组组织,共享航路 +│ └── 按批次组织,共享航路 ├── List ← 搭载平台(发射方)★重构 │ ├── AirBasedPlatform ← 空基(大型无人机) │ └── GroundBasedPlatform ← 地基(火炮) @@ -613,7 +629,7 @@ Unity 渲染毁伤效果(尾焰异常、姿态失控、坠落) ``` 属性: - - 编组 GroupId、航路点队列 Waypoint[] + - 批次 WaveId、航路点队列 Waypoint[] - 当前位置、速度、朝向 - HP(0~1 归一化) - DamageStage(Normal → EngineAnomaly → AttitudeLoss → Destroyed) @@ -1080,7 +1096,7 @@ planner 所有策略参数从 `data/planner_config.json` 读取,代码零默 // === 防御规划 === public interface IDefensePlanner { - PlannerResult Plan(List fireUnits, List threats, CombatScene environment); + PlannerResult Plan(List fireUnits, List threats, CombatScene environment); } // ThreatProfile / DefenseRecommendation / DefenseSolution 等模型见本节上文 @@ -1265,7 +1281,7 @@ public interface IFrameDataStore | 阶段 | 内容 | 数据来源 | |------|------|------| -| **仿真前** | 我方配置(装备类型/数量/编组、弹药类型/数量、部署位置、管控区域) | SimTask + 5 步配置 | +| **仿真前** | 我方配置(装备类型/数量、火力单元部署、弹药类型/数量、部署位置、管控区域) | SimTask + 5 步配置 | | **仿真中** | 关键事件时序表、各实体轨迹概要、云团演化数据 | SimEvent + SimFrameRecord | | **仿真后** | 拦截成功率、各阶段耗时、毁伤效果评估、对抗结果判定 | SimEvent 汇总统计 | @@ -1297,14 +1313,6 @@ interface IModelService ModelInfo GetModel(string id); } -// === 编组管理 === -interface IGroupService -{ - Group CreateGroup(string name, GroupType type, string description); - void DeleteGroup(string id); - List GetGroups(GroupType? type); -} - // === 想定管理 === interface IScenarioService { @@ -1366,7 +1374,7 @@ interface IReportService ### 数据模型 -一个仿真任务可包含**多种类型、多批次**的无人机,每批次(编队)有独立的航路: +一个仿真任务可包含**多种类型、多批次**的无人机,每批次有独立的航路: ``` SimTask "城市防御演习" @@ -1374,51 +1382,51 @@ SimTask "城市防御演习" ├── ControlZone[](共享) ├── CloudDispersal(共享:云团参数,具体拦截方案由算法确定) │ -├── 编队 A(活塞×3) ← Group.Id -│ ├── TargetConfig(类型=活塞,数量=3) -│ ├── RoutePlan(航路 A:北→南) +├── 批次 A(活塞×3) ← WaveId +│ ├── TargetConfig(WaveId=A,类型=活塞,数量=3) +│ ├── RoutePlan(TaskId + WaveId=A,航路 A:北→南) │ └── Waypoint[] │ -├── 编队 B(喷气×2) ← Group.Id -│ ├── TargetConfig(类型=高速,数量=2) -│ ├── RoutePlan(航路 B:西→东) +├── 批次 B(喷气×2) ← WaveId +│ ├── TargetConfig(WaveId=B,类型=高速,数量=2) +│ ├── RoutePlan(TaskId + WaveId=B,航路 B:西→东) │ └── Waypoint[] │ -└── 编队 C(旋翼×5) ← Group.Id - ├── TargetConfig(类型=旋翼,数量=5) - ├── RoutePlan(航路 C) +└── 批次 C(旋翼×5) ← WaveId + ├── TargetConfig(WaveId=C,类型=旋翼,数量=5) + ├── RoutePlan(TaskId + WaveId=C,航路 C) └── Waypoint[] ``` -**关键变更**:`RoutePlan` 主键从 `TaskId` 改为 `(TaskId, GroupId)` 复合键。 +**关键变更**:`RoutePlan` 主键为 `(TaskId, WaveId)` 复合键。`Waypoint` 通过 `(TaskId, WaveId)` 关联航路。 ### 推荐算法 -算法需要同时考虑**所有无人机编队**和**可用装备火力单元**,自动匹配分配: +算法需要同时考虑**所有无人机批次**和**可用火力单元**,自动匹配分配: ```csharp // 输入:所有无人机编队 + 已部署的火力单元 // 输出:每个编队的拦截方案 + 合并的发射计划 var allSolutions = new List(); -var usedPlatforms = new HashSet(); +var usedFireUnits = new HashSet(); -foreach (var droneGroup in threat.DroneGroups) +foreach (var droneWave in threat.DroneWaves) { - // 从可用装备中选择合适的火力单元(弹药类型匹配目标动力类型) - var availableUnits = fireUnits.Where(u => !usedPlatforms.Contains(u.Id)).ToList(); - var solution = advisor.RecommendForGroup(droneGroup, environment, availableUnits); + // 从可用火力单元中选择合适的(弹药类型匹配目标动力类型) + var availableUnits = fireUnits.Where(u => !usedFireUnits.Contains(u.Id)).ToList(); + var solution = advisor.RecommendForWave(droneWave, environment, availableUnits); - // 标记占用的平台(一组装备可同时对付一个编队) - foreach (var idx in solution.AssignedPlatformIndices) - usedPlatforms.Add(idx); + // 标记占用的火力单元 + foreach (var idx in solution.AssignedFireUnitIds) + usedFireUnits.Add(idx); allSolutions.Add(solution); } // 复用判定(不写死"一对一"或"一对多"): // 同种弹药 → 只需冷却时间(秒级),可直接复用 -// 异种弹药 → 需要 AmmoChangeTime(默认 5 分钟),检查编队间隔是否够 +// 异种弹药 → 需要 AmmoChangeTime(默认 5 分钟),检查批次间隔是否够 // 弹药耗尽 → 不可复用 var merged = MergeFireSchedules(allSolutions); @@ -1435,13 +1443,13 @@ engine.SetFireSchedule(merged); | 表 | 变更 | |------|------| -| `RoutePlan` | PK 改为 (TaskId, GroupId) 复合键 | -| `TargetConfig` | 不变(已有 GroupId FK) | -| `Waypoint` | 关联 `(TaskId, GroupId)` 定位航路 | +| `RoutePlan` | PK 为 (TaskId, WaveId) 复合键 | +| `TargetConfig` | 不变(已有 WaveId) | +| `Waypoint` | 关联 `(TaskId, WaveId)` 定位航路 | -`IScenarioService` 新增: +`IScenarioService` 的 `SaveRoute` 签名: ```csharp -void SaveRoute(string taskId, string groupId, RoutePlan route, List wps); // groupId 参数 +void SaveRoute(string taskId, string waveId, RoutePlan route, List wps); // waveId 参数 ``` ### 报告 @@ -1450,10 +1458,10 @@ void SaveRoute(string taskId, string groupId, RoutePlan route, List wp ```markdown ## 编队汇总 -| 编队 | 目标 | 数量 | 拦截方案 | 结果 | +| 批次 | 目标 | 数量 | 拦截方案 | 结果 | |------|------|------|------|------| -| 编队A | 活塞 | 3 | 惰性气体 12发 | ✅ 全毁 | -| 编队B | 喷气 | 2 | 活性材料 8发 | ⚠️ 1逃脱 | +| 批次A | 活塞 | 3 | 惰性气体 12发 | ✅ 全毁 | +| 批次B | 喷气 | 2 | 活性材料 8发 | ⚠️ 1逃脱 | ``` --- @@ -1473,7 +1481,7 @@ void SaveRoute(string taskId, string groupId, RoutePlan route, List wp │ └─ 多边形绘制 / 编辑 / 删除 │ ├────────────────────────────────────────────────────────┤ │ 步骤 2:目标配置 │ -│ ├─ 编组选择(已有编队/新建编队) │ +│ ├─ 批次选择(已有批次/新建批次) │ │ ├─ 目标类型选择(自动填充默认参数) │ │ ├─ 数量 / 动力类型 / 翼展 / 速度 / 高度 │ │ └─ 预设典型目标库(NEW) │ @@ -1481,7 +1489,7 @@ void SaveRoute(string taskId, string groupId, RoutePlan route, List wp │ 步骤 3:装备部署(重构) │ │ ├─ 装备角色(探测设备 / 发射平台) │ │ ├─ 发射平台类型(空基 / 地基 NEW) │ -│ ├─ 数量 / 编组 / 部署位置 │ +│ ├─ 数量 / 部署位置 │ │ ├─ 挂载气溶胶类型 / 弹药数量 │ │ ├─ 弹药初速 / 释放高度 / 冷却时间 │ │ └─ 探测半径(探测设备) │ @@ -1521,7 +1529,7 @@ void SaveRoute(string taskId, string groupId, RoutePlan route, List wp | 12 | 第三方数据 | **静态配置(JSON → SQLite)** | 仅提供弹药基础参数和初始扩散状态,不提供运行时算法 | | 13 | 运动学 | 简化运动学 + 风偏 | 航路点线性插值 + 风速矢量 | | 14 | 弹药弹道 | 抛物线(地基)/ 自由落体(空基) | 基于初速、角度、释放高度计算 | -| 15 | 编组 | Group 独立实体 | 编组独立管理,任务中引用 | +| 15 | 批次 / 火力单元 | WaveId + 火力单元独立作战 | 批次关联航路但不独占;火力单元是最小独立作战单位,不需要上层编组 | | 16 | 管控区域 | ControlZone 表 + Tick 判定 | 电子围栏,侵入即任务失败 | | 17 | 配置向导流程 | **威胁驱动三阶段** | Phase1 定义威胁 → Phase2 算法推荐 → Phase3 审阅确认 | | 18 | 报告导出 | 模板填充 → Markdown | PDF/Word 待调研 | diff --git a/docs/design/technical/DefensePlanner-技术方案.md b/docs/design/technical/DefensePlanner-技术方案.md index 2c8390c..a9b9b4c 100644 --- a/docs/design/technical/DefensePlanner-技术方案.md +++ b/docs/design/technical/DefensePlanner-技术方案.md @@ -1,14 +1,14 @@ # DefensePlanner 防御规划引擎 — 技术方案 -- **版本**:V3 -- **日期**:2026-06-13 +- **版本**:V4 +- **日期**:2026-06-15 - **状态**:已实现 --- ## 1. 概述 -DefensePlanner 是防御推荐模块的核心引擎。它接收**可用火力单元池**和**威胁编队列表**,综合考虑弹药匹配、空间可达性、时间约束、资源竞争,输出**最优分配方案**和**临界边际方案**。 +DefensePlanner 是防御推荐模块的核心引擎。它接收**可用火力单元池**和**威胁批次列表**,综合考虑弹药匹配、空间可达性、时间约束、资源竞争,输出**最优分配方案**和**临界边际方案**。 **当前问题**: @@ -54,11 +54,11 @@ class FireUnit { } ``` -### 2.2 DroneGroup(威胁编队) +### 2.2 DroneWave(威胁批次) ```csharp -class DroneGroup { - string GroupId; // 编队 ID +class DroneWave { + string WaveId; // 批次 ID TargetConfig Target; // 类型、数量、动力、翼展、速度、高度 List Waypoints; // 航路点 float ArrivalTime; // 预计算:到达防御区域中点的时间 s @@ -78,7 +78,7 @@ class DroneGroup { ```csharp class UnitAssignment { string FireUnitId; // 分配到的火力单元 - string DroneGroupId; // 对抗的威胁编队 + string DroneWaveId; // 对抗的威胁批次 AerosolType AmmoType; // 装填的弹药类型 int RoundsFired; // 本次发射几发 float FirstFireTime; // 首发发射时机 s @@ -113,7 +113,7 @@ class PlannerResult { ## 4. 内部流程(五步法) ``` -输入:List + List + CombatScene +输入:List + List + CombatScene │ ▼ Step 1 — 威胁排序 @@ -190,7 +190,7 @@ class InterceptCandidate { **候选生成逻辑**: -对于给定的威胁编队和火力单元: +对于给定的威胁批次和火力单元: 1. **弹药兼容性**:`Unit.AmmoTypes` 包含威胁需要的弹药类型 2. **地基可达性**:计算目标与部署点的水平距离,校验 `MuzzleVelocity` 射程 @@ -239,7 +239,7 @@ for each threat in pending: > 作用:给操作员一个置信区间——最优 vs 临界,展示"再少就不够了"的底线。 -### 5.5 多编队合并 +### 5.5 多批次合并 ``` Step 4 的贪心算法天然支持多威胁: @@ -258,7 +258,7 @@ Step 4 的贪心算法天然支持多威胁: public interface IDefensePlanner { /// 为给定火力单元池和威胁列表生成规划方案 - PlannerResult Plan(List fireUnits, List threats, CombatScene environment); + PlannerResult Plan(List fireUnits, List threats, CombatScene environment); } ``` @@ -273,16 +273,16 @@ public class DefaultDefensePlanner : IDefensePlanner public DefaultDefensePlanner(List ammoCatalog) { ... } - public PlannerResult Plan(List fireUnits, List threats, CombatScene env) + public PlannerResult Plan(List fireUnits, List threats, CombatScene env) { // Step 1-5 } // 内部方法 - private List Prioritize(List threats) { ... } + private List Prioritize(List threats) { ... } private AerosolType MatchAmmo(PowerType power) { ... } - private List GenerateCandidates(DroneGroup threat, List units, float now) { ... } - private DefensePlan Solve(List threats, List units) { ... } + private List GenerateCandidates(DroneWave threat, List units, float now) { ... } + private DefensePlan Solve(List threats, List units) { ... } private DefensePlan DeriveCritical(DefensePlan best) { ... } } ``` @@ -293,7 +293,7 @@ public class DefaultDefensePlanner : IDefensePlanner |--------|--------|------| | `IDefenseAdvisor.Recommend(ThreatProfile)` | `IDefensePlanner.Plan(fireUnits, threats, env)` | ✅ 已替换 | | `IDefenseAdvisor.GetDefenseRecommendation(taskId)` | 不再需要 | ✅ 已删除 | -| `DefaultDefenseAdvisor.RecommendMultiGroup()` | Planner 原生支持多编队 | ✅ 已删除 | +| `DefaultDefenseAdvisor.RecommendMultiGroup()` | Planner 原生支持多批次 | ✅ 已删除 | --- @@ -304,7 +304,7 @@ SimulationEngine.Initialize(taskId) │ │ // 引擎内部组装 ├── BuildFireUnits(config) → List - ├── BuildDroneGroups(config) → List + ├── BuildDroneWaves(config) → List └── _scene (CombatScene) │ ▼ diff --git a/docs/design/technical/仿真器实体与事件映射.md b/docs/design/technical/仿真器实体与事件映射.md index fc833c0..306378c 100644 --- a/docs/design/technical/仿真器实体与事件映射.md +++ b/docs/design/technical/仿真器实体与事件映射.md @@ -41,7 +41,7 @@ SimulationEngine.Initialize() │ ├─ BuildFireUnits() → List - ├─ BuildDroneGroups() → List + ├─ BuildDroneWaves() → List │ ▼ IDefensePlanner.Plan(fireUnits, threats, scene) diff --git a/docs/implementation/tasks/实施计划与任务跟踪.md b/docs/implementation/tasks/实施计划与任务跟踪.md index 061ff59..90b2ba4 100644 --- a/docs/implementation/tasks/实施计划与任务跟踪.md +++ b/docs/implementation/tasks/实施计划与任务跟踪.md @@ -1,8 +1,8 @@ # 实施计划与任务跟踪 > **项目**:反无人机仿真系统后端 -> **文档版本**:V1.3 -> **更新日期**:2026-06-14 +> **文档版本**:V1.4 +> **更新日期**:2026-06-15 --- @@ -32,7 +32,7 @@ Phase 8 🔄 待开发(天气/物理模型统一已完成) | # | 任务 | 状态 | |---|------|------| -| 1.1-1.12 | 项目骨架、枚举、数据模型、Repository、ModelService/GroupService、单元测试 | ✅ | +| 1.1-1.12 | 项目骨架、枚举、数据模型、Repository、ModelService、单元测试 | ✅ | --- @@ -105,7 +105,7 @@ Phase 8 🔄 待开发(天气/物理模型统一已完成) | 6.1 | 创建 Unity 项目,导入 Core.dll | 1h | ✅ | `src/Unity/` 项目,16 个 DLL → `Assets/Plugins/` | | 6.2 | 实现 `UnityPathProvider` | 1h | ✅ | `Application.persistentDataPath` 桥接 | | 6.3 | 实现 `ModelManager` | 3h | ✅ | 导入/删除/查询,含 Verify | -| 6.4 | 实现 `ScenarioManager`(5 步配置) | 4h | ✅ | 完整 CRUD + 搜索分页 + 多编队 Route | +| 6.4 | 实现 `ScenarioManager`(5 步配置) | 4h | ✅ | 完整 CRUD + 搜索分页 + 多批次 Route | | 6.5 | 实现 `SimulationRunner`(Update 驱动) | 3h | ✅ | Tick 驱动 + 事件订阅 + 实体位置同步 + 炮弹轨迹可视化 | | 6.6 | 实现 `ReplayController`(帧加载) | 3h | ✅ | 从分库加载帧数据,TotalFrames/GetFrame | | 6.7 | 实现 `ReportManager` | 2h | ✅ | 生成 + Markdown 导出 | diff --git a/docs/对接文档_Unity前端.md b/docs/对接文档_Unity前端.md index 6f1e992..78e3b84 100644 --- a/docs/对接文档_Unity前端.md +++ b/docs/对接文档_Unity前端.md @@ -1,7 +1,7 @@ # 后端对接文档(Unity 前端) -> **版本**:V1.1 -> **日期**:2026-06-12 +> **版本**:V1.2 +> **日期**:2026-06-15 > **Unity 版本**:2022.3.62f3c1 --- @@ -35,17 +35,21 @@ data/default_ammo.json ← 默认弹药参数(放到 persisten var scenario = GetComponent(); var task = scenario.CreateTask("拦截活塞式无人机", ""); scenario.SaveScene(task.Id, new CombatScene { WindSpeed = 3 }); -scenario.SaveTarget(task.Id, new TargetConfig { PowerType = (int)PowerType.Piston, ... }); -scenario.SaveRoute(task.Id, routePlan, waypoints); +scenario.SaveTarget(task.Id, new TargetConfig { WaveId = "default", PowerType = (int)PowerType.Piston, ... }); +scenario.SaveRoute(task.Id, "default", routePlan, waypoints); -// 推荐方案(自动从数据库读弹药规格) +// 推荐方案(DefensePlanner 五步流水线) var detail = scenario.GetDetail(task.Id); var ammoCatalog = db.Table().ToList(); // DatabaseManager 已种子 -var rec = new DefaultDefenseAdvisor(ammoCatalog).Recommend(new ThreatProfile { ... }); +var fireUnits = new List { /* 从 EquipmentDeployment 构建 */ }; +var threats = new List { new DroneWave { WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"] } }; +var plan = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths)).Plan(fireUnits, threats, detail.Scene, new List()); -// 应用到想定 -scenario.SaveCloud(task.Id, rec.Best.RecommendedCloud); -scenario.SaveDeployment(task.Id, rec.Best.Platforms.Select(p => new EquipmentDeployment { ... }).ToList()); +// 应用到想定(发射计划传给引擎,部署方案保存到配置) +var mergedSchedule = plan.Best.MergedSchedule; +// 部署火力单元到 EquipmentDeployment... +scenario.SaveCloud(task.Id, new CloudDispersal { /* 从 plan.Best.Assignments 推算云团参数 */ }); +scenario.SaveDeployment(task.Id, equips); ``` ### 3.2 启动仿真(事件驱动) @@ -54,7 +58,7 @@ scenario.SaveDeployment(task.Id, rec.Best.Platforms.Select(p => new EquipmentDep var runner = GetComponent(); // 推荐方案的 FireSchedule 直接传给引擎 -runner.Engine.SetFireSchedule(rec.Best.FireSchedule); +runner.Engine.SetFireSchedule(plan.Best.MergedSchedule); runner.LoadAndStart(task.Id); runner.Engine.TimeScale = 4f; // 加速 @@ -93,7 +97,7 @@ mgr.SaveScene(id, combatScene); mgr.SaveTarget(id, targetConfig); mgr.SaveDeployment(id, equipmentList); mgr.SaveCloud(id, cloudDispersal); -mgr.SaveRoute(id, routePlan, waypoints); +mgr.SaveRoute(id, "default", routePlan, waypoints); TaskFullConfig detail = mgr.GetDetail(id); PagedResult result = mgr.Search("关键词", from, to, page, pageSize); ``` @@ -163,4 +167,4 @@ dotnet publish -c Release -o ../../unity_plugins | 实体事件映射 | `docs/design/technical/仿真器实体与事件映射.md` | | 任务跟踪 | `docs/implementation/tasks/实施计划与任务跟踪.md` | | 测试报告 | `test/reports/`(每次集成测试自动生成) | -| 测试状态 | **129 测试,95.4% 行覆盖率,12 秒** | +| 测试状态 | **204 测试,95.4% 行覆盖率,63 秒** | diff --git a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs index 01b11c1..eb31381 100644 --- a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs +++ b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs @@ -46,10 +46,10 @@ namespace CounterDrone.Core.Algorithms public float MuzzleVelocity { get; set; } = 800f; } - /// 无人机编队(规划器输入) - public class DroneGroup + /// 无人机批次(规划器输入) + public class DroneWave { - public string GroupId { get; set; } = string.Empty; + public string WaveId { get; set; } = string.Empty; public TargetConfig Target { get; set; } = new(); public RoutePlan Route { get; set; } = new(); public List Waypoints { get; set; } = new(); @@ -120,7 +120,7 @@ namespace CounterDrone.Core.Algorithms public class UnitAssignment { public string FireUnitId { get; set; } = string.Empty; - public string DroneGroupId { get; set; } = string.Empty; + public string DroneWaveId { get; set; } = string.Empty; public AerosolType AmmoType { get; set; } public int RoundsFired { get; set; } public float FirstFireTime { get; set; } diff --git a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs index e5e2653..17307a5 100644 --- a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs @@ -26,7 +26,7 @@ namespace CounterDrone.Core.Algorithms // 五步流水线 // ═══════════════════════════════════════════════ - public PlannerResult Plan(List fireUnits, List threats, + public PlannerResult Plan(List fireUnits, List threats, CombatScene environment, List detectionSources) { var result = new PlannerResult(); @@ -108,7 +108,7 @@ namespace CounterDrone.Core.Algorithms // Step 3-4: 候选生成 → 贪心分配 // ═══════════════════════════════════════════════ - private DefensePlan Solve(List sortedThreats, + private DefensePlan Solve(List sortedThreats, List fireUnits, CombatScene env) { var plan = new DefensePlan(); @@ -215,7 +215,7 @@ namespace CounterDrone.Core.Algorithms plan.Assignments.Add(new UnitAssignment { FireUnitId = unit.Id, - DroneGroupId = threat.GroupId, + DroneWaveId = threat.WaveId, AmmoType = neededAmmo, RoundsFired = rounds, FirstFireTime = fireEvents.Count > 0 ? fireEvents[0].FireTime : 0, @@ -236,7 +236,7 @@ namespace CounterDrone.Core.Algorithms var a2 = _ammoCatalog.FirstOrDefault(s => s.AerosolType == (int)MatchAmmo(_config, (PowerType)threat.Target.PowerType)); int totalRounds = plan.Assignments - .Where(a => a.DroneGroupId == threat.GroupId) + .Where(a => a.DroneWaveId == threat.WaveId) .Sum(a => a.RoundsFired); if (totalRounds > 0) threatProbs.Add(ComputeInterceptProbability(threat, a2, totalRounds, env)); @@ -253,7 +253,7 @@ namespace CounterDrone.Core.Algorithms // 候选生成(使用真实物理) // ═══════════════════════════════════════════════ - private List GenerateCandidates(DroneGroup threat, + private List GenerateCandidates(DroneWave threat, List availableUnits, CombatScene env) { var candidates = new List(); @@ -281,7 +281,7 @@ namespace CounterDrone.Core.Algorithms return candidates; } - private InterceptCandidate? BuildGroundBasedCandidate(DroneGroup threat, + private InterceptCandidate? BuildGroundBasedCandidate(DroneWave threat, FireUnit unit, AerosolType ammoType, AmmunitionSpec ammo, float effectiveR, CombatScene env) { @@ -311,7 +311,7 @@ namespace CounterDrone.Core.Algorithms }; } - private InterceptCandidate? BuildAirBasedCandidate(DroneGroup threat, + private InterceptCandidate? BuildAirBasedCandidate(DroneWave threat, FireUnit unit, AerosolType ammoType, AmmunitionSpec ammo, float effectiveR, CombatScene env) { @@ -358,7 +358,7 @@ namespace CounterDrone.Core.Algorithms return (effectiveR, expansionTime, rPhase2); } - private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec ammo, + private int CalcRoundsNeeded(DroneWave threat, AmmunitionSpec ammo, CombatScene env, bool isAirBased, float turbulentRadius) { var cloudModel = new CloudExpansionModel(ammo, env); @@ -371,7 +371,7 @@ namespace CounterDrone.Core.Algorithms return cloudModel.RoundsNeeded(requiredCoverage, spacing); } - private float ComputeInterceptProbability(DroneGroup threat, + private float ComputeInterceptProbability(DroneWave threat, AmmunitionSpec ammo, int rounds, CombatScene env) { float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f; @@ -390,7 +390,7 @@ namespace CounterDrone.Core.Algorithms // 发射事件生成(真实物理) // ═══════════════════════════════════════════════ - private List GenerateFireEventsAt(DroneGroup threat, FireUnit unit, + private List GenerateFireEventsAt(DroneWave threat, FireUnit unit, AerosolType ammoType, AmmunitionSpec ammo, CombatScene env, float targetOffset, int yLane, int yLanes, float formationWidth) { @@ -517,7 +517,7 @@ namespace CounterDrone.Core.Algorithms var reduced = new UnitAssignment { FireUnitId = assignment.FireUnitId, - DroneGroupId = assignment.DroneGroupId, + DroneWaveId = assignment.DroneWaveId, AmmoType = assignment.AmmoType, RoundsFired = rounds, FirstFireTime = assignment.FirstFireTime, @@ -537,7 +537,7 @@ namespace CounterDrone.Core.Algorithms // 辅助 // ═══════════════════════════════════════════════ - private static Vector3 ThreatMidpoint(DroneGroup threat) + private static Vector3 ThreatMidpoint(DroneWave threat) { if (threat.Waypoints.Count < 2) return new Vector3(0, (float)threat.Target.TypicalAltitude, 0); diff --git a/src/CounterDrone.Core/Algorithms/IDefensePlanner.cs b/src/CounterDrone.Core/Algorithms/IDefensePlanner.cs index 5117e7c..7d1d13f 100644 --- a/src/CounterDrone.Core/Algorithms/IDefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/IDefensePlanner.cs @@ -8,10 +8,10 @@ namespace CounterDrone.Core.Algorithms public interface IDefensePlanner { /// 可用的火力单元(空基/地基统一表达) - /// 威胁编队列表 + /// 威胁批次列表 /// 作战环境(含天气,影响光电探测距离) /// 统一信息网络的探测源列表(独立探测设备 + 火力单元自带探测)。可为空列表。 - PlannerResult Plan(List fireUnits, List threats, + PlannerResult Plan(List fireUnits, List threats, CombatScene environment, List detectionSources); } } diff --git a/src/CounterDrone.Core/DatabaseManager.cs b/src/CounterDrone.Core/DatabaseManager.cs index 701d1e5..c24496d 100644 --- a/src/CounterDrone.Core/DatabaseManager.cs +++ b/src/CounterDrone.Core/DatabaseManager.cs @@ -68,9 +68,8 @@ namespace CounterDrone.Core db.CreateTable(); db.CreateTable(); db.CreateTable(); - db.CreateIndex("RoutePlan", new[] { "TaskId", "GroupId" }, true); + db.CreateIndex("RoutePlan", new[] { "TaskId", "WaveId" }, true); db.CreateTable(); - db.CreateTable(); db.CreateTable(); db.CreateIndex("SimTask", "TaskNumber"); diff --git a/src/CounterDrone.Core/Models/Enums.cs b/src/CounterDrone.Core/Models/Enums.cs index ba37077..93e2085 100644 --- a/src/CounterDrone.Core/Models/Enums.cs +++ b/src/CounterDrone.Core/Models/Enums.cs @@ -92,7 +92,7 @@ namespace CounterDrone.Core.Models Destroyed = 3 } - // === 编队 & 编组 === + // === 编队 & 批次 === public enum FormationMode { Single = 0, @@ -100,12 +100,6 @@ namespace CounterDrone.Core.Models Swarm = 2 } - public enum GroupType - { - DroneFleet = 0, - EquipmentGroup = 1 - } - // === 运行时 === public enum EntityType { diff --git a/src/CounterDrone.Core/Models/EquipmentDeployment.cs b/src/CounterDrone.Core/Models/EquipmentDeployment.cs index ac1913d..b4003ec 100644 --- a/src/CounterDrone.Core/Models/EquipmentDeployment.cs +++ b/src/CounterDrone.Core/Models/EquipmentDeployment.cs @@ -17,7 +17,7 @@ namespace CounterDrone.Core.Models public int Quantity { get; set; } = 1; - public string GroupId { get; set; } = string.Empty; + public string WaveId { get; set; } = string.Empty; // 发射平台专用 public int? PlatformType { get; set; } diff --git a/src/CounterDrone.Core/Models/Group.cs b/src/CounterDrone.Core/Models/Group.cs deleted file mode 100644 index 15155d7..0000000 --- a/src/CounterDrone.Core/Models/Group.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using SQLite; - -namespace CounterDrone.Core.Models -{ - /// 编组 - [Table("Group")] - public class Group - { - [PrimaryKey] - public string Id { get; set; } = Guid.NewGuid().ToString(); - - [NotNull] - public string Name { get; set; } = string.Empty; - - public int GroupType { get; set; } = (int)Models.GroupType.DroneFleet; - - public string Description { get; set; } = string.Empty; - - public string CreatedAt { get; set; } = DateTime.UtcNow.ToString("o"); - } -} diff --git a/src/CounterDrone.Core/Models/RoutePlan.cs b/src/CounterDrone.Core/Models/RoutePlan.cs index 13b6c28..e1089f2 100644 --- a/src/CounterDrone.Core/Models/RoutePlan.cs +++ b/src/CounterDrone.Core/Models/RoutePlan.cs @@ -3,7 +3,7 @@ using SQLite; namespace CounterDrone.Core.Models { - /// 步骤5:航路规划 — 多编队支持:每个(任务,编队)一条航路 + /// 步骤5:航路规划 — 多批次支持:每个(任务,批次)一条航路 [Table("RoutePlan")] public class RoutePlan { @@ -14,7 +14,7 @@ namespace CounterDrone.Core.Models public string TaskId { get; set; } = string.Empty; [Indexed] - public string GroupId { get; set; } = string.Empty; + public string WaveId { get; set; } = string.Empty; public int FormationMode { get; set; } = (int)Models.FormationMode.Single; diff --git a/src/CounterDrone.Core/Models/TargetConfig.cs b/src/CounterDrone.Core/Models/TargetConfig.cs index 603d9e4..d27cb77 100644 --- a/src/CounterDrone.Core/Models/TargetConfig.cs +++ b/src/CounterDrone.Core/Models/TargetConfig.cs @@ -13,7 +13,7 @@ namespace CounterDrone.Core.Models [Indexed] public string TaskId { get; set; } = string.Empty; - public string GroupId { get; set; } = string.Empty; + public string WaveId { get; set; } = string.Empty; public int TargetType { get; set; } = (int)Models.TargetType.Rotor; diff --git a/src/CounterDrone.Core/Models/TaskFullConfig.cs b/src/CounterDrone.Core/Models/TaskFullConfig.cs index d4ef3e8..711bf65 100644 --- a/src/CounterDrone.Core/Models/TaskFullConfig.cs +++ b/src/CounterDrone.Core/Models/TaskFullConfig.cs @@ -11,9 +11,9 @@ namespace CounterDrone.Core.Models public List Targets { get; set; } = new(); public List Equipment { get; set; } = new(); public CloudDispersal Cloud { get; set; } = new(); - /// 多编队航路(多批次支持) + /// 多批次航路(多批次支持) public List Routes { get; set; } = new(); - /// 按 GroupId 分组的航路点 + /// 按 WaveId 分组的航路点 public Dictionary> WaypointGroups { get; set; } = new(); } diff --git a/src/CounterDrone.Core/Models/Waypoint.cs b/src/CounterDrone.Core/Models/Waypoint.cs index 67ac110..845c4dd 100644 --- a/src/CounterDrone.Core/Models/Waypoint.cs +++ b/src/CounterDrone.Core/Models/Waypoint.cs @@ -13,8 +13,8 @@ namespace CounterDrone.Core.Models [Indexed] public string TaskId { get; set; } = string.Empty; - /// 关联的编队 ID(多编队支持) - public string GroupId { get; set; } = string.Empty; + /// 关联的批次 ID(多批次支持) + public string WaveId { get; set; } = string.Empty; [NotNull] public int OrderIndex { get; set; } diff --git a/src/CounterDrone.Core/Repository/GroupRepository.cs b/src/CounterDrone.Core/Repository/GroupRepository.cs deleted file mode 100644 index 0a268c9..0000000 --- a/src/CounterDrone.Core/Repository/GroupRepository.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using CounterDrone.Core.Models; -using SQLite; - -namespace CounterDrone.Core.Repository -{ - public class GroupRepository : BaseRepository - { - public GroupRepository(SQLiteConnection db) : base(db) { } - - public List GetByType(int groupType) - { - return Db.Table() - .Where(g => g.GroupType == groupType) - .ToList(); - } - } -} diff --git a/src/CounterDrone.Core/Repository/RoutePlanRepository.cs b/src/CounterDrone.Core/Repository/RoutePlanRepository.cs index 0cf5d1d..f2c2bb1 100644 --- a/src/CounterDrone.Core/Repository/RoutePlanRepository.cs +++ b/src/CounterDrone.Core/Repository/RoutePlanRepository.cs @@ -14,10 +14,10 @@ namespace CounterDrone.Core.Repository return Db.Table().Where(r => r.TaskId == taskId).ToList(); } - public RoutePlan GetByTaskAndGroup(string taskId, string groupId) + public RoutePlan GetByTaskAndWave(string taskId, string waveId) { return Db.Table() - .FirstOrDefault(r => r.TaskId == taskId && r.GroupId == groupId); + .FirstOrDefault(r => r.TaskId == taskId && r.WaveId == waveId); } } } diff --git a/src/CounterDrone.Core/Repository/WaypointRepository.cs b/src/CounterDrone.Core/Repository/WaypointRepository.cs index 81d917e..cba1bdd 100644 --- a/src/CounterDrone.Core/Repository/WaypointRepository.cs +++ b/src/CounterDrone.Core/Repository/WaypointRepository.cs @@ -17,10 +17,10 @@ namespace CounterDrone.Core.Repository .ToList(); } - public List GetByTaskAndGroup(string taskId, string groupId) + public List GetByTaskAndWave(string taskId, string waveId) { return Db.Table() - .Where(w => w.TaskId == taskId && w.GroupId == groupId) + .Where(w => w.TaskId == taskId && w.WaveId == waveId) .OrderBy(w => w.OrderIndex) .ToList(); } diff --git a/src/CounterDrone.Core/Services/GroupService.cs b/src/CounterDrone.Core/Services/GroupService.cs deleted file mode 100644 index fe94156..0000000 --- a/src/CounterDrone.Core/Services/GroupService.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using CounterDrone.Core.Models; -using CounterDrone.Core.Repository; - -namespace CounterDrone.Core.Services -{ - public class GroupService : IGroupService - { - private readonly GroupRepository _repo; - - public GroupService(GroupRepository repo) - { - _repo = repo; - } - - public Group CreateGroup(string name, GroupType type, string description) - { - if (string.IsNullOrWhiteSpace(name)) - throw new ArgumentException("编组名称不能为空"); - - var group = new Group - { - Name = name, - GroupType = (int)type, - Description = description, - }; - - _repo.Insert(group); - return group; - } - - public void DeleteGroup(string id) - { - _repo.Delete(id); - } - - public List GetGroups(GroupType? type) - { - if (type.HasValue) - return _repo.GetByType((int)type.Value); - return _repo.GetAll(); - } - - public Group GetGroup(string id) - { - return _repo.GetById(id); - } - } -} diff --git a/src/CounterDrone.Core/Services/IGroupService.cs b/src/CounterDrone.Core/Services/IGroupService.cs deleted file mode 100644 index 611c1b4..0000000 --- a/src/CounterDrone.Core/Services/IGroupService.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using CounterDrone.Core.Models; - -namespace CounterDrone.Core.Services -{ - /// 编组管理服务 - public interface IGroupService - { - Group CreateGroup(string name, GroupType type, string description); - void DeleteGroup(string id); - List GetGroups(GroupType? type); - Group GetGroup(string id); - } -} diff --git a/src/CounterDrone.Core/Services/IScenarioService.cs b/src/CounterDrone.Core/Services/IScenarioService.cs index f5c27db..325256d 100644 --- a/src/CounterDrone.Core/Services/IScenarioService.cs +++ b/src/CounterDrone.Core/Services/IScenarioService.cs @@ -24,7 +24,7 @@ namespace CounterDrone.Core.Services List GetDetections(string taskId); void SaveCloudDispersal(string taskId, CloudDispersal cloud); - void SaveRoute(string taskId, string groupId, RoutePlan route, List waypoints); + void SaveRoute(string taskId, string waveId, RoutePlan route, List waypoints); void UpdateStep(string taskId, int step); } } diff --git a/src/CounterDrone.Core/Services/ScenarioService.cs b/src/CounterDrone.Core/Services/ScenarioService.cs index d169592..d8e8fd9 100644 --- a/src/CounterDrone.Core/Services/ScenarioService.cs +++ b/src/CounterDrone.Core/Services/ScenarioService.cs @@ -101,7 +101,7 @@ namespace CounterDrone.Core.Services Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { TaskId = id }, Routes = _routeRepo.GetByTaskId(id), WaypointGroups = _waypointRepo.GetByTaskId(id) - .GroupBy(w => w.GroupId) + .GroupBy(w => w.WaveId) .ToDictionary(g => g.Key, g => g.OrderBy(w => w.OrderIndex).ToList()), }; } @@ -189,11 +189,11 @@ namespace CounterDrone.Core.Services TouchTask(taskId); } - public void SaveRoute(string taskId, string groupId, RoutePlan route, List waypoints) + public void SaveRoute(string taskId, string waveId, RoutePlan route, List waypoints) { route.TaskId = taskId; - route.GroupId = groupId; - var existing = _routeRepo.GetByTaskAndGroup(taskId, groupId); + route.WaveId = waveId; + var existing = _routeRepo.GetByTaskAndWave(taskId, waveId); if (existing != null) { route.Id = existing.Id; @@ -202,13 +202,13 @@ namespace CounterDrone.Core.Services else _routeRepo.Insert(route); - // 删除该编队的旧航路点,插入新的 - var oldWps = _waypointRepo.GetByTaskAndGroup(taskId, groupId); + // 删除该批次的旧航路点,插入新的 + var oldWps = _waypointRepo.GetByTaskAndWave(taskId, waveId); foreach (var w in oldWps) _waypointRepo.Delete(w.Id); for (int i = 0; i < waypoints.Count; i++) { waypoints[i].TaskId = taskId; - waypoints[i].GroupId = groupId; + waypoints[i].WaveId = waveId; waypoints[i].OrderIndex = i; if (string.IsNullOrEmpty(waypoints[i].Id)) waypoints[i].Id = Guid.NewGuid().ToString(); diff --git a/src/CounterDrone.Core/Simulation/DroneEntity.cs b/src/CounterDrone.Core/Simulation/DroneEntity.cs index 2240195..bf1b862 100644 --- a/src/CounterDrone.Core/Simulation/DroneEntity.cs +++ b/src/CounterDrone.Core/Simulation/DroneEntity.cs @@ -8,7 +8,7 @@ namespace CounterDrone.Core.Simulation public class DroneEntity { public string Id { get; } - public string GroupId { get; } + public string WaveId { get; } public TargetType TargetType { get; } public PowerType PowerType { get; } public float Wingspan { get; } @@ -27,11 +27,11 @@ namespace CounterDrone.Core.Simulation /// 沿航路已飞行弧长(米),由 RouteGeometry 驱动 private float _traveledArc; - public DroneEntity(string id, string groupId, TargetConfig config, List route, + public DroneEntity(string id, string waveId, TargetConfig config, List route, int formationIndex, float lateralSpacing, int longitudinalIndex, float longitudinalSpacing, FormationMode mode) { Id = id; - GroupId = groupId; + WaveId = waveId; TargetType = (TargetType)config.TargetType; PowerType = (PowerType)config.PowerType; Wingspan = (float)config.Wingspan; diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index fe069cf..a83440b 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -88,8 +88,8 @@ namespace CounterDrone.Core.Simulation _drones.Clear(); foreach (var target in config.Targets) { - var route = config.Routes.FirstOrDefault(r => r.GroupId == target.GroupId); - var wps = config.WaypointGroups.GetValueOrDefault(target.GroupId, new List()); + var route = config.Routes.FirstOrDefault(r => r.WaveId == target.WaveId); + var wps = config.WaypointGroups.GetValueOrDefault(target.WaveId, new List()); if (route == null || wps.Count == 0) continue; var mode = (FormationMode)route.FormationMode; var lateralSpacing = (float)route.LateralSpacing; @@ -100,7 +100,7 @@ namespace CounterDrone.Core.Simulation { int latIdx = i % latCount; int longIdx = i / latCount; - _drones.Add(new DroneEntity($"drone_{++_entityCounter}", target.GroupId, + _drones.Add(new DroneEntity($"drone_{++_entityCounter}", target.WaveId, target, wps, latIdx, lateralSpacing, longIdx, longSpacing, mode)); } } @@ -137,7 +137,7 @@ namespace CounterDrone.Core.Simulation if (_fireSchedule.Count == 0) { var fireUnits = BuildFireUnits(config); - var threats = BuildDroneGroups(config); + var threats = BuildDroneWaves(config); if (fireUnits.Count > 0 && threats.Count > 0) { var detectionSources = BuildDetectionSources(config); @@ -378,16 +378,16 @@ namespace CounterDrone.Core.Simulation return sources; } - private static List BuildDroneGroups(TaskFullConfig config) + private static List BuildDroneWaves(TaskFullConfig config) { - var groups = new List(); + var groups = new List(); foreach (var t in config.Targets) { - var route = config.Routes.FirstOrDefault(r => r.GroupId == t.GroupId); - var wps = config.WaypointGroups.GetValueOrDefault(t.GroupId, new List()); - groups.Add(new DroneGroup + var route = config.Routes.FirstOrDefault(r => r.WaveId == t.WaveId); + var wps = config.WaypointGroups.GetValueOrDefault(t.WaveId, new List()); + groups.Add(new DroneWave { - GroupId = t.GroupId, + WaveId = t.WaveId, Target = t, Route = route ?? new RoutePlan(), Waypoints = wps, diff --git a/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll b/src/Unity/Assets/Plugins/CounterDrone.Core/CounterDrone.Core.dll index 1ccf79dc0a2e008c857cd85dfca12ee0f9ed2a41..548e9dae82cd604ef00c898057849f7f5cdda4d5 100644 GIT binary patch literal 133120 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 literal 134144 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 diff --git a/src/Unity/Assets/Scripts/Managers/GroupManager.cs b/src/Unity/Assets/Scripts/Managers/GroupManager.cs deleted file mode 100644 index a8ce477..0000000 --- a/src/Unity/Assets/Scripts/Managers/GroupManager.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using CounterDrone.Core; -using CounterDrone.Core.Models; -using CounterDrone.Core.Repository; -using CounterDrone.Core.Services; -using UnityEngine; - -namespace CounterDrone.Unity -{ - /// 编组管理桥接 — 无人机编队和装备编组的 CRUD - public class GroupManager : MonoBehaviour - { - private IGroupService _service; - private SQLite.SQLiteConnection _db; - - public IGroupService Service => _service; - - public void Awake() - { - var paths = new UnityPathProvider(); - _db = new DatabaseManager(paths).OpenMainDb(); - SqliteConnectionTracker.Track(_db); - _service = new GroupService(new GroupRepository(_db)); - } - - void OnDisable() { SqliteConnectionTracker.Untrack(_db); _db?.Dispose(); _db = null; } - - public Group Create(string name, GroupType type, string desc = "") - => _service.CreateGroup(name, type, desc); - - public void Delete(string id) => _service.DeleteGroup(id); - - public List GetByType(GroupType? type = null) => _service.GetGroups(type); - - public Group Get(string id) => _service.GetGroup(id); - - [ContextMenu("Verify")] - void Verify() - { - Awake(); - var g = Create("测试编队", GroupType.DroneFleet, "自动验证"); - Debug.Log($"GroupManager OK. Created: {g.Name} ({g.Id})"); - } - } -} diff --git a/src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta b/src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta deleted file mode 100644 index 499ff8c..0000000 --- a/src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 24c080ea3d6cbcf45aa804fd10269e4a -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs b/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs index dc30612..7f7d93d 100644 --- a/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs +++ b/src/Unity/Assets/Scripts/Managers/ManagerVerification.cs @@ -35,7 +35,7 @@ namespace CounterDrone.Unity scenarioMgr.SaveScene(taskId, new CombatScene { WindSpeed = 0 }); scenarioMgr.SaveTarget(taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, }); @@ -54,9 +54,9 @@ namespace CounterDrone.Unity var ammoCatalog = db.Table().ToList(); var detail = scenarioMgr.GetDetail(taskId); - var droneGroup = new DroneGroup + var droneGroup = new DroneWave { - GroupId = "default", + WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"], @@ -72,7 +72,7 @@ namespace CounterDrone.Unity TotalMunitions = 3, AmmoTypes = new() { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel }, }); - var result = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths)).Plan(fireUnits, new List { droneGroup }, detail.Scene, new List()); + var result = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths)).Plan(fireUnits, new List { droneGroup }, detail.Scene, new List()); scenarioMgr.SaveCloud(taskId, new CloudDispersal { PositionX = (droneGroup.Waypoints[0].PosX + droneGroup.Waypoints[^1].PosX) / 2, diff --git a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs index a58761b..26fe2e9 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs @@ -53,7 +53,7 @@ namespace CounterDrone.Unity }); scenario.SaveTarget(taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = _droneSpeed, TypicalAltitude = 500, }); @@ -68,9 +68,9 @@ namespace CounterDrone.Unity var detail = scenario.GetTaskDetail(taskId); var ammoCatalog = db.Table().ToList(); var planner = new DefensePlanner(ammoCatalog, PlannerConfig.Load(paths)); - var droneGroup = new DroneGroup + var droneGroup = new DroneWave { - GroupId = "default", + WaveId = "default", Target = detail.Targets[0], Route = detail.Routes[0], Waypoints = detail.WaypointGroups["default"], @@ -86,7 +86,7 @@ namespace CounterDrone.Unity TotalMunitions = 3, AmmoTypes = new() { AerosolType.InertGas, AerosolType.ActiveMaterial, AerosolType.ActiveFuel }, }); - var result = planner.Plan(fireUnits, new List { droneGroup }, detail.Scene, new List()); + var result = planner.Plan(fireUnits, new List { droneGroup }, detail.Scene, new List()); scenario.SaveCloudDispersal(taskId, new CloudDispersal { PositionX = (droneGroup.Waypoints[0].PosX + droneGroup.Waypoints[^1].PosX) / 2, diff --git a/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs b/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs index 3e6867f..8180035 100644 --- a/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DatabaseManagerTests.cs @@ -31,12 +31,12 @@ namespace CounterDrone.Core.Tests { var db = _dbManager.OpenMainDb(); - // 验证所有 12 张表存在 + // 验证所有 11 张表存在 var tables = new[] { "ModelInfo", "AmmunitionSpec", "SimTask", "CombatScene", "ControlZone", "TargetConfig", "EquipmentDeployment", - "CloudDispersal", "RoutePlan", "Waypoint", "Group", + "CloudDispersal", "RoutePlan", "Waypoint", "SimulationReport" }; diff --git a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs index c08d25a..c5daec1 100644 --- a/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DefensePlannerTests.cs @@ -37,12 +37,12 @@ namespace CounterDrone.Core.Tests AmmoTypes = new() { AerosolType.InertGas }, }; - private static DroneGroup MakeThreat(PowerType power = PowerType.Piston, float speed = 120f, + private static DroneWave MakeThreat(PowerType power = PowerType.Piston, float speed = 120f, float startX = 0, float endX = 10000, float alt = 500) { - var t = new DroneGroup + var t = new DroneWave { - GroupId = "default", + WaveId = "default", Target = new TargetConfig { TargetType = (int)TargetType.Piston, PowerType = (int)power, @@ -57,7 +57,7 @@ namespace CounterDrone.Core.Tests return t; } - private PlannerResult Plan(List units, DroneGroup threat, + private PlannerResult Plan(List units, DroneWave threat, CombatScene? env = null) => new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan(units, new() { threat }, env ?? new CombatScene(), new List()); @@ -291,8 +291,8 @@ namespace CounterDrone.Core.Tests [Fact] public void MultiThreat_BothEngaged() { - var a = MakeThreat(PowerType.Piston, 120); a.GroupId = "g0"; - var b = MakeThreat(PowerType.Jet, 300); b.GroupId = "g1"; + var a = MakeThreat(PowerType.Piston, 120); a.WaveId = "g0"; + var b = MakeThreat(PowerType.Jet, 300); b.WaveId = "g1"; var result = new DefensePlanner(TestAmmo, TestPlannerConfig.Instance).Plan( new() { MakeGroundUnit("u0", 5000), MakeGroundUnit("u1", 5100) }, new() { a, b }, new CombatScene(), new List()); @@ -405,9 +405,9 @@ namespace CounterDrone.Core.Tests // Z 向航路:从 (5000,0) 飞向 (5000,10000),即沿 +Z 方向 // 之前 offset 写死 X 轴时,多发云团会沿 X 发散到 5000±N×spacing,偏离航路 // 改用 RouteGeometry 后,offset 沿航路切向(+Z),云团应集中在 X=5000 - var threat = new DroneGroup + var threat = new DroneWave { - GroupId = "default", + WaveId = "default", Target = new TargetConfig { TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, diff --git a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs index bcd447e..cd8292f 100644 --- a/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs +++ b/test/unit/CounterDrone.Core.Tests/EdgeCaseTests.cs @@ -49,7 +49,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, Quantity = 1, TypicalSpeed = 100, @@ -92,7 +92,7 @@ namespace CounterDrone.Core.Tests }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, Quantity = 1, TypicalSpeed = 600, // 高速对抗强风 @@ -131,7 +131,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.HighSpeed, Quantity = 1, TypicalSpeed = 500, diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index 5089cd9..4dc2ee0 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -114,7 +114,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.FixedWing, PowerType = (int)PowerType.Jet, Quantity = 1, @@ -147,7 +147,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Electric, PowerType = (int)PowerType.Electric, Quantity = 1, @@ -189,7 +189,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, @@ -273,7 +273,7 @@ namespace CounterDrone.Core.Tests }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, @@ -333,7 +333,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.HighSpeed, PowerType = (int)PowerType.Jet, Quantity = 1, @@ -377,7 +377,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, @@ -443,7 +443,7 @@ namespace CounterDrone.Core.Tests }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, @@ -502,7 +502,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 3, TypicalSpeed = 200, TypicalAltitude = 500, @@ -557,7 +557,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 3, TypicalSpeed = 150, TypicalAltitude = 500, @@ -619,7 +619,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, }); _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, @@ -644,7 +644,7 @@ namespace CounterDrone.Core.Tests _scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0, Visibility = 10000 }); _scenario.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, + WaveId = "default", TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston, Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500, }); _scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single }, diff --git a/test/unit/CounterDrone.Core.Tests/GroupRepositoryTests.cs b/test/unit/CounterDrone.Core.Tests/GroupRepositoryTests.cs deleted file mode 100644 index 9a90feb..0000000 --- a/test/unit/CounterDrone.Core.Tests/GroupRepositoryTests.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System; -using System.IO; -using CounterDrone.Core; -using CounterDrone.Core.Models; -using CounterDrone.Core.Repository; -using SQLite; -using Xunit; - -namespace CounterDrone.Core.Tests -{ - public class GroupRepositoryTests : IDisposable - { - private readonly string _testDir; - private readonly SQLiteConnection _db; - private readonly GroupRepository _repo; - - public GroupRepositoryTests() - { - _testDir = Path.Combine(Path.GetTempPath(), $"cd_test_{Guid.NewGuid():N}"); - var paths = new TestPathProvider(_testDir); - var dbManager = new DatabaseManager(paths); - _db = dbManager.OpenMainDb(); - _repo = new GroupRepository(_db); - } - - public void Dispose() - { - _db?.Close(); - if (Directory.Exists(_testDir)) - Directory.Delete(_testDir, true); - } - - [Fact] - public void Insert_And_GetById_ReturnsGroup() - { - var group = new Group - { - Name = "Alpha Fleet", - GroupType = (int)GroupType.DroneFleet, - Description = "Test drone formation" - }; - - _repo.Insert(group); - - var result = _repo.GetById(group.Id); - Assert.NotNull(result); - Assert.Equal("Alpha Fleet", result.Name); - Assert.Equal((int)GroupType.DroneFleet, result.GroupType); - } - - [Fact] - public void GetByType_FiltersCorrectly() - { - _repo.Insert(new Group { Name = "Fleet A", GroupType = (int)GroupType.DroneFleet }); - _repo.Insert(new Group { Name = "Fleet B", GroupType = (int)GroupType.DroneFleet }); - _repo.Insert(new Group { Name = "Equip G1", GroupType = (int)GroupType.EquipmentGroup }); - - var fleets = _repo.GetByType((int)GroupType.DroneFleet); - Assert.Equal(2, fleets.Count); - - var equipGroups = _repo.GetByType((int)GroupType.EquipmentGroup); - Assert.Single(equipGroups); - } - - [Fact] - public void Delete_RemovesGroup() - { - var group = new Group { Name = "ToDelete" }; - _repo.Insert(group); - _repo.Delete(group.Id); - - Assert.Null(_repo.GetById(group.Id)); - } - } -} diff --git a/test/unit/CounterDrone.Core.Tests/GroupServiceTests.cs b/test/unit/CounterDrone.Core.Tests/GroupServiceTests.cs deleted file mode 100644 index 03de66a..0000000 --- a/test/unit/CounterDrone.Core.Tests/GroupServiceTests.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.IO; -using CounterDrone.Core; -using CounterDrone.Core.Models; -using CounterDrone.Core.Services; -using SQLite; -using Xunit; - -namespace CounterDrone.Core.Tests -{ - public class GroupServiceTests : IDisposable - { - private readonly string _testDir; - private readonly SQLiteConnection _db; - private readonly IGroupService _service; - - public GroupServiceTests() - { - _testDir = Path.Combine(Path.GetTempPath(), $"cd_test_{Guid.NewGuid():N}"); - var paths = new TestPathProvider(_testDir); - var dbManager = new DatabaseManager(paths); - _db = dbManager.OpenMainDb(); - _service = new GroupService(new Repository.GroupRepository(_db)); - } - - public void Dispose() - { - _db?.Close(); - if (Directory.Exists(_testDir)) - Directory.Delete(_testDir, true); - } - - [Fact] - public void CreateGroup_Valid_ReturnsGroup() - { - var group = _service.CreateGroup("Alpha", GroupType.DroneFleet, "Test fleet"); - - Assert.NotNull(group); - Assert.Equal("Alpha", group.Name); - Assert.NotEmpty(group.Id); - } - - [Fact] - public void CreateGroup_EmptyName_Throws() - { - Assert.Throws(() => - _service.CreateGroup("", GroupType.DroneFleet, "")); - } - - [Fact] - public void GetGroups_WithTypeFilter() - { - _service.CreateGroup("Fleet A", GroupType.DroneFleet, ""); - _service.CreateGroup("Fleet B", GroupType.DroneFleet, ""); - _service.CreateGroup("Equip X", GroupType.EquipmentGroup, ""); - - var fleets = _service.GetGroups(GroupType.DroneFleet); - Assert.Equal(2, fleets.Count); - - var all = _service.GetGroups(null); - Assert.Equal(3, all.Count); - } - - [Fact] - public void DeleteGroup_RemovesGroup() - { - var group = _service.CreateGroup("DeleteMe", GroupType.DroneFleet, ""); - _service.DeleteGroup(group.Id); - - Assert.Null(_service.GetGroup(group.Id)); - } - - [Fact] - public void GetGroup_ById_ReturnsCorrectGroup() - { - var created = _service.CreateGroup("Target", GroupType.EquipmentGroup, "desc"); - var fetched = _service.GetGroup(created.Id); - - Assert.Equal("Target", fetched.Name); - Assert.Equal("desc", fetched.Description); - } - } -} diff --git a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs index c0c876b..59585af 100644 --- a/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs +++ b/test/unit/CounterDrone.Core.Tests/ScenarioServiceTests.cs @@ -103,7 +103,7 @@ namespace CounterDrone.Core.Tests _service.SaveTarget(task.Id, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.FixedWing, Quantity = 3, }); @@ -201,7 +201,7 @@ namespace CounterDrone.Core.Tests var task = _service.CreateTask("Target Test", ""); _service.SaveTarget(task.Id, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, Quantity = 5, PowerType = (int)PowerType.Piston, @@ -394,7 +394,7 @@ namespace CounterDrone.Core.Tests // 4. 步骤2:目标配置 _service.SaveTarget(task.Id, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.HighSpeed, Quantity = 2, PowerType = (int)PowerType.Jet, diff --git a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs index cb873ef..7a93603 100644 --- a/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/SimulationEngineTests.cs @@ -53,7 +53,7 @@ namespace CounterDrone.Core.Tests _scenarioService.SaveScene(_taskId, new CombatScene { WindSpeed = 0 }); _scenarioService.SaveTarget(_taskId, new TargetConfig { - GroupId = "default", + WaveId = "default", TargetType = (int)TargetType.Piston, Quantity = 1, PowerType = (int)PowerType.Piston, @@ -147,7 +147,7 @@ namespace CounterDrone.Core.Tests { var task = _scenarioService.CreateTask("t", ""); _scenarioService.SaveScene(task.Id, new CombatScene()); - _scenarioService.SaveTarget(task.Id, new TargetConfig { GroupId = "d", Quantity = 1, TypicalSpeed = 60, TypicalAltitude = 300 }); + _scenarioService.SaveTarget(task.Id, new TargetConfig { WaveId = "d", Quantity = 1, TypicalSpeed = 60, TypicalAltitude = 300 }); _scenarioService.SaveDeployment(task.Id, new List { new() { EquipmentRole = (int)EquipmentRole.LaunchPlatform, PlatformType = (int)PlatformType.AirBased,