378 lines
13 KiB
C#
378 lines
13 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 = EnumToDict<Models.DroneType>(),
|
|
PowerType = EnumToDict<Models.PowerType>(),
|
|
PlatformType = EnumToDict<Models.PlatformType>(),
|
|
AerosolType = EnumToDict<Models.AerosolType>(),
|
|
WeatherType = EnumToDict<Models.WeatherType>(),
|
|
WindDirection = EnumToDict<Models.WindDirection>(),
|
|
SceneType = EnumToDict<Models.SceneType>(),
|
|
FormationMode = EnumToDict<Models.FormationMode>(),
|
|
TriggerMode = EnumToDict<Models.TriggerMode>(),
|
|
ReleaseMode = EnumToDict<Models.ReleaseMode>(),
|
|
EquipmentRole = EnumToDict<Models.EquipmentRole>(),
|
|
};
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
// ========== 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}";
|
|
}
|
|
}
|
|
}
|