多批次Phase1: RoutePlan改Id主键+Waypoint加GroupId+ScenarioService/Engine/所有测试适配
This commit is contained in:
parent
42b390a264
commit
12cb6576fe
@ -68,6 +68,7 @@ namespace CounterDrone.Core
|
||||
db.CreateTable<EquipmentDeployment>();
|
||||
db.CreateTable<CloudDispersal>();
|
||||
db.CreateTable<RoutePlan>();
|
||||
db.CreateIndex("RoutePlan", new[] { "TaskId", "GroupId" }, true);
|
||||
db.CreateTable<Waypoint>();
|
||||
db.CreateTable<Group>();
|
||||
db.CreateTable<SimulationReport>();
|
||||
|
||||
@ -1,14 +1,19 @@
|
||||
using System;
|
||||
using SQLite;
|
||||
|
||||
namespace CounterDrone.Core.Models
|
||||
{
|
||||
/// <summary>步骤5:航路规划</summary>
|
||||
/// <summary>步骤5:航路规划 — 多编队支持:每个(任务,编队)一条航路</summary>
|
||||
[Table("RoutePlan")]
|
||||
public class RoutePlan
|
||||
{
|
||||
[PrimaryKey]
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
|
||||
[Indexed]
|
||||
public string TaskId { get; set; } = string.Empty;
|
||||
|
||||
[Indexed]
|
||||
public string GroupId { get; set; } = string.Empty;
|
||||
|
||||
public int FormationMode { get; set; } = (int)Models.FormationMode.Single;
|
||||
|
||||
@ -11,8 +11,10 @@ namespace CounterDrone.Core.Models
|
||||
public List<TargetConfig> Targets { get; set; } = new();
|
||||
public List<EquipmentDeployment> Equipment { get; set; } = new();
|
||||
public CloudDispersal Cloud { get; set; } = new();
|
||||
public RoutePlan Route { get; set; } = new();
|
||||
public List<Waypoint> Waypoints { get; set; } = new();
|
||||
/// <summary>多编队航路(多批次支持)</summary>
|
||||
public List<RoutePlan> Routes { get; set; } = new();
|
||||
/// <summary>按 GroupId 分组的航路点</summary>
|
||||
public Dictionary<string, List<Waypoint>> WaypointGroups { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>分页结果</summary>
|
||||
|
||||
@ -13,6 +13,9 @@ namespace CounterDrone.Core.Models
|
||||
[Indexed]
|
||||
public string TaskId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>关联的编队 ID(多编队支持)</summary>
|
||||
public string GroupId { get; set; } = string.Empty;
|
||||
|
||||
[NotNull]
|
||||
public int OrderIndex { get; set; }
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CounterDrone.Core.Models;
|
||||
using SQLite;
|
||||
|
||||
@ -6,5 +8,16 @@ namespace CounterDrone.Core.Repository
|
||||
public class RoutePlanRepository : BaseRepository<RoutePlan>
|
||||
{
|
||||
public RoutePlanRepository(SQLiteConnection db) : base(db) { }
|
||||
|
||||
public List<RoutePlan> GetByTaskId(string taskId)
|
||||
{
|
||||
return Db.Table<RoutePlan>().Where(r => r.TaskId == taskId).ToList();
|
||||
}
|
||||
|
||||
public RoutePlan GetByTaskAndGroup(string taskId, string groupId)
|
||||
{
|
||||
return Db.Table<RoutePlan>()
|
||||
.FirstOrDefault(r => r.TaskId == taskId && r.GroupId == groupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,14 @@ namespace CounterDrone.Core.Repository
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public List<Waypoint> GetByTaskAndGroup(string taskId, string groupId)
|
||||
{
|
||||
return Db.Table<Waypoint>()
|
||||
.Where(w => w.TaskId == taskId && w.GroupId == groupId)
|
||||
.OrderBy(w => w.OrderIndex)
|
||||
.ToList();
|
||||
}
|
||||
|
||||
public void DeleteByTaskId(string taskId)
|
||||
{
|
||||
var wps = GetByTaskId(taskId);
|
||||
|
||||
@ -16,7 +16,7 @@ namespace CounterDrone.Core.Services
|
||||
void SaveTarget(string taskId, TargetConfig target);
|
||||
void SaveDeployment(string taskId, List<EquipmentDeployment> equips);
|
||||
void SaveCloudDispersal(string taskId, CloudDispersal cloud);
|
||||
void SaveRoute(string taskId, RoutePlan route, List<Waypoint> waypoints);
|
||||
void SaveRoute(string taskId, string groupId, RoutePlan route, List<Waypoint> waypoints);
|
||||
void UpdateStep(string taskId, int step);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using CounterDrone.Core.Models;
|
||||
using CounterDrone.Core.Repository;
|
||||
@ -98,8 +99,10 @@ namespace CounterDrone.Core.Services
|
||||
Targets = _targetRepo.GetByTaskId(id),
|
||||
Equipment = _equipRepo.GetByTaskId(id),
|
||||
Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { TaskId = id },
|
||||
Route = _routeRepo.GetById(id) ?? new RoutePlan { TaskId = id },
|
||||
Waypoints = _waypointRepo.GetByTaskId(id),
|
||||
Routes = _routeRepo.GetByTaskId(id),
|
||||
WaypointGroups = _waypointRepo.GetByTaskId(id)
|
||||
.GroupBy(w => w.GroupId)
|
||||
.ToDictionary(g => g.Key, g => g.OrderBy(w => w.OrderIndex).ToList()),
|
||||
};
|
||||
}
|
||||
|
||||
@ -166,19 +169,26 @@ namespace CounterDrone.Core.Services
|
||||
TouchTask(taskId);
|
||||
}
|
||||
|
||||
public void SaveRoute(string taskId, RoutePlan route, List<Waypoint> waypoints)
|
||||
public void SaveRoute(string taskId, string groupId, RoutePlan route, List<Waypoint> waypoints)
|
||||
{
|
||||
route.TaskId = taskId;
|
||||
var existingRoute = _routeRepo.GetById(taskId);
|
||||
if (existingRoute != null)
|
||||
route.GroupId = groupId;
|
||||
var existing = _routeRepo.GetByTaskAndGroup(taskId, groupId);
|
||||
if (existing != null)
|
||||
{
|
||||
route.Id = existing.Id;
|
||||
_routeRepo.Update(route);
|
||||
}
|
||||
else
|
||||
_routeRepo.Insert(route);
|
||||
|
||||
_waypointRepo.DeleteByTaskId(taskId);
|
||||
// 删除该编队的旧航路点,插入新的
|
||||
var oldWps = _waypointRepo.GetByTaskAndGroup(taskId, groupId);
|
||||
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].OrderIndex = i;
|
||||
if (string.IsNullOrEmpty(waypoints[i].Id))
|
||||
waypoints[i].Id = Guid.NewGuid().ToString();
|
||||
|
||||
@ -86,11 +86,14 @@ namespace CounterDrone.Core.Simulation
|
||||
_drones.Clear();
|
||||
foreach (var target in config.Targets)
|
||||
{
|
||||
var mode = (FormationMode)config.Route.FormationMode;
|
||||
var spacing = (float)config.Route.FormationSpacing;
|
||||
var route = config.Routes.FirstOrDefault(r => r.GroupId == target.GroupId);
|
||||
var wps = config.WaypointGroups.GetValueOrDefault(target.GroupId, new List<Waypoint>());
|
||||
if (route == null || wps.Count == 0) continue;
|
||||
var mode = (FormationMode)route.FormationMode;
|
||||
var spacing = (float)route.FormationSpacing;
|
||||
for (int i = 0; i < target.Quantity; i++)
|
||||
_drones.Add(new DroneEntity($"drone_{++_entityCounter}", target.GroupId,
|
||||
target, config.Waypoints, i, spacing, mode));
|
||||
target, wps, i, spacing, mode));
|
||||
}
|
||||
|
||||
_platforms.Clear();
|
||||
|
||||
Binary file not shown.
11
src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta
Normal file
11
src/Unity/Assets/Scripts/Managers/GroupManager.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 24c080ea3d6cbcf45aa804fd10269e4a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -36,7 +36,7 @@ namespace CounterDrone.Unity
|
||||
TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston,
|
||||
Quantity = 1, TypicalSpeed = 200, TypicalAltitude = 500,
|
||||
});
|
||||
scenarioMgr.SaveRoute(taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
scenarioMgr.SaveRoute(taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 500, PosZ = 0 },
|
||||
|
||||
@ -35,7 +35,7 @@ namespace CounterDrone.Unity
|
||||
public void SaveTarget(string taskId, TargetConfig t) => _service.SaveTarget(taskId, t);
|
||||
public void SaveDeployment(string taskId, List<EquipmentDeployment> e) => _service.SaveDeployment(taskId, e);
|
||||
public void SaveCloud(string taskId, CloudDispersal c) => _service.SaveCloudDispersal(taskId, c);
|
||||
public void SaveRoute(string taskId, RoutePlan r, List<Waypoint> w) => _service.SaveRoute(taskId, r, w);
|
||||
public void SaveRoute(string taskId, string groupId, RoutePlan r, List<Waypoint> w) => _service.SaveRoute(taskId, groupId, r, w);
|
||||
public void UpdateStep(string taskId, int step) => _service.UpdateStep(taskId, step);
|
||||
|
||||
[ContextMenu("Verify")]
|
||||
|
||||
@ -54,7 +54,7 @@ namespace CounterDrone.Unity
|
||||
TargetType = (int)TargetType.Piston, PowerType = (int)PowerType.Piston,
|
||||
Quantity = 1, TypicalSpeed = _droneSpeed, TypicalAltitude = 500,
|
||||
});
|
||||
scenario.SaveRoute(taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
scenario.SaveRoute(taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = _droneSpeed },
|
||||
|
||||
@ -49,6 +49,7 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Piston,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 100,
|
||||
@ -56,7 +57,7 @@ namespace CounterDrone.Core.Tests
|
||||
});
|
||||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
|
||||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0 },
|
||||
@ -91,13 +92,14 @@ namespace CounterDrone.Core.Tests
|
||||
});
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Piston,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 600, // 高速对抗强风
|
||||
});
|
||||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>());
|
||||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0 },
|
||||
@ -129,6 +131,7 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.HighSpeed,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 500,
|
||||
@ -137,7 +140,7 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal());
|
||||
|
||||
// 500 km/h, 100km 航程
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 500, PosZ = 0 },
|
||||
|
||||
@ -85,8 +85,8 @@ namespace CounterDrone.Core.Tests
|
||||
{
|
||||
Environment = detail.Scene,
|
||||
Targets = detail.Targets,
|
||||
Route = detail.Route,
|
||||
Waypoints = detail.Waypoints,
|
||||
Route = detail.Routes[0],
|
||||
Waypoints = detail.WaypointGroups["default"],
|
||||
};
|
||||
var rec = new DefaultDefenseAdvisor(_ammoCatalog).Recommend(threat);
|
||||
|
||||
@ -151,13 +151,14 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.FixedWing,
|
||||
PowerType = (int)PowerType.Jet,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 600,
|
||||
TypicalAltitude = 400,
|
||||
});
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 400, PosZ = 0, Speed = 600 },
|
||||
@ -183,13 +184,14 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Electric,
|
||||
PowerType = (int)PowerType.Electric,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 300,
|
||||
TypicalAltitude = 300,
|
||||
});
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0, Speed = 300 },
|
||||
@ -228,13 +230,14 @@ namespace CounterDrone.Core.Tests
|
||||
});
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Piston,
|
||||
PowerType = (int)PowerType.Piston,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 200,
|
||||
TypicalAltitude = 500,
|
||||
});
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 500, PosZ = 0, Speed = 200 },
|
||||
@ -294,13 +297,14 @@ namespace CounterDrone.Core.Tests
|
||||
_scenario.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenario.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.HighSpeed,
|
||||
PowerType = (int)PowerType.Jet,
|
||||
Quantity = 1,
|
||||
TypicalSpeed = 500,
|
||||
TypicalAltitude = 800,
|
||||
});
|
||||
_scenario.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 800, PosZ = 0, Speed = 500 },
|
||||
|
||||
@ -103,6 +103,7 @@ namespace CounterDrone.Core.Tests
|
||||
|
||||
_service.SaveTarget(task.Id, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.FixedWing,
|
||||
Quantity = 3,
|
||||
});
|
||||
@ -200,6 +201,7 @@ namespace CounterDrone.Core.Tests
|
||||
var task = _service.CreateTask("Target Test", "");
|
||||
_service.SaveTarget(task.Id, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Piston,
|
||||
Quantity = 5,
|
||||
PowerType = (int)PowerType.Piston,
|
||||
@ -265,14 +267,14 @@ namespace CounterDrone.Core.Tests
|
||||
new Waypoint { PosX = 10000, PosY = 0, PosZ = 100, Altitude = 300, Speed = 80 },
|
||||
};
|
||||
|
||||
_service.SaveRoute(task.Id, route, waypoints);
|
||||
_service.SaveRoute(task.Id, "default", route, waypoints);
|
||||
|
||||
var detail = _service.GetTaskDetail(task.Id);
|
||||
Assert.Equal((int)FormationMode.Formation, detail.Route.FormationMode);
|
||||
Assert.Equal(3, detail.Waypoints.Count);
|
||||
Assert.Equal(0, detail.Waypoints[0].OrderIndex);
|
||||
Assert.Equal(2, detail.Waypoints[2].OrderIndex);
|
||||
Assert.Equal(5000.0, detail.Waypoints[1].PosX);
|
||||
Assert.Equal((int)FormationMode.Formation, detail.Routes[0].FormationMode);
|
||||
Assert.Equal(3, detail.WaypointGroups["default"].Count);
|
||||
Assert.Equal(0, detail.WaypointGroups["default"][0].OrderIndex);
|
||||
Assert.Equal(2, detail.WaypointGroups["default"][2].OrderIndex);
|
||||
Assert.Equal(5000.0, detail.WaypointGroups["default"][1].PosX);
|
||||
}
|
||||
|
||||
// ========== Search & Pagination ==========
|
||||
@ -392,6 +394,7 @@ namespace CounterDrone.Core.Tests
|
||||
// 4. 步骤2:目标配置
|
||||
_service.SaveTarget(task.Id, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.HighSpeed,
|
||||
Quantity = 2,
|
||||
PowerType = (int)PowerType.Jet,
|
||||
@ -433,7 +436,7 @@ namespace CounterDrone.Core.Tests
|
||||
_service.UpdateStep(task.Id, 4);
|
||||
|
||||
// 7. 步骤5:航路规划
|
||||
_service.SaveRoute(task.Id, new RoutePlan
|
||||
_service.SaveRoute(task.Id, "default", new RoutePlan
|
||||
{
|
||||
FormationMode = (int)FormationMode.Swarm,
|
||||
FormationSpacing = 30.0,
|
||||
@ -473,9 +476,9 @@ namespace CounterDrone.Core.Tests
|
||||
Assert.Equal(50.0, detail.Cloud.Duration);
|
||||
|
||||
// 航路
|
||||
Assert.Equal((int)FormationMode.Swarm, detail.Route.FormationMode);
|
||||
Assert.Equal(2, detail.Waypoints.Count);
|
||||
Assert.Equal(250.0, detail.Waypoints[0].Speed);
|
||||
Assert.Equal((int)FormationMode.Swarm, detail.Routes[0].FormationMode);
|
||||
Assert.Equal(2, detail.WaypointGroups["default"].Count);
|
||||
Assert.Equal(250.0, detail.WaypointGroups["default"][0].Speed);
|
||||
|
||||
// 9. 搜索验证
|
||||
var searchResult = _service.SearchTasks("完整流程", null, null, 1, 10);
|
||||
|
||||
@ -53,6 +53,7 @@ namespace CounterDrone.Core.Tests
|
||||
_scenarioService.SaveScene(_taskId, new CombatScene { WindSpeed = 0 });
|
||||
_scenarioService.SaveTarget(_taskId, new TargetConfig
|
||||
{
|
||||
GroupId = "default",
|
||||
TargetType = (int)TargetType.Piston,
|
||||
Quantity = 1,
|
||||
PowerType = (int)PowerType.Piston,
|
||||
@ -79,7 +80,7 @@ namespace CounterDrone.Core.Tests
|
||||
PositionZ = 0,
|
||||
PositionMode = (int)PositionMode.AlgorithmRecommended,
|
||||
});
|
||||
_scenarioService.SaveRoute(_taskId, new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
_scenarioService.SaveRoute(_taskId, "default", new RoutePlan { FormationMode = (int)FormationMode.Single },
|
||||
new List<Waypoint>
|
||||
{
|
||||
new Waypoint { PosX = 0, PosY = 300, PosZ = 0, Altitude = 300, Speed = 60 },
|
||||
|
||||
Loading…
Reference in New Issue
Block a user