feat(*): 添加 ConfigRoot 运行时配置目录隔离

* 新增 ControllerClientCompatOptions.ConfigRoot 及解析方法
* 兼容层默认从运行目录 Config 加载模型、轨迹和配置
* 移除隐式父工作区根目录推断,旧路径仅在显式配置时生效
* Host 项目编译时将 Config 目录复制到输出目录
* 请求响应日志中间件忽略 /api/status/snapshot 高频轮询
* 补充 ConfigRoot 和日志过滤相关单元测试
This commit is contained in:
2026-04-29 18:27:03 +08:00
parent c38faddbf0
commit a6579f1e5b
16 changed files with 451 additions and 143 deletions

View File

@@ -36,7 +36,7 @@ public interface IFlyshotTrajectoryStore
}
/// <summary>
/// 使用与旧版 RobotConfig.json 一致的 JSON 格式持久化飞拍轨迹和机器人配置。
/// 使用与旧版 RobotConfig.json 一致的 JSON 格式在运行目录 Config 中持久化飞拍轨迹和机器人配置。
/// </summary>
public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
{
@@ -47,7 +47,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
/// <summary>
/// 初始化基于 JSON 文件的轨迹存储。
/// </summary>
/// <param name="options">兼容层基础配置,用于定位工作区根目录。</param>
/// <param name="options">兼容层基础配置,用于定位运行配置根目录。</param>
/// <param name="configLoader">旧版 RobotConfig.json 加载器,用于反序列化已保存的轨迹。</param>
/// <param name="logger">日志记录器;允许 null。</param>
public JsonFlyshotTrajectoryStore(ControllerClientCompatOptions options, RobotConfigLoader configLoader, ILogger<JsonFlyshotTrajectoryStore>? logger = null)
@@ -166,8 +166,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
{
_logger?.LogInformation("TrajectoryStore 正在加载: {Path}", path);
var workspaceRoot = ResolveWorkspaceRoot();
var loaded = _configLoader.Load(path, workspaceRoot);
var loaded = _configLoader.Load(path, _options.ResolveConfigRoot());
settings = loaded.Robot;
var dict = new Dictionary<string, ControllerClientCompatUploadedTrajectory>(StringComparer.Ordinal);
@@ -234,32 +233,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
/// </summary>
private string ResolveStorePath(string robotName)
{
var workspaceRoot = ResolveWorkspaceRoot();
var storeDir = Path.Combine(workspaceRoot, "flyshot-replacement", "TrajectoryStore");
var storeDir = Path.Combine(_options.ResolveConfigRoot(), "TrajectoryStore");
return Path.Combine(storeDir, $"{robotName}_trajectories.json");
}
/// <summary>
/// 解析父工作区根目录,优先使用显式配置。
/// </summary>
private string ResolveWorkspaceRoot()
{
if (!string.IsNullOrWhiteSpace(_options.WorkspaceRoot))
{
return Path.GetFullPath(_options.WorkspaceRoot);
}
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current is not null)
{
if (File.Exists(Path.Combine(current.FullName, "FlyshotReplacement.sln")))
{
return Path.GetFullPath(Path.Combine(current.FullName, ".."));
}
current = current.Parent;
}
throw new DirectoryNotFoundException("Unable to locate the flyshot workspace root.");
}
}