Planner no longer writes local physics formulas. All kinematics/geometry/damage go through shared utility classes (Kinematics/RouteGeometry/CloudExpansionModel/DamageAssessment). Core: RouteGeometry (route geometry), PlannerConfig + planner_config.json (config externalization). Planner route-aware layout (offset along tangent), cloud overlap 20pct. DroneEntity arc-length driven. Fixes: PathInSphere cloud-frame correction (root cause), GaussianPuffDispersion hardcoded Sunny, ComputeEffectiveRadius cloud age, planner wind offset, remove drone wind bias. Tests 167 to 191, 41s. Windy scenarios pass.
72 lines
3.4 KiB
C#
72 lines
3.4 KiB
C#
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
|
||
{
|
||
/// <summary>防御规划器配置 — 全局策略参数,从 planner_config.json 加载。
|
||
/// 代码零默认值,所有字段必须从配置文件读取;文件缺失或字段缺失即抛异常。</summary>
|
||
public class PlannerConfig
|
||
{
|
||
/// <summary>云团重叠比例(0=相切,0.2=重叠 20%)。间距 = 2R × (1 − 重叠比例)。</summary>
|
||
public float CloudOverlapRatio { get; set; }
|
||
|
||
/// <summary>临界方案概率阈值(DeriveCritical 用)</summary>
|
||
public float CriticalProbabilityThreshold { get; set; }
|
||
|
||
/// <summary>拦截概率上限(封顶值)</summary>
|
||
public float MaxInterceptProbability { get; set; }
|
||
|
||
/// <summary>威胁类型系数表(TargetType → 系数)</summary>
|
||
public Dictionary<TargetType, float> TypeCoefficient { get; set; } = new();
|
||
|
||
/// <summary>弹药匹配表(PowerType → AerosolType)</summary>
|
||
public Dictionary<PowerType, AerosolType> AmmoMatch { get; set; } = new();
|
||
|
||
private const string ConfigFileName = "planner_config.json";
|
||
|
||
/// <summary>从 dataRoot 加载配置。文件缺失或字段非法即抛异常。</summary>
|
||
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<PlannerConfig>(json, JsonOptions)
|
||
?? throw new InvalidDataException($"planner 配置解析失败: {path}");
|
||
config.Validate();
|
||
return config;
|
||
}
|
||
|
||
/// <summary>从 IPathProvider 加载(便捷重载)</summary>
|
||
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 不能为空");
|
||
}
|
||
|
||
private static readonly JsonSerializerOptions JsonOptions = new()
|
||
{
|
||
Converters = { new JsonStringEnumConverter() },
|
||
PropertyNameCaseInsensitive = true,
|
||
};
|
||
}
|
||
}
|