CounterDroneBackend/src/CounterDrone.Core/Services/ScenarioService.cs
tian a6ad8a348e GetEnums() +SensorType 枚举(雷达/光电/红外)
- 新增 SensorType 枚举(Radar=0, EO=1, IR=2)供前端探测设备 UI 分类
- EnumMetadata + GetEnums() 加入 SensorType(含中文翻译)
- 对接文档同步更新
- 262 测试通过,Unity 编译通过
2026-06-21 10:33:57 +08:00

491 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using CounterDrone.Core.Models;
using CounterDrone.Core.Repository;
namespace CounterDrone.Core.Services
{
public class ScenarioService : IScenarioService
{
private readonly ScenarioRepository _scenarioRepo;
private readonly CombatSceneRepository _sceneRepo;
private readonly ControlZoneRepository _zoneRepo;
private readonly ScenarioDroneRepository _droneRepo;
private readonly ScenarioUnitRepository _unitRepo;
private readonly CloudDispersalRepository _cloudRepo;
private readonly RoutePlanRepository _routeRepo;
private readonly WaypointRepository _waypointRepo;
public ScenarioService(
ScenarioRepository scenarioRepo,
CombatSceneRepository sceneRepo,
ControlZoneRepository zoneRepo,
ScenarioDroneRepository targetRepo,
ScenarioUnitRepository equipRepo,
CloudDispersalRepository cloudRepo,
RoutePlanRepository routeRepo,
WaypointRepository waypointRepo)
{
_scenarioRepo = scenarioRepo;
_sceneRepo = sceneRepo;
_zoneRepo = zoneRepo;
_droneRepo = targetRepo;
_unitRepo = equipRepo;
_cloudRepo = cloudRepo;
_routeRepo = routeRepo;
_waypointRepo = waypointRepo;
}
// ========== scenario CRUD ==========
public Scenario CreateScenario(string name, string ScenarioNumber)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentException("任务名称不能为空");
if (string.IsNullOrWhiteSpace(ScenarioNumber))
ScenarioNumber = GenerateScenarioNumber();
var existing = _scenarioRepo.GetByScenarioNumber(ScenarioNumber);
if (existing != null)
throw new ArgumentException($"任务编号 {ScenarioNumber} 已存在");
var scenario = new Scenario
{
Name = name,
ScenarioNumber = ScenarioNumber,
Status = (int)ScenarioStatus.Configuring,
CurrentStep = 1,
};
_scenarioRepo.Insert(scenario);
return scenario;
}
public void DeleteScenario(string id)
{
_scenarioRepo.Delete(id);
}
public PagedResult<Scenario> SearchScenarios(string keyword, string? dateFrom, string? dateTo, int page, int pageSize)
{
if (page < 1) page = 1;
if (pageSize < 1) pageSize = 10;
var offset = (page - 1) * pageSize;
var items = _scenarioRepo.Search(keyword, dateFrom, dateTo, offset, pageSize, out var total);
return new PagedResult<Scenario>
{
Items = items,
TotalCount = total,
Page = page,
PageSize = pageSize,
};
}
public ScenarioConfig? GetScenarioDetail(string id)
{
var scenario = _scenarioRepo.GetById(id);
if (scenario == null) return null;
return new ScenarioConfig
{
Info = scenario,
Scene = _sceneRepo.GetById(id) ?? new CombatScene { ScenarioId = id },
ControlZones = _zoneRepo.GetByScenarioId(id),
Drones = _droneRepo.GetByScenarioId(id),
Units = _unitRepo.GetByScenarioId(id),
Cloud = _cloudRepo.GetById(id) ?? new CloudDispersal { ScenarioId = id },
Routes = _routeRepo.GetByScenarioId(id),
WaypointGroups = _waypointRepo.GetByScenarioId(id)
.GroupBy(w => w.WaveId)
.ToDictionary(g => g.Key, g => g.OrderBy(w => w.OrderIndex).ToList()),
};
}
// ========== Step Save ==========
public void SaveScene(string scenarioId, CombatScene scene)
{
scene.ScenarioId = scenarioId;
var existing = _sceneRepo.GetById(scenarioId);
if (existing != null)
_sceneRepo.Update(scene);
else
_sceneRepo.Insert(scene);
TouchScenario(scenarioId);
}
public void SaveControlZones(string scenarioId, List<ControlZone> zones)
{
_zoneRepo.DeleteByScenarioId(scenarioId);
for (int i = 0; i < zones.Count; i++)
{
zones[i].ScenarioId = scenarioId;
zones[i].OrderIndex = i;
}
_zoneRepo.InsertAll(zones);
TouchScenario(scenarioId);
}
public ScenarioDrone SaveScenarioDrone(string scenarioId, ScenarioDrone target)
{
target.ScenarioId = scenarioId;
if (string.IsNullOrEmpty(target.Id))
target.Id = Guid.NewGuid().ToString();
var existing = _droneRepo.GetById(target.Id);
if (existing != null)
_droneRepo.Update(target);
else
_droneRepo.Insert(target);
TouchScenario(scenarioId);
return target;
}
public void SaveDeployment(string scenarioId, List<ScenarioUnit> equips)
{
_unitRepo.DeleteByScenarioId(scenarioId);
foreach (var e in equips)
{
e.ScenarioId = scenarioId;
if (string.IsNullOrEmpty(e.Id))
e.Id = Guid.NewGuid().ToString();
}
_unitRepo.InsertAll(equips);
TouchScenario(scenarioId);
}
public void AddDetection(string scenarioId, ScenarioUnit detection)
{
detection.ScenarioId = scenarioId;
detection.EquipmentRole = (int)EquipmentRole.Detection;
if (string.IsNullOrEmpty(detection.Id))
detection.Id = Guid.NewGuid().ToString();
_unitRepo.Insert(detection);
TouchScenario(scenarioId);
}
public void DeleteDetection(string detectionId)
{
_unitRepo.Delete(detectionId);
}
public List<ScenarioUnit> GetDetections(string scenarioId)
{
return _unitRepo.GetByScenarioIdAndRole(scenarioId, (int)EquipmentRole.Detection);
}
public void SaveCloudDispersal(string scenarioId, CloudDispersal cloud)
{
cloud.ScenarioId = scenarioId;
var existing = _cloudRepo.GetById(scenarioId);
if (existing != null)
_cloudRepo.Update(cloud);
else
_cloudRepo.Insert(cloud);
TouchScenario(scenarioId);
}
public void SaveRoute(string scenarioId, string waveId, RoutePlan route, List<Waypoint> waypoints)
{
route.ScenarioId = scenarioId;
route.WaveId = waveId;
var existing = _routeRepo.GetByScenarioAndWave(scenarioId, waveId);
if (existing != null)
{
route.Id = existing.Id;
_routeRepo.Update(route);
}
else
_routeRepo.Insert(route);
// 删除该批次的旧航路点,插入新的
var oldWps = _waypointRepo.GetByScenarioAndWave(scenarioId, waveId);
foreach (var w in oldWps) _waypointRepo.Delete(w.Id);
for (int i = 0; i < waypoints.Count; i++)
{
waypoints[i].ScenarioId = scenarioId;
waypoints[i].WaveId = waveId;
waypoints[i].OrderIndex = i;
if (string.IsNullOrEmpty(waypoints[i].Id))
waypoints[i].Id = Guid.NewGuid().ToString();
}
_waypointRepo.InsertAll(waypoints);
TouchScenario(scenarioId);
}
public void UpdateStep(string scenarioId, int step)
{
var scenario = _scenarioRepo.GetById(scenarioId);
if (scenario == null) return;
if (step < 1) step = 1;
if (step > 5) step = 5;
scenario.CurrentStep = step;
_scenarioRepo.Update(scenario);
}
public EnumMetadata GetEnums()
{
return new EnumMetadata
{
DroneType = EnumToList<Models.DroneType>(TranslateDrone),
PowerType = EnumToList<Models.PowerType>(TranslatePower),
PlatformType = EnumToList<Models.PlatformType>(TranslatePlatform),
AerosolType = EnumToList<Models.AerosolType>(TranslateAerosol),
WeatherType = EnumToList<Models.WeatherType>(TranslateWeather),
WindDirection = EnumToList<Models.WindDirection>(TranslateWindDirection),
SceneType = EnumToList<Models.SceneType>(TranslateScene),
FormationMode = EnumToList<Models.FormationMode>(TranslateFormation),
TriggerMode = EnumToList<Models.TriggerMode>(TranslateTrigger),
ReleaseMode = EnumToList<Models.ReleaseMode>(TranslateRelease),
EquipmentRole = EnumToList<Models.EquipmentRole>(TranslateEquipmentRole),
EntityType = EnumToList<Models.EntityType>(TranslateEntityType),
SensorType = EnumToList<Models.SensorType>(TranslateSensorType),
};
}
private static List<EnumItem> EnumToList<T>(Func<T, string> translate) where T : Enum
{
var list = new List<EnumItem>();
foreach (T v in Enum.GetValues(typeof(T)))
list.Add(new EnumItem { Name = v.ToString(), ChineseName = translate(v), Value = Convert.ToInt32(v) });
return list;
}
private static Dictionary<string, int> EnumToDict<T>() where T : Enum
{
var dict = new Dictionary<string, int>();
foreach (T v in Enum.GetValues(typeof(T)))
dict[v.ToString()] = Convert.ToInt32(v);
return dict;
}
// ═══ 中文翻译 ═══
private static string TranslateDrone(Models.DroneType t) => t switch
{
Models.DroneType.Rotor => "旋翼",
Models.DroneType.FixedWing => "固定翼",
Models.DroneType.HighSpeed => "高速目标",
_ => t.ToString(),
};
private static string TranslatePower(Models.PowerType p) => p switch
{
Models.PowerType.Electric => "电推",
Models.PowerType.Piston => "活塞",
Models.PowerType.Jet => "喷气",
_ => p.ToString(),
};
private static string TranslatePlatform(Models.PlatformType p) => p switch
{
Models.PlatformType.AirBased => "空基",
Models.PlatformType.GroundBased => "地基",
_ => p.ToString(),
};
private static string TranslateAerosol(Models.AerosolType t) => t switch
{
Models.AerosolType.InertGas => "惰性气体(吸入式灭火)",
Models.AerosolType.ActiveMaterial => "活性材料(爆燃式)",
Models.AerosolType.ActiveFuel => "活性燃料(吸入式爆炸)",
_ => t.ToString(),
};
private static string TranslateWeather(Models.WeatherType w) => w switch
{
Models.WeatherType.Sunny => "晴天",
Models.WeatherType.Overcast => "阴天",
Models.WeatherType.Fog => "雾天",
Models.WeatherType.Rain => "雨天",
Models.WeatherType.Night => "夜间",
_ => w.ToString(),
};
private static string TranslateWindDirection(Models.WindDirection d) => d switch
{
Models.WindDirection.N => "北 (N)",
Models.WindDirection.NE => "东北 (NE)",
Models.WindDirection.E => "东 (E)",
Models.WindDirection.SE => "东南 (SE)",
Models.WindDirection.S => "南 (S)",
Models.WindDirection.SW => "西南 (SW)",
Models.WindDirection.W => "西 (W)",
Models.WindDirection.NW => "西北 (NW)",
_ => d.ToString(),
};
private static string TranslateScene(Models.SceneType s) => s switch
{
Models.SceneType.Urban => "城市",
Models.SceneType.Plain => "平原",
Models.SceneType.Mountain => "山区",
Models.SceneType.Coast => "海岸",
_ => s.ToString(),
};
private static string TranslateFormation(Models.FormationMode f) => f switch
{
Models.FormationMode.Single => "单机",
Models.FormationMode.Formation => "编队",
Models.FormationMode.Swarm => "蜂群",
_ => f.ToString(),
};
private static string TranslateTrigger(Models.TriggerMode t) => t switch
{
Models.TriggerMode.Time => "时间触发",
Models.TriggerMode.Area => "区域触发",
Models.TriggerMode.Manual => "手动触发",
_ => t.ToString(),
};
private static string TranslateRelease(Models.ReleaseMode r) => r switch
{
Models.ReleaseMode.Single => "单次",
Models.ReleaseMode.Continuous => "连续",
Models.ReleaseMode.Pulse => "脉冲",
_ => r.ToString(),
};
private static string TranslateEquipmentRole(Models.EquipmentRole r) => r switch
{
Models.EquipmentRole.Detection => "探测设备",
Models.EquipmentRole.LaunchPlatform => "发射平台",
_ => r.ToString(),
};
private static string TranslateSensorType(Models.SensorType t) => t switch
{
Models.SensorType.Radar => "雷达",
Models.SensorType.EO => "光电",
Models.SensorType.IR => "红外",
_ => t.ToString(),
};
private static string TranslateEntityType(Models.EntityType t) => t switch
{
Models.EntityType.Drone => "无人机",
Models.EntityType.Platform => "发射平台",
Models.EntityType.DetectionEquip => "探测设备",
Models.EntityType.Cloud => "云团",
Models.EntityType.Munition => "弹药",
_ => t.ToString(),
};
// ========== Scenario CRUD ==========
public Scenario UpdateScenario(Scenario scenario)
{
var existing = _scenarioRepo.GetById(scenario.Id);
if (existing == null)
throw new ArgumentException($"想定 {scenario.Id} 不存在");
scenario.UpdatedAt = DateTime.UtcNow.ToString("o");
_scenarioRepo.Update(scenario);
return scenario;
}
// ========== CombatScene ==========
public CombatScene GetScene(string scenarioId)
{
return _sceneRepo.GetById(scenarioId) ?? new CombatScene { ScenarioId = scenarioId };
}
// ========== ControlZone ==========
public List<ControlZone> GetControlZones(string scenarioId)
=> _zoneRepo.GetByScenarioId(scenarioId);
public void DeleteControlZone(string id)
=> _zoneRepo.Delete(id);
// ========== ScenarioDrone ==========
public ScenarioDrone AddScenarioDrone(string scenarioId, ScenarioDrone drone)
=> SaveScenarioDrone(scenarioId, drone);
public void UpdateScenarioDrone(ScenarioDrone drone)
{
_droneRepo.Update(drone);
TouchScenario(drone.ScenarioId);
}
public void DeleteScenarioDrone(string id)
=> _droneRepo.Delete(id);
public List<ScenarioDrone> GetScenarioDrones(string scenarioId)
=> _droneRepo.GetByScenarioId(scenarioId);
// ========== ScenarioUnit ==========
public ScenarioUnit AddDeploymentUnit(string scenarioId, ScenarioUnit unit)
{
unit.ScenarioId = scenarioId;
if (string.IsNullOrEmpty(unit.Id))
unit.Id = Guid.NewGuid().ToString();
_unitRepo.Insert(unit);
TouchScenario(scenarioId);
return unit;
}
public void UpdateDeploymentUnit(ScenarioUnit unit)
=> _unitRepo.Update(unit);
public void DeleteDeploymentUnit(string id)
=> _unitRepo.Delete(id);
public List<ScenarioUnit> GetDeploymentUnits(string scenarioId)
=> _unitRepo.GetByScenarioId(scenarioId);
// ========== CloudDispersal ==========
public CloudDispersal GetCloudDispersal(string scenarioId)
=> _cloudRepo.GetById(scenarioId) ?? new CloudDispersal { ScenarioId = scenarioId };
public void DeleteCloudDispersal(string scenarioId)
=> _cloudRepo.Delete(scenarioId);
// ========== Route & Waypoint ==========
public List<RoutePlan> GetRoutes(string scenarioId)
=> _routeRepo.GetByScenarioId(scenarioId);
public void DeleteRoute(string id)
{
var r = _routeRepo.GetById(id);
if (r == null) return;
var wps = _waypointRepo.GetByScenarioAndWave(r.ScenarioId, r.WaveId);
foreach (var w in wps) _waypointRepo.Delete(w.Id);
_routeRepo.Delete(id);
}
public List<Waypoint> GetWaypoints(string scenarioId)
=> _waypointRepo.GetByScenarioId(scenarioId);
public List<Waypoint> GetWaypointsByWave(string scenarioId, string waveId)
=> _waypointRepo.GetByScenarioAndWave(scenarioId, waveId);
public void DeleteWaypoint(string id)
=> _waypointRepo.Delete(id);
// ========== Helpers ==========
private void TouchScenario(string scenarioId)
{
var scenario = _scenarioRepo.GetById(scenarioId);
if (scenario != null)
{
scenario.UpdatedAt = DateTime.UtcNow.ToString("o");
_scenarioRepo.Update(scenario);
}
}
private static int _counter;
public static string GenerateScenarioNumber()
{
var now = DateTime.Now;
var datePart = now.ToString("yyyyMMdd");
var seq = Interlocked.Increment(ref _counter);
return $"SIM-{datePart}-{seq:D3}";
}
}
}