diff --git a/src/CounterDrone.Core/DatabaseManager.cs b/src/CounterDrone.Core/DatabaseManager.cs index 4b7a937..e51ce64 100644 --- a/src/CounterDrone.Core/DatabaseManager.cs +++ b/src/CounterDrone.Core/DatabaseManager.cs @@ -62,6 +62,16 @@ namespace CounterDrone.Core // 插入或更新默认数据(按 Id 覆盖,不删除用户自建数据) foreach (var a in defaults.Ammunition) db.InsertOrReplace(a); + foreach (var f in defaults.FireUnits) db.InsertOrReplace(f); + foreach (var d in defaults.Drones) db.InsertOrReplace(d); + foreach (var s in defaults.Sensors) db.InsertOrReplace(s); + foreach (var e in defaults.Environments) db.InsertOrReplace(e); + foreach (var f in defaults.Formations) db.InsertOrReplace(f); + foreach (var r in defaults.Routes) + { + r.WaypointsJson = System.Text.Json.JsonSerializer.Serialize(r.Waypoints); + db.InsertOrReplace(r); + } db.InsertOrReplace(new MetaEntry { Key = "defaultsVersion", Value = defaults.Version }); } @@ -100,6 +110,12 @@ namespace CounterDrone.Core db.CreateIndex("RoutePlan", new[] { "TaskId", "WaveId" }, true); db.CreateTable(); db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); + db.CreateTable(); db.CreateIndex("SimTask", "TaskNumber"); db.CreateIndex("ControlZone", "TaskId"); diff --git a/src/CounterDrone.Core/DefaultData.cs b/src/CounterDrone.Core/DefaultData.cs index 3fb0f56..60a0585 100644 --- a/src/CounterDrone.Core/DefaultData.cs +++ b/src/CounterDrone.Core/DefaultData.cs @@ -16,11 +16,15 @@ namespace CounterDrone.Core public string Version { get; set; } = "0"; public List Ammunition { get; set; } = new(); public List Formations { get; set; } = new(); - public List Routes { get; set; } = new(); - public List FireUnits { get; set; } = new(); - public List Targets { get; set; } = new(); - public List DetectionEquipment { get; set; } = new(); - public List Weather { get; set; } = new(); + public List Routes { get; set; } = new(); + [System.Text.Json.Serialization.JsonPropertyName("fireUnits")] + public List FireUnits { get; set; } = new(); + [System.Text.Json.Serialization.JsonPropertyName("targets")] + public List Drones { get; set; } = new(); + [System.Text.Json.Serialization.JsonPropertyName("detectionEquipment")] + public List Sensors { get; set; } = new(); + [System.Text.Json.Serialization.JsonPropertyName("weather")] + public List Environments { get; set; } = new(); /// 从 data/defaults.json 加载。文件缺失或解析失败即抛异常。 public static DefaultData Load(IPathProvider paths) @@ -57,12 +61,12 @@ namespace CounterDrone.Core throw new InvalidOperationException($"默认数据缺少 routes: {source}"); if (data.FireUnits == null || data.FireUnits.Count == 0) throw new InvalidOperationException($"默认数据缺少 fireUnits: {source}"); - if (data.Targets == null || data.Targets.Count == 0) - throw new InvalidOperationException($"默认数据缺少 targets: {source}"); - if (data.Weather == null || data.Weather.Count == 0) - throw new InvalidOperationException($"默认数据缺少 weather: {source}"); - if (data.DetectionEquipment == null) - data.DetectionEquipment = new(); + if (data.Drones == null || data.Drones.Count == 0) + throw new InvalidOperationException($"默认数据缺少 drones: {source}"); + if (data.Environments == null || data.Environments.Count == 0) + throw new InvalidOperationException($"默认数据缺少 environments: {source}"); + if (data.Sensors == null) + data.Sensors = new(); } } @@ -90,9 +94,11 @@ namespace CounterDrone.Core } } - /// 火力单元模板 - public class FireUnitTemplate + /// 火力单元规格(基础数据模板) + [SQLite.Table("FireUnitSpec")] + public class FireUnitSpec { + [SQLite.PrimaryKey] public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int PlatformType { get; set; } @@ -104,6 +110,7 @@ namespace CounterDrone.Core public double MuzzleVelocity { get; set; } public double CruiseSpeed { get; set; } public double ReleaseAltitude { get; set; } + [SQLite.Ignore] public List AmmoTypes { get; set; } = new(); public double RadarRange { get; set; } public double EORange { get; set; } @@ -145,9 +152,11 @@ namespace CounterDrone.Core } } - /// 无人机/威胁目标预设 - public class TargetPreset + /// 无人机规格(基础数据模板) + [SQLite.Table("DroneSpec")] + public class DroneSpec { + [SQLite.PrimaryKey] public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int TargetType { get; set; } @@ -171,9 +180,11 @@ namespace CounterDrone.Core } } - /// 独立探测设备预设 - public class DetectionPreset + /// 传感器规格(基础数据模板) + [SQLite.Table("SensorSpec")] + public class SensorSpec { + [SQLite.PrimaryKey] public string Id { get; set; } = ""; public string Name { get; set; } = ""; public double RadarRange { get; set; } @@ -207,9 +218,11 @@ namespace CounterDrone.Core } } - /// 天气/场景预设 - public class WeatherPreset + /// 环境规格(基础数据模板) + [SQLite.Table("EnvironmentSpec")] + public class EnvironmentSpec { + [SQLite.PrimaryKey] public string Id { get; set; } = ""; public string Name { get; set; } = ""; public int WeatherType { get; set; } @@ -235,11 +248,16 @@ namespace CounterDrone.Core } } - /// 航线预设 — 航路点坐标集合,速度由目标 TypicalSpeed 提供 - public class RoutePreset + /// 航线模板(基础数据模板) + [SQLite.Table("RouteTemplate")] + public class RouteTemplate { + [SQLite.PrimaryKey] public string Id { get; set; } = ""; public string Name { get; set; } = ""; + /// 航路点 JSON(WaypointCoord 序列化),SQLite 存储用 + public string WaypointsJson { get; set; } = "[]"; + [SQLite.Ignore] public List Waypoints { get; set; } = new(); public List ToWaypoints(double speed) diff --git a/src/CounterDrone.Core/DefaultScenarios.cs b/src/CounterDrone.Core/DefaultScenarios.cs index a8e48d4..704ddcd 100644 --- a/src/CounterDrone.Core/DefaultScenarios.cs +++ b/src/CounterDrone.Core/DefaultScenarios.cs @@ -36,8 +36,8 @@ namespace CounterDrone.Core private static void SeedNoDefense(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 无防御-无人机抵达目标", ""); - s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Drones.First(p => p.Id == "shahed").ToTargetConfig()); 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()); @@ -47,8 +47,8 @@ namespace CounterDrone.Core private static void SeedZoneIntrusion(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 管控区域侵入", ""); - s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "electric-scout").ToTargetConfig()); + s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Drones.First(p => p.Id == "electric-scout").ToTargetConfig()); 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()); @@ -67,11 +67,11 @@ namespace CounterDrone.Core private static void SeedPistonWindy(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 活塞拦截-西风5ms", ""); - var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene(); + var scene = d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene(); scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.W; s.SaveScene(t.Id, scene); - s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveTarget(t.Id, d.Drones.First(p => p.Id == "shahed").ToTargetConfig()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), new List { new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, @@ -88,8 +88,8 @@ namespace CounterDrone.Core private static void SeedJetActiveMaterial(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 喷气式拦截-活性材料", ""); - s.SaveScene(t.Id, d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); - s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "cruise-missile").ToTargetConfig()); + s.SaveScene(t.Id, d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); + s.SaveTarget(t.Id, d.Drones.First(p => p.Id == "cruise-missile").ToTargetConfig()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), new List { new() { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 }, @@ -106,11 +106,11 @@ namespace CounterDrone.Core private static void SeedAirBasedWindy(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 空基拦截-东风5ms", ""); - var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene(); + var scene = d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene(); scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.E; s.SaveScene(t.Id, scene); - s.SaveTarget(t.Id, d.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + s.SaveTarget(t.Id, d.Drones.First(p => p.Id == "shahed").ToTargetConfig()); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "single").ToRoutePlan(), R("20km-h500", 200, d)); s.SaveDeployment(t.Id, new List { @@ -123,11 +123,11 @@ namespace CounterDrone.Core private static void Seed3DronesAirBased(IScenarioService s, DefaultData d) { var t = s.CreateTask("[Demo] 3架空基编队拦截", ""); - var scene = d.Weather.First(w => w.Id == "sunny-calm").ToCombatScene(); + var scene = d.Environments.First(w => w.Id == "sunny-calm").ToCombatScene(); scene.WindSpeed = 5; scene.WindDirection = (int)WindDirection.E; s.SaveScene(t.Id, scene); - var target = d.Targets.First(p => p.Id == "shahed").ToTargetConfig(); + var target = d.Drones.First(p => p.Id == "shahed").ToTargetConfig(); target.Quantity = 3; s.SaveTarget(t.Id, target); s.SaveRoute(t.Id, "default", d.Formations.First(f => f.Id == "line-3").ToRoutePlan(), R("20km-h500", 200, d)); diff --git a/src/CounterDrone.Core/Repository/SpecRepositories.cs b/src/CounterDrone.Core/Repository/SpecRepositories.cs new file mode 100644 index 0000000..f8fd1a2 --- /dev/null +++ b/src/CounterDrone.Core/Repository/SpecRepositories.cs @@ -0,0 +1,35 @@ +using CounterDrone.Core; +using SQLite; + +namespace CounterDrone.Core.Repository +{ + public class FireUnitSpecRepository : BaseRepository + { + public FireUnitSpecRepository(SQLiteConnection db) : base(db) { } + } + + public class DroneSpecRepository : BaseRepository + { + public DroneSpecRepository(SQLiteConnection db) : base(db) { } + } + + public class SensorSpecRepository : BaseRepository + { + public SensorSpecRepository(SQLiteConnection db) : base(db) { } + } + + public class EnvironmentSpecRepository : BaseRepository + { + public EnvironmentSpecRepository(SQLiteConnection db) : base(db) { } + } + + public class FormationTemplateRepository : BaseRepository + { + public FormationTemplateRepository(SQLiteConnection db) : base(db) { } + } + + public class RouteTemplateRepository : BaseRepository + { + public RouteTemplateRepository(SQLiteConnection db) : base(db) { } + } +} diff --git a/src/CounterDrone.Core/Services/DataService.cs b/src/CounterDrone.Core/Services/DataService.cs new file mode 100644 index 0000000..199b025 --- /dev/null +++ b/src/CounterDrone.Core/Services/DataService.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using CounterDrone.Core.Models; +using CounterDrone.Core.Repository; + +namespace CounterDrone.Core.Services +{ + public class DataService : IDataService + { + private readonly AmmunitionSpecRepository _ammoRepo; + private readonly FireUnitSpecRepository _fireUnitRepo; + private readonly DroneSpecRepository _droneRepo; + private readonly SensorSpecRepository _sensorRepo; + private readonly EnvironmentSpecRepository _envRepo; + private readonly FormationTemplateRepository _formationRepo; + private readonly RouteTemplateRepository _routeRepo; + + public DataService(AmmunitionSpecRepository ammoRepo, FireUnitSpecRepository fireUnitRepo, + DroneSpecRepository droneRepo, SensorSpecRepository sensorRepo, + EnvironmentSpecRepository envRepo, FormationTemplateRepository formationRepo, + RouteTemplateRepository routeRepo) + { + _ammoRepo = ammoRepo; + _fireUnitRepo = fireUnitRepo; + _droneRepo = droneRepo; + _sensorRepo = sensorRepo; + _envRepo = envRepo; + _formationRepo = formationRepo; + _routeRepo = routeRepo; + } + + // ═══ AmmunitionSpec ═══ + public List GetAllAmmo() => _ammoRepo.GetAll(); + public AmmunitionSpec GetAmmo(string id) => _ammoRepo.GetById(id); + public void SaveAmmo(AmmunitionSpec spec) { if (_ammoRepo.GetById(spec.Id) != null) _ammoRepo.Update(spec); else _ammoRepo.Insert(spec); } + public void DeleteAmmo(string id) => _ammoRepo.Delete(id); + + // ═══ FireUnitSpec ═══ + public List GetAllFireUnits() => _fireUnitRepo.GetAll(); + public FireUnitSpec GetFireUnit(string id) => _fireUnitRepo.GetById(id); + public void SaveFireUnit(FireUnitSpec spec) { if (_fireUnitRepo.GetById(spec.Id) != null) _fireUnitRepo.Update(spec); else _fireUnitRepo.Insert(spec); } + public void DeleteFireUnit(string id) => _fireUnitRepo.Delete(id); + + // ═══ DroneSpec ═══ + public List GetAllDrones() => _droneRepo.GetAll(); + public DroneSpec GetDrone(string id) => _droneRepo.GetById(id); + public void SaveDrone(DroneSpec spec) { if (_droneRepo.GetById(spec.Id) != null) _droneRepo.Update(spec); else _droneRepo.Insert(spec); } + public void DeleteDrone(string id) => _droneRepo.Delete(id); + + // ═══ SensorSpec ═══ + public List GetAllSensors() => _sensorRepo.GetAll(); + public SensorSpec GetSensor(string id) => _sensorRepo.GetById(id); + public void SaveSensor(SensorSpec spec) { if (_sensorRepo.GetById(spec.Id) != null) _sensorRepo.Update(spec); else _sensorRepo.Insert(spec); } + public void DeleteSensor(string id) => _sensorRepo.Delete(id); + + // ═══ EnvironmentSpec ═══ + public List GetAllEnvironments() => _envRepo.GetAll(); + public EnvironmentSpec GetEnvironment(string id) => _envRepo.GetById(id); + public void SaveEnvironment(EnvironmentSpec spec) { if (_envRepo.GetById(spec.Id) != null) _envRepo.Update(spec); else _envRepo.Insert(spec); } + public void DeleteEnvironment(string id) => _envRepo.Delete(id); + + // ═══ FormationTemplate ═══ + public List GetAllFormations() => _formationRepo.GetAll(); + public FormationTemplate GetFormation(string id) => _formationRepo.GetById(id); + public void SaveFormation(FormationTemplate spec) { if (_formationRepo.GetById(spec.Id) != null) _formationRepo.Update(spec); else _formationRepo.Insert(spec); } + public void DeleteFormation(string id) => _formationRepo.Delete(id); + + // ═══ RouteTemplate ═══ + public List GetAllRoutes() => _routeRepo.GetAll(); + public RouteTemplate GetRoute(string id) => _routeRepo.GetById(id); + public void SaveRoute(RouteTemplate spec) { if (_routeRepo.GetById(spec.Id) != null) _routeRepo.Update(spec); else _routeRepo.Insert(spec); } + public void DeleteRoute(string id) => _routeRepo.Delete(id); + } +} diff --git a/src/CounterDrone.Core/Services/IDataService.cs b/src/CounterDrone.Core/Services/IDataService.cs new file mode 100644 index 0000000..20d351e --- /dev/null +++ b/src/CounterDrone.Core/Services/IDataService.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using CounterDrone.Core.Models; + +namespace CounterDrone.Core.Services +{ + /// 基础数据管理服务 — 弹药/火力单元/无人机/传感器/环境/编队/航线 全 CRUD + public interface IDataService + { + // ═══ AmmunitionSpec ═══ + List GetAllAmmo(); + AmmunitionSpec GetAmmo(string id); + void SaveAmmo(AmmunitionSpec spec); + void DeleteAmmo(string id); + + // ═══ FireUnitSpec ═══ + List GetAllFireUnits(); + FireUnitSpec GetFireUnit(string id); + void SaveFireUnit(FireUnitSpec spec); + void DeleteFireUnit(string id); + + // ═══ DroneSpec ═══ + List GetAllDrones(); + DroneSpec GetDrone(string id); + void SaveDrone(DroneSpec spec); + void DeleteDrone(string id); + + // ═══ SensorSpec ═══ + List GetAllSensors(); + SensorSpec GetSensor(string id); + void SaveSensor(SensorSpec spec); + void DeleteSensor(string id); + + // ═══ EnvironmentSpec ═══ + List GetAllEnvironments(); + EnvironmentSpec GetEnvironment(string id); + void SaveEnvironment(EnvironmentSpec spec); + void DeleteEnvironment(string id); + + // ═══ FormationTemplate ═══ + List GetAllFormations(); + FormationTemplate GetFormation(string id); + void SaveFormation(FormationTemplate spec); + void DeleteFormation(string id); + + // ═══ RouteTemplate ═══ + List GetAllRoutes(); + RouteTemplate GetRoute(string id); + void SaveRoute(RouteTemplate spec); + void DeleteRoute(string id); + } +} diff --git a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs index 115097f..f2ad479 100644 --- a/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs +++ b/src/Unity/Assets/Scripts/Managers/SimulationBootstrap.cs @@ -63,8 +63,8 @@ namespace CounterDrone.Unity var task = scenario.CreateTask("Unity Demo", ""); taskId = task.Id; - scenario.SaveScene(taskId, defaults.Weather.First(w => w.Id == "sunny-calm").ToCombatScene()); - scenario.SaveTarget(taskId, defaults.Targets.First(p => p.Id == "shahed").ToTargetConfig()); + scenario.SaveScene(taskId, defaults.Environments.First(w => w.Id == "sunny-calm").ToCombatScene()); + scenario.SaveTarget(taskId, defaults.Drones.First(p => p.Id == "shahed").ToTargetConfig()); scenario.SaveRoute(taskId, "default", defaults.Formations.First(f => f.Id == "single").ToRoutePlan(), defaults.Routes.First(r => _routeLength > 6000 ? r.Id == "10km-h500" : r.Id == "5km-h500")