CounterDroneBackend/src/CounterDrone.Core/Algorithms/PlannerConfig.cs
tian 8515f7821a feat(v0.5.0): weather into planner + unified physics model
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.
2026-06-14 14:49:53 +08:00

72 lines
3.4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
};
}
}