486 lines
21 KiB
C#
486 lines
21 KiB
C#
using Tomlyn;
|
||
using System.Diagnostics;
|
||
using System.Reflection;
|
||
|
||
namespace ThreatSource.Data
|
||
{
|
||
/// <summary>
|
||
/// 威胁源数据管理器,负责加载和管理所有设备的配置数据
|
||
/// </summary>
|
||
/// <remarks>
|
||
/// 该类提供以下功能:
|
||
/// - 加载配置数据
|
||
/// - 获取可用设备列表
|
||
/// - 获取设备配置数据
|
||
/// 用于统一管理威胁源库中的各类设备数据
|
||
/// </remarks>
|
||
public class ThreatSourceDataManager
|
||
{
|
||
/// <summary>
|
||
/// 默认数据目录名称
|
||
/// </summary>
|
||
private static readonly string DEFAULT_DATA_FOLDER = "data";
|
||
|
||
/// <summary>
|
||
/// 回退数据目录路径(向后兼容)
|
||
/// </summary>
|
||
private static readonly string FALLBACK_DATA_PATH = "data";
|
||
|
||
private static readonly TomlModelOptions _tomlOptions = new()
|
||
{
|
||
ConvertPropertyName = name => name
|
||
};
|
||
|
||
private readonly Dictionary<string, MissileData> _missiles = [];
|
||
private readonly Dictionary<string, IndicatorData> _indicators = [];
|
||
private readonly Dictionary<string, SensorData> _sensors = [];
|
||
private readonly Dictionary<string, EquipmentData> _equipments = [];
|
||
private readonly Dictionary<string, WeatherData> _weathers = [];
|
||
private readonly Dictionary<string, JammerData> _jammers = [];
|
||
|
||
/// <summary>
|
||
/// 获取数据目录路径
|
||
/// 优先使用DLL上级目录的data文件夹,如果不存在则回退到相对路径
|
||
/// </summary>
|
||
/// <returns>数据目录的绝对路径</returns>
|
||
private static string GetDataPath()
|
||
{
|
||
try
|
||
{
|
||
// 获取当前程序集(DLL)的位置
|
||
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
|
||
if (!string.IsNullOrEmpty(assemblyLocation))
|
||
{
|
||
// 获取DLL所在目录的父目录
|
||
var dllDirectory = Path.GetDirectoryName(assemblyLocation);
|
||
if (!string.IsNullOrEmpty(dllDirectory))
|
||
{
|
||
var parentDirectory = Directory.GetParent(dllDirectory)?.FullName;
|
||
if (!string.IsNullOrEmpty(parentDirectory))
|
||
{
|
||
var dataPath = Path.Combine(parentDirectory, DEFAULT_DATA_FOLDER);
|
||
|
||
Debug.WriteLine($"[ThreatSourceDataManager] DLL位置: {assemblyLocation}");
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 解析的data目录: {dataPath}");
|
||
|
||
if (Directory.Exists(dataPath))
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 使用自动解析的data目录: {dataPath}");
|
||
return dataPath;
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 自动解析的data目录不存在: {dataPath}");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 自动路径解析失败: {ex.Message}");
|
||
}
|
||
|
||
// 回退到原始的相对路径(向后兼容)
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 回退使用相对路径: {FALLBACK_DATA_PATH}");
|
||
return FALLBACK_DATA_PATH;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化威胁源数据管理器
|
||
/// </summary>
|
||
/// <param name="dataPath">可选的数据目录路径,如果为null则使用智能路径解析</param>
|
||
public ThreatSourceDataManager(string? dataPath = null)
|
||
{
|
||
if (dataPath != null)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 使用用户指定的data路径: {dataPath}");
|
||
LoadData(dataPath);
|
||
}
|
||
else
|
||
{
|
||
var resolvedPath = GetDataPath();
|
||
LoadData(resolvedPath);
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载数据
|
||
/// </summary>
|
||
/// <param name="path">数据目录路径</param>
|
||
private void LoadData(string path)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] 开始从路径加载数据: {Path.GetFullPath(path)}");
|
||
|
||
LoadMissiles(Path.Combine(path, "missiles"));
|
||
LoadIndicators(Path.Combine(path, "indicators"));
|
||
LoadWarners(Path.Combine(path, "warners"));
|
||
LoadTargets(Path.Combine(path, "equipments"));
|
||
LoadWeathers(Path.Combine(path, "weathers"));
|
||
LoadJammers(Path.Combine(path, "jammers"));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载导弹数据
|
||
/// </summary>
|
||
private void LoadMissiles(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"导弹数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<MissileData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
if (data.Properties?.GuidanceSuite != null && data.Properties.GuidanceSuite.Count > 0)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Post-processing GuidanceSuite for missile file: {Path.GetFileName(file)} with {data.Properties.GuidanceSuite.Count} component(s).");
|
||
foreach (var componentConfig in data.Properties.GuidanceSuite)
|
||
{
|
||
if (componentConfig == null)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Warning: Encountered a null componentConfig in GuidanceSuite for {Path.GetFileName(file)}.");
|
||
continue;
|
||
}
|
||
|
||
string? typeKey = componentConfig.GuidanceSystemType?.ToLowerInvariant();
|
||
object? assignedConfig = null; // Will hold the config object from MissileData
|
||
string? topLevelConfigName = "UnknownPropertyInMissileData"; // For logging
|
||
|
||
switch (typeKey)
|
||
{
|
||
case "millimeterwave": // Alias
|
||
case "millimeterwaveterminalguidance":
|
||
assignedConfig = data.MillimeterWaveGuidanceConfig;
|
||
componentConfig.SpecificConfig = data.MillimeterWaveGuidanceConfig;
|
||
topLevelConfigName = nameof(data.MillimeterWaveGuidanceConfig);
|
||
break;
|
||
case "infraredimagingterminalguidance":
|
||
assignedConfig = data.InfraredImagingGuidanceConfig;
|
||
componentConfig.SpecificConfig = data.InfraredImagingGuidanceConfig;
|
||
topLevelConfigName = nameof(data.InfraredImagingGuidanceConfig);
|
||
break;
|
||
case "lasersemiactiveguidance":
|
||
assignedConfig = data.LaserSemiActiveGuidanceConfig;
|
||
componentConfig.SpecificConfig = data.LaserSemiActiveGuidanceConfig;
|
||
topLevelConfigName = nameof(data.LaserSemiActiveGuidanceConfig);
|
||
break;
|
||
case "laserbeamriderguidance":
|
||
assignedConfig = data.LaserBeamRiderGuidanceConfig;
|
||
componentConfig.SpecificConfig = data.LaserBeamRiderGuidanceConfig;
|
||
topLevelConfigName = nameof(data.LaserBeamRiderGuidanceConfig);
|
||
break;
|
||
case "infraredcommandguidance":
|
||
assignedConfig = data.InfraredCommandGuidanceConfig;
|
||
componentConfig.SpecificConfig = data.InfraredCommandGuidanceConfig;
|
||
topLevelConfigName = nameof(data.InfraredCommandGuidanceConfig);
|
||
break;
|
||
default:
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Info: Component '{componentConfig.ComponentName}' (GuidanceSystemType: '{componentConfig.GuidanceSystemType}') has no explicit top-level config association logic in the current switch statement.");
|
||
break;
|
||
}
|
||
|
||
if (componentConfig.SpecificConfig != null) // Check if SpecificConfig was successfully assigned
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Successfully associated SpecificConfig for component '{componentConfig.ComponentName}' (Type: {componentConfig.GuidanceSystemType}) with data.{topLevelConfigName} (Actual Type: {componentConfig.SpecificConfig.GetType().FullName}).");
|
||
}
|
||
else // SpecificConfig is still null
|
||
{
|
||
if (assignedConfig == null &&
|
||
(typeKey == "millimeterwaveterminalguidance" || typeKey == "millimeterwave" ||
|
||
typeKey == "infraredimagingterminalguidance" || typeKey == "lasersemiactiveguidance" ||
|
||
typeKey == "laserbeamriderguidance" || typeKey == "infraredcommandguidance"))
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Error: SpecificConfig for component '{componentConfig.ComponentName}' (Type: {componentConfig.GuidanceSystemType}) remains NULL. The corresponding top-level config 'data.{topLevelConfigName}' was likely missing or null in the TOML file '{Path.GetFileName(file)}'.");
|
||
}
|
||
else
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Info: SpecificConfig for component '{componentConfig.ComponentName}' (Type: {componentConfig.GuidanceSystemType}) is still null after processing. This implies no matching top-level config was found/assigned or the type is unhandled by the switch for top-level association.");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else if (data.Properties?.GuidanceSuite != null && data.Properties.GuidanceSuite.Count == 0 && data.Type == ThreatSource.Missile.MissileType.CompositeGuidance)
|
||
{
|
||
Debug.WriteLine($"[ThreatSourceDataManager] Warning: Missile file {Path.GetFileName(file)} is of type CompositeGuidance but its GuidanceSuite is empty or null.");
|
||
}
|
||
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_missiles[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载导弹数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载指示器数据
|
||
/// </summary>
|
||
private void LoadIndicators(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"指示器数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<IndicatorData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_indicators[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载指示器数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载告警器数据
|
||
/// </summary>
|
||
private void LoadWarners(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"告警器数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<SensorData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_sensors[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载告警器数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载目标数据
|
||
/// </summary>
|
||
private void LoadTargets(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"目标数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<EquipmentData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_equipments[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载目标数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载天气数据
|
||
/// </summary>
|
||
/// <param name="path">天气数据目录路径</param>
|
||
private void LoadWeathers(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"天气数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<WeatherData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_weathers[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载天气数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 加载干扰机数据
|
||
/// </summary>
|
||
private void LoadJammers(string path)
|
||
{
|
||
if (!Directory.Exists(path))
|
||
{
|
||
Trace.TraceError($"干扰机数据目录不存在:{path}");
|
||
return;
|
||
}
|
||
|
||
foreach (var file in Directory.GetFiles(path, "*.toml", SearchOption.AllDirectories))
|
||
{
|
||
try
|
||
{
|
||
var tomlContent = File.ReadAllText(file);
|
||
var data = Toml.ToModel<JammerData>(tomlContent, options: _tomlOptions); // Pass options
|
||
if (data != null)
|
||
{
|
||
string model = Path.GetFileNameWithoutExtension(file);
|
||
_jammers[model] = data;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Trace.TraceError($"加载干扰机数据文件失败:{file},错误:{ex.Message}");
|
||
Trace.TraceError($"异常堆栈:{ex.StackTrace}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取导弹配置数据
|
||
/// </summary>
|
||
/// <param name="model">导弹型号</param>
|
||
/// <returns>导弹配置数据</returns>
|
||
public MissileData GetMissile(string model)
|
||
{
|
||
if (_missiles.TryGetValue(model, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"导弹 {model} 不存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取指示器配置数据
|
||
/// </summary>
|
||
/// <param name="model">指示器型号</param>
|
||
/// <returns>指示器配置数据</returns>
|
||
public IndicatorData GetIndicator(string model)
|
||
{
|
||
if (_indicators.TryGetValue(model, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"指示器 {model} 不存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取传感器配置数据
|
||
/// </summary>
|
||
/// <param name="model">传感器型号</param>
|
||
/// <returns>传感器配置数据</returns>
|
||
public SensorData GetSensor(string model)
|
||
{
|
||
if (_sensors.TryGetValue(model, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"传感器 {model} 不存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取目标配置数据
|
||
/// </summary>
|
||
/// <param name="model">目标型号</param>
|
||
/// <returns>目标配置数据</returns>
|
||
public EquipmentData GetEquipment(string model)
|
||
{
|
||
if (_equipments.TryGetValue(model, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"目标 {model} 不存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取天气配置数据
|
||
/// </summary>
|
||
/// <param name="weatherType">天气类型</param>
|
||
/// <returns>天气配置数据</returns>
|
||
public WeatherData GetWeather(string weatherType)
|
||
{
|
||
if (_weathers.TryGetValue(weatherType, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"天气 {weatherType} 不存在");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取干扰机配置数据
|
||
/// </summary>
|
||
/// <param name="model">干扰机型号</param>
|
||
/// <returns>干扰机配置数据</returns>
|
||
public JammerData GetJammer(string model)
|
||
{
|
||
if (_jammers.TryGetValue(model, out var data))
|
||
return data;
|
||
throw new KeyNotFoundException($"干扰机 {model} 不存在");
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的导弹ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableMissiles() => _missiles.Keys;
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的指示器ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableIndicators() => _indicators.Keys;
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的传感器ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableSensors() => _sensors.Keys;
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的目标ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableTargets() => _equipments.Keys;
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的天气ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableWeathers() => _weathers.Keys;
|
||
|
||
/// <summary>
|
||
/// 获取所有可用的干扰机ID列表
|
||
/// </summary>
|
||
public IEnumerable<string> GetAvailableJammers() => _jammers.Keys;
|
||
}
|
||
} |