配置运行时重载 IConfigService + 防御推荐 + 文档更新
新增: - IConfigService / ConfigService:读写 defaults.json / planner_config.json - SavePlannerConfig / SaveDefaults / Reload,无需重启 Unity - PlannerConfigReloaded 事件 → ScenarioManager 自动重建 planner - SimulationEngine.UpdatePlanner / ScenarioService.UpdatePlanner - SimulationRunner.ReloadPlanner 桥接 - DefenseRecommendation / RecommendOption 返回类型 - IScenarioService.GetDefenseRecommendation(配置阶段生成最佳抛撒参数) - ConfigServiceTests 5 个测试 + DefenseRecommendationTests 3 个测试 修复: - 云团间距半径基准不一致(间距用 R30、穿越用 R27 → 统一用 effectiveRadius) - 消除硬编码 30f(DefensePlanner + GaussianPuffDispersion 读 Phase2Duration) - TryGenerateFireEvents 复用 ComputeEffectiveRadius,消除重复计算 - 概率计算间距加重叠系数 - DatabaseManager 不再 DROP SimulationReport(保留历史报告) - 清理迁移代码(MetaEntry / 旧表 DROP / 版本检查) 文档: - 对接文档 V2.1:加 IConfigService API + 配置重载用法 + 修正 DLL 更新说明 - 实施跟踪 V1.8:8.2.1 更新 + 新增 8.2.3/8.2.4 + M14 里程碑 - 270 测试通过,Unity 编译通过
This commit is contained in:
parent
a3ceae5c14
commit
36ab4fad7b
@ -1,8 +1,8 @@
|
||||
# 实施计划与任务跟踪
|
||||
|
||||
> **项目**:反无人机仿真系统后端
|
||||
> **文档版本**:V1.7
|
||||
> **更新日期**:2026-06-20
|
||||
> **文档版本**:V1.8
|
||||
> **更新日期**:2026-06-22
|
||||
|
||||
---
|
||||
|
||||
@ -144,7 +144,7 @@ Phase 13 ✅ 模型重构:分层架构 + 代码审查 + 探测分离
|
||||
|
||||
| 指标 | 值 |
|
||||
|------|------|
|
||||
| 测试总数 | **262**(全部通过) |
|
||||
| 测试总数 | **270**(全部通过) |
|
||||
| 行覆盖率 | **95%+** |
|
||||
| 分支覆盖率 | **80%+** |
|
||||
| 执行时间 | ~15 秒 |
|
||||
@ -190,8 +190,10 @@ Phase 13 ✅ 模型重构:分层架构 + 代码审查 + 探测分离
|
||||
|
||||
| # | 功能 | 说明 |
|
||||
|---|------|------|
|
||||
| 8.2.1 | PDF 导出 | ✅ PdfSharpCore(netstandard2.0,纯托管 Unity 兼容)+ ReportData 结构化模型 + StandardPdfTemplate 内置标准模板 + CJK 字体嵌入。`IReportService.ExportReport(id, "pdf")` 按需导出,仿真后自动生成 MD 到报告目录 |
|
||||
| 8.2.1 | PDF 导出 | ✅ PdfSharpCore + ReportData 结构化模型 + StandardPdfTemplate + CJK 字体嵌入 + IConfigService 运行时重载 |
|
||||
| 8.2.2 | Word 导出 | ⬜ 调研可行方案 |
|
||||
| 8.2.3 | 防御推荐方案 | ✅ `IScenarioService.GetDefenseRecommendation`,配置阶段调 planner 生成最佳抛撒参数,前端"一键应用" |
|
||||
| 8.2.4 | 配置运行时重载 | ✅ `IConfigService` 读写 defaults.json / planner_config.json,SavePlannerConfig/SaveDefaults/Reload 无需重启 |
|
||||
|
||||
### 8.3 第三方对接
|
||||
|
||||
@ -453,6 +455,7 @@ T1(数据层)→ T2(算法层 IsInCoverage)
|
||||
| M11 — 运动学重构 + 实体暴露 + 基础数据 CRUD | ✅ |
|
||||
| M12 — 空基/地基统一规划 + 编队轴 + LaneDivider | ✅ |
|
||||
| M13 — PDF 导出 + 报告模板架构 | ✅ |
|
||||
| M14 — 防御推荐 GetDefenseRecommendation + 配置运行时重载 | ✅ |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# 后端对接文档(Unity 前端)
|
||||
|
||||
> **版本**:V2.0
|
||||
> **日期**:2026-06-20
|
||||
> **版本**:V2.1
|
||||
> **日期**:2026-06-22
|
||||
> **Unity 版本**:2022.3.62f3c1
|
||||
|
||||
---
|
||||
@ -60,7 +60,7 @@ dotnet publish -c Release -o ../../unity_plugins
|
||||
# 然后将 unity_plugins/*.dll 全部复制到 src/Unity/Assets/Plugins/CounterDrone.Core/
|
||||
```
|
||||
|
||||
> **重要**:更新 DLL 后首次运行 Unity 会重建数据库,**历史仿真报告会丢失**(基础数据和预设想定自动重新种子)。这是预期行为。
|
||||
> **重要**:更新 DLL 后首次运行 Unity 会建表+种子默认数据。**历史仿真报告不再丢失**(SimulationReport 表不再被 DROP)。
|
||||
|
||||
---
|
||||
|
||||
@ -272,6 +272,7 @@ cam.transform.LookAt(new Vector3(midX, 500, 0)); // 看向 500m 高度
|
||||
```csharp
|
||||
public IScenarioService Service { get; }
|
||||
public IDataService DataService { get; } // 基础数据 CRUD(7 类)
|
||||
public IConfigService Config { get; } // 配置读写 + 运行时重载
|
||||
|
||||
// Scenario CRUD
|
||||
public Scenario CreateScenario(string name, string number);
|
||||
@ -354,6 +355,47 @@ else
|
||||
{
|
||||
Debug.Log($"推荐失败: {rec.Summary}");
|
||||
}
|
||||
```
|
||||
|
||||
### IConfigService(通过 `ScenarioManager.Config` 访问)
|
||||
|
||||
配置文件读写 + 运行时重载,**无需重启 Unity**:
|
||||
|
||||
```csharp
|
||||
public interface IConfigService
|
||||
{
|
||||
string GetPlannerConfigJson(); // 读取 planner_config.json 原文
|
||||
void SavePlannerConfig(string json); // 保存并立即重载(planner 自动重建)
|
||||
string GetDefaultsJson(); // 读取 defaults.json 原文
|
||||
void SaveDefaults(string json); // 保存并重载种子数据到数据库
|
||||
void Reload(); // 从磁盘重新加载(前端直接改文件后通知后端刷新)
|
||||
}
|
||||
```
|
||||
|
||||
**用法 1:通过 API 修改配置**(推荐)
|
||||
|
||||
```csharp
|
||||
var cfg = GetComponent<ScenarioManager>().Config;
|
||||
|
||||
// 读当前 planner 配置
|
||||
string json = cfg.GetPlannerConfigJson();
|
||||
|
||||
// 修改后保存 — 自动重载,planner 立即用新参数
|
||||
cfg.SavePlannerConfig(modifiedJson);
|
||||
|
||||
// 修改 defaults.json — 自动重载,数据库 InsertOrReplace 覆盖同 Id 记录
|
||||
cfg.SaveDefaults(modifiedDefaultsJson);
|
||||
```
|
||||
|
||||
**用法 2:直接改文件后通知后端重载**
|
||||
|
||||
```csharp
|
||||
// 前端直接修改 persistentDataPath 下的 JSON 文件
|
||||
// 然后通知后端重新加载
|
||||
cfg.Reload(); // 重新读磁盘 → 重建 planner → 刷新种子数据
|
||||
```
|
||||
|
||||
> `SavePlannerConfig` 触发后,`ScenarioManager` 内部的 `DefensePlanner` 会自动重建。`SimulationRunner` 中的 planner 在下次 `LoadAndStart` 时自动用新配置。运行中的仿真不受影响(planner 在 `Initialize` 时调用,不在运行中调用)。
|
||||
|
||||
### IDataService(通过 `ScenarioManager.DataService` 访问)
|
||||
|
||||
@ -910,4 +952,4 @@ ScenarioConfig
|
||||
|------|------|
|
||||
| 架构设计 | `docs/design/architecture/总体架构设计.md` |
|
||||
| 实体事件映射 | `docs/design/technical/仿真器实体与事件映射.md` |
|
||||
| 测试状态 | **262 测试,15 秒(全部通过)** |
|
||||
| 测试状态 | **270 测试,14 秒(全部通过)** |
|
||||
|
||||
93
src/CounterDrone.Core/Services/ConfigService.cs
Normal file
93
src/CounterDrone.Core/Services/ConfigService.cs
Normal file
@ -0,0 +1,93 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using CounterDrone.Core.Algorithms;
|
||||
using CounterDrone.Core.Models;
|
||||
using SQLite;
|
||||
|
||||
namespace CounterDrone.Core.Services
|
||||
{
|
||||
/// <summary>配置服务实现 — 读写 JSON 配置文件,重载时触发事件通知消费方刷新</summary>
|
||||
public class ConfigService : IConfigService
|
||||
{
|
||||
private readonly IPathProvider _paths;
|
||||
private readonly SQLiteConnection _db;
|
||||
|
||||
/// <summary>planner 配置重载后触发(消费方应重建 DefensePlanner)</summary>
|
||||
public event Action<PlannerConfig>? PlannerConfigReloaded;
|
||||
|
||||
/// <summary>默认数据重载后触发(消费方应刷新缓存)</summary>
|
||||
public event Action<DefaultData>? DefaultsReloaded;
|
||||
|
||||
public ConfigService(IPathProvider paths, SQLiteConnection db)
|
||||
{
|
||||
_paths = paths;
|
||||
_db = db;
|
||||
}
|
||||
|
||||
public string GetPlannerConfigJson()
|
||||
{
|
||||
var path = Path.Combine(_paths.GetDataRoot(), "planner_config.json");
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException($"planner 配置文件不存在: {path}");
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public void SavePlannerConfig(string json)
|
||||
{
|
||||
var path = Path.Combine(_paths.GetDataRoot(), "planner_config.json");
|
||||
File.WriteAllText(path, json);
|
||||
ReloadPlannerConfig();
|
||||
}
|
||||
|
||||
public string GetDefaultsJson()
|
||||
{
|
||||
var path = Path.Combine(_paths.GetDataRoot(), "defaults.json");
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException($"默认数据文件不存在: {path}");
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
|
||||
public void SaveDefaults(string json)
|
||||
{
|
||||
var path = Path.Combine(_paths.GetDataRoot(), "defaults.json");
|
||||
File.WriteAllText(path, json);
|
||||
ReloadDefaults();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
ReloadPlannerConfig();
|
||||
ReloadDefaults();
|
||||
}
|
||||
|
||||
private void ReloadPlannerConfig()
|
||||
{
|
||||
var config = PlannerConfig.Load(_paths);
|
||||
PlannerConfigReloaded?.Invoke(config);
|
||||
}
|
||||
|
||||
private void ReloadDefaults()
|
||||
{
|
||||
var defaults = DefaultData.Load(_paths);
|
||||
ReseedDatabase(defaults);
|
||||
DefaultsReloaded?.Invoke(defaults);
|
||||
}
|
||||
|
||||
private void ReseedDatabase(DefaultData defaults)
|
||||
{
|
||||
foreach (var a in defaults.Ammunition) _db.InsertOrReplace(a);
|
||||
foreach (var f in defaults.FireUnits) _db.InsertOrReplace(f);
|
||||
foreach (var l in defaults.LaunchPlatforms) _db.InsertOrReplace(l);
|
||||
foreach (var d in defaults.Drones) _db.InsertOrReplace(d);
|
||||
foreach (var s in defaults.Sensors) _db.InsertOrReplace(s);
|
||||
foreach (var e in defaults.Environments) _db.InsertOrReplace(e);
|
||||
foreach (var f in defaults.Formations) _db.InsertOrReplace(f);
|
||||
foreach (var r in defaults.Routes)
|
||||
{
|
||||
r.WaypointsJson = JsonSerializer.Serialize(r.Waypoints);
|
||||
_db.InsertOrReplace(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/CounterDrone.Core/Services/IConfigService.cs
Normal file
23
src/CounterDrone.Core/Services/IConfigService.cs
Normal file
@ -0,0 +1,23 @@
|
||||
using CounterDrone.Core.Algorithms;
|
||||
|
||||
namespace CounterDrone.Core.Services
|
||||
{
|
||||
/// <summary>配置服务 — 读写 defaults.json / planner_config.json,支持运行时重载</summary>
|
||||
public interface IConfigService
|
||||
{
|
||||
/// <summary>读取 planner 配置 JSON 原文</summary>
|
||||
string GetPlannerConfigJson();
|
||||
|
||||
/// <summary>保存 planner 配置 JSON 并立即重载</summary>
|
||||
void SavePlannerConfig(string json);
|
||||
|
||||
/// <summary>读取 defaults.json 原文</summary>
|
||||
string GetDefaultsJson();
|
||||
|
||||
/// <summary>保存 defaults.json 并重载种子数据到数据库(InsertOrReplace 覆盖同 Id 记录)</summary>
|
||||
void SaveDefaults(string json);
|
||||
|
||||
/// <summary>重新从磁盘加载配置(前端直接改文件后调此方法通知后端刷新)</summary>
|
||||
void Reload();
|
||||
}
|
||||
}
|
||||
@ -60,7 +60,10 @@ namespace CounterDrone.Core.Services
|
||||
/// <summary>返回所有枚举定义(供前端下拉框使用)</summary>
|
||||
EnumMetadata GetEnums();
|
||||
|
||||
/// <summary>生成防御推荐方案(配置阶段调用,仿真前基于已配置参数计算最佳/最危险抛撒参数)</summary>
|
||||
/// <summary>生成防御推荐方案(配置阶段调用,仿真前基于已配置参数计算最佳抛撒参数)</summary>
|
||||
DefenseRecommendation GetDefenseRecommendation(string scenarioId);
|
||||
|
||||
/// <summary>替换 planner 实例(配置重载后由 ConfigService 触发)</summary>
|
||||
void UpdatePlanner(Algorithms.IDefensePlanner planner);
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ namespace CounterDrone.Core.Services
|
||||
private readonly RoutePlanRepository _routeRepo;
|
||||
private readonly WaypointRepository _waypointRepo;
|
||||
private readonly IPathProvider? _paths;
|
||||
private readonly IDefensePlanner? _planner;
|
||||
private IDefensePlanner? _planner;
|
||||
|
||||
public ScenarioService(
|
||||
ScenarioRepository scenarioRepo,
|
||||
@ -46,6 +46,9 @@ namespace CounterDrone.Core.Services
|
||||
_planner = planner;
|
||||
}
|
||||
|
||||
/// <summary>替换 planner 实例(配置重载后由 ConfigService 触发)</summary>
|
||||
public void UpdatePlanner(IDefensePlanner planner) => _planner = planner;
|
||||
|
||||
// ========== scenario CRUD ==========
|
||||
|
||||
public Scenario CreateScenario(string name, string ScenarioNumber)
|
||||
|
||||
@ -59,7 +59,7 @@ namespace CounterDrone.Core.Simulation
|
||||
private float _detectionTime;
|
||||
private readonly List<SimEvent> _allEvents = new();
|
||||
private string _scenarioId = string.Empty;
|
||||
private readonly IDefensePlanner _planner;
|
||||
private IDefensePlanner _planner;
|
||||
private int _entityCounter;
|
||||
|
||||
// ── GC 优化:预分配复用池 ──
|
||||
@ -79,6 +79,9 @@ namespace CounterDrone.Core.Simulation
|
||||
_planner = planner;
|
||||
}
|
||||
|
||||
/// <summary>替换 planner 实例(配置重载后调用)</summary>
|
||||
public void UpdatePlanner(IDefensePlanner planner) => _planner = planner;
|
||||
|
||||
public void SetFireSchedule(List<FireEvent> schedule)
|
||||
{
|
||||
_fireSchedule.Clear();
|
||||
|
||||
Binary file not shown.
@ -14,32 +14,45 @@ namespace CounterDrone.Unity
|
||||
private IScenarioService _service;
|
||||
private SQLite.SQLiteConnection _db;
|
||||
private IDataService _dataService;
|
||||
private ConfigService _configService;
|
||||
private IPathProvider _paths;
|
||||
|
||||
public IScenarioService Service => _service;
|
||||
public IDataService DataService => _dataService;
|
||||
public IConfigService Config => _configService;
|
||||
|
||||
public void Awake()
|
||||
{
|
||||
try
|
||||
{
|
||||
var paths = new UnityPathProvider();
|
||||
Debug.Log($"ScenarioManager.Awake: dbPath={paths.GetMainDbPath()}");
|
||||
_db = new DatabaseManager(paths).OpenMainDb();
|
||||
_paths = new UnityPathProvider();
|
||||
Debug.Log($"ScenarioManager.Awake: dbPath={_paths.GetMainDbPath()}");
|
||||
_db = new DatabaseManager(_paths).OpenMainDb();
|
||||
SqliteConnectionTracker.Track(_db);
|
||||
var planner = new DefensePlanner(
|
||||
DefaultData.Load(paths).Ammunition,
|
||||
PlannerConfig.Load(paths));
|
||||
DefaultData.Load(_paths).Ammunition,
|
||||
PlannerConfig.Load(_paths));
|
||||
_service = new ScenarioService(
|
||||
new ScenarioRepository(_db), new CombatSceneRepository(_db),
|
||||
new ControlZoneRepository(_db), new ScenarioDroneRepository(_db),
|
||||
new ScenarioUnitRepository(_db), new CloudDispersalRepository(_db),
|
||||
new RoutePlanRepository(_db), new WaypointRepository(_db),
|
||||
paths, planner);
|
||||
_paths, planner);
|
||||
_dataService = new DataService(
|
||||
new AmmunitionSpecRepository(_db), new FireUnitSpecRepository(_db),
|
||||
new DroneSpecRepository(_db), new SensorSpecRepository(_db),
|
||||
new EnvironmentSpecRepository(_db), new FormationTemplateRepository(_db),
|
||||
new RouteTemplateRepository(_db));
|
||||
|
||||
// 配置服务:订阅重载事件,自动重建 planner
|
||||
_configService = new ConfigService(_paths, _db);
|
||||
_configService.PlannerConfigReloaded += cfg =>
|
||||
{
|
||||
var newPlanner = new DefensePlanner(DefaultData.Load(_paths).Ammunition, cfg);
|
||||
_service.UpdatePlanner(newPlanner);
|
||||
Debug.Log($"ScenarioManager: planner 配置已重载");
|
||||
};
|
||||
|
||||
Debug.Log("ScenarioManager.Awake: OK");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
|
||||
@ -25,6 +25,7 @@ namespace CounterDrone.Unity
|
||||
private IPathProvider _paths;
|
||||
private string _scenarioId;
|
||||
private SQLite.SQLiteConnection _db;
|
||||
private IDefensePlanner _planner;
|
||||
|
||||
private readonly Dictionary<string, GameObject> _entityVisuals = new();
|
||||
private readonly Dictionary<string, GameObject> _clouds = new();
|
||||
@ -49,7 +50,16 @@ namespace CounterDrone.Unity
|
||||
new RoutePlanRepository(_db), new WaypointRepository(_db));
|
||||
|
||||
_frameStore = new FrameDataStore(_paths);
|
||||
_engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, new DefensePlanner(DefaultData.Load(_paths).Ammunition, PlannerConfig.Load(_paths)));
|
||||
_planner = new DefensePlanner(DefaultData.Load(_paths).Ammunition, PlannerConfig.Load(_paths));
|
||||
_engine = new SimulationEngine(_scenario, _frameStore, new DamageModelRouter(), _paths, _planner);
|
||||
}
|
||||
|
||||
/// <summary>配置重载后重建 planner(由 ConfigService 触发)</summary>
|
||||
public void ReloadPlanner()
|
||||
{
|
||||
_planner = new DefensePlanner(DefaultData.Load(_paths).Ammunition, PlannerConfig.Load(_paths));
|
||||
if (_engine != null)
|
||||
_engine.UpdatePlanner(_planner);
|
||||
}
|
||||
|
||||
public void LoadAndStart(string scenarioId)
|
||||
|
||||
118
test/unit/CounterDrone.Core.Tests/ConfigServiceTests.cs
Normal file
118
test/unit/CounterDrone.Core.Tests/ConfigServiceTests.cs
Normal file
@ -0,0 +1,118 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using CounterDrone.Core;
|
||||
using CounterDrone.Core.Algorithms;
|
||||
using CounterDrone.Core.Services;
|
||||
using Xunit;
|
||||
|
||||
namespace CounterDrone.Core.Tests
|
||||
{
|
||||
public class ConfigServiceTests : IDisposable
|
||||
{
|
||||
private readonly string _testDir;
|
||||
private readonly IPathProvider _paths;
|
||||
private readonly ConfigService _config;
|
||||
private readonly SQLite.SQLiteConnection _db;
|
||||
|
||||
public ConfigServiceTests()
|
||||
{
|
||||
_testDir = Path.Combine(Path.GetTempPath(), $"cd_cfg_{Guid.NewGuid():N}");
|
||||
_paths = new TestPathProvider(_testDir);
|
||||
var dbm = new DatabaseManager(_paths);
|
||||
_db = dbm.OpenMainDb();
|
||||
_config = new ConfigService(_paths, _db);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_db?.Close();
|
||||
if (Directory.Exists(_testDir)) Directory.Delete(_testDir, true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetPlannerConfigJson_ReturnsValidJson()
|
||||
{
|
||||
var json = _config.GetPlannerConfigJson();
|
||||
var cfg = JsonSerializer.Deserialize<PlannerConfig>(json, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
PropertyNameCaseInsensitive = true,
|
||||
});
|
||||
Assert.NotNull(cfg);
|
||||
Assert.True(cfg.CloudOverlapRatio >= 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SavePlannerConfig_PersistsAndReloads()
|
||||
{
|
||||
var original = _config.GetPlannerConfigJson();
|
||||
var cfg = JsonSerializer.Deserialize<PlannerConfig>(original, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
PropertyNameCaseInsensitive = true,
|
||||
})!;
|
||||
cfg.CloudOverlapRatio = 0.3f;
|
||||
var modifiedJson = JsonSerializer.Serialize(cfg, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
});
|
||||
|
||||
_config.SavePlannerConfig(modifiedJson);
|
||||
|
||||
var reloaded = _config.GetPlannerConfigJson();
|
||||
var reloadedCfg = JsonSerializer.Deserialize<PlannerConfig>(reloaded, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
PropertyNameCaseInsensitive = true,
|
||||
})!;
|
||||
Assert.Equal(0.3f, reloadedCfg.CloudOverlapRatio);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void PlannerConfigReloaded_EventFires()
|
||||
{
|
||||
PlannerConfig? received = null;
|
||||
_config.PlannerConfigReloaded += cfg => received = cfg;
|
||||
|
||||
var json = _config.GetPlannerConfigJson();
|
||||
_config.SavePlannerConfig(json);
|
||||
|
||||
Assert.NotNull(received);
|
||||
Assert.True(received.CloudOverlapRatio >= 0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reload_FromDiskRefreshesConfig()
|
||||
{
|
||||
// 直接改文件
|
||||
var path = Path.Combine(_testDir, "planner_config.json");
|
||||
var json = File.ReadAllText(path);
|
||||
var cfg = JsonSerializer.Deserialize<PlannerConfig>(json, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
PropertyNameCaseInsensitive = true,
|
||||
})!;
|
||||
cfg.ExpansionFactor = 0.8f;
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(cfg, new JsonSerializerOptions
|
||||
{
|
||||
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
|
||||
}));
|
||||
|
||||
PlannerConfig? received = null;
|
||||
_config.PlannerConfigReloaded += cfg => received = cfg;
|
||||
|
||||
_config.Reload();
|
||||
|
||||
Assert.NotNull(received);
|
||||
Assert.Equal(0.8f, received.ExpansionFactor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GetDefaultsJson_ReturnsValidJson()
|
||||
{
|
||||
var json = _config.GetDefaultsJson();
|
||||
Assert.Contains("ammunition", json.ToLower());
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Loading…
Reference in New Issue
Block a user