using System; using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; using CounterDrone.Core.Models; namespace CounterDrone.Core.Algorithms { /// 防御规划器配置 — 全局策略参数,从 planner_config.json 加载。 /// 代码零默认值,所有字段必须从配置文件读取;文件缺失或字段缺失即抛异常。 public class PlannerConfig { /// 云团重叠比例(0=相切,0.2=重叠 20%)。间距 = 2R × (1 − 重叠比例)。 public float CloudOverlapRatio { get; set; } /// 临界方案概率阈值(DeriveCritical 用) public float CriticalProbabilityThreshold { get; set; } /// 拦截概率上限(封顶值) public float MaxInterceptProbability { get; set; } /// 威胁类型系数表(TargetType → 系数) public Dictionary TypeCoefficient { get; set; } = new(); /// 弹药匹配表(PowerType → AerosolType) public Dictionary AmmoMatch { get; set; } = new(); /// 无探测设备时的默认探测精度 m(回退值,上帝视角但有标称误差) public float DefaultDetectionAccuracy { get; set; } /// 云团有效膨胀系数(0~1)。planner 取 Phase 2 膨胀时间的此比例作为有效云团年龄。默认 0.9 public float ExpansionFactor { get; set; } = 0.9f; /// 拦截窗口安全余量(s)。计算所需探测弧长时在膨胀时间基础上额外预留。默认 1 public float TimingSafetyMargin { get; set; } = 1f; private const string ConfigFileName = "planner_config.json"; /// 从 dataRoot 加载配置。文件缺失或字段非法即抛异常。 public static PlannerConfig Load(string dataRoot) { if (string.IsNullOrEmpty(dataRoot)) throw new ArgumentException("dataRoot 不能为空", nameof(dataRoot)); string path = Path.Combine(dataRoot, ConfigFileName); if (!File.Exists(path)) throw new FileNotFoundException($"planner 配置文件不存在: {path}"); string json = File.ReadAllText(path); var config = JsonSerializer.Deserialize(json, JsonOptions) ?? throw new InvalidDataException($"planner 配置解析失败: {path}"); config.Validate(); return config; } /// 从 IPathProvider 加载(便捷重载) public static PlannerConfig Load(IPathProvider paths) => Load(paths?.GetDataRoot() ?? throw new ArgumentNullException(nameof(paths))); private void Validate() { if (CloudOverlapRatio < 0f || CloudOverlapRatio >= 1f) throw new InvalidDataException($"CloudOverlapRatio 必须在 [0, 1),实际 {CloudOverlapRatio}"); if (CriticalProbabilityThreshold <= 0f || CriticalProbabilityThreshold >= 1f) throw new InvalidDataException($"CriticalProbabilityThreshold 必须在 (0, 1),实际 {CriticalProbabilityThreshold}"); if (MaxInterceptProbability <= 0f || MaxInterceptProbability > 1f) throw new InvalidDataException($"MaxInterceptProbability 必须在 (0, 1],实际 {MaxInterceptProbability}"); if (TypeCoefficient == null || TypeCoefficient.Count == 0) throw new InvalidDataException("TypeCoefficient 不能为空"); if (AmmoMatch == null || AmmoMatch.Count == 0) throw new InvalidDataException("AmmoMatch 不能为空"); if (DefaultDetectionAccuracy < 0f) throw new InvalidDataException($"DefaultDetectionAccuracy 必须 >= 0,实际 {DefaultDetectionAccuracy}"); if (ExpansionFactor <= 0f || ExpansionFactor > 1f) throw new InvalidDataException($"ExpansionFactor 必须在 (0, 1],实际 {ExpansionFactor}"); if (TimingSafetyMargin < 0f) throw new InvalidDataException($"TimingSafetyMargin 必须 >= 0,实际 {TimingSafetyMargin}"); } private static readonly JsonSerializerOptions JsonOptions = new() { Converters = { new JsonStringEnumConverter() }, PropertyNameCaseInsensitive = true, }; } }