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

@@ -263,11 +263,11 @@ public sealed class RuntimeOrchestrationTests
[Fact]
public void ControllerClientCompatService_SetUpRobot_AppliesRobotConfigLimitScales()
{
var tempRoot = CreateTempWorkspaceRoot();
var configRoot = CreateTempConfigRoot();
try
{
File.WriteAllText(
Path.Combine(tempRoot, "RobotConfig.json"),
Path.Combine(configRoot, "RobotConfig.json"),
"""
{
"robot": {
@@ -282,7 +282,7 @@ public sealed class RuntimeOrchestrationTests
}
""");
var options = new ControllerClientCompatOptions { WorkspaceRoot = tempRoot };
var options = new ControllerClientCompatOptions { ConfigRoot = configRoot };
var runtime = new RecordingControllerRuntime();
var service = new ControllerClientCompatService(
options,
@@ -300,28 +300,27 @@ public sealed class RuntimeOrchestrationTests
}
finally
{
Directory.Delete(tempRoot, recursive: true);
Directory.Delete(configRoot, recursive: true);
}
}
/// <summary>
/// 创建只包含当前支持机器人模型和 RobotConfig.json 的临时工作区
/// 创建只包含当前支持机器人模型和 RobotConfig.json 的临时运行配置根
/// </summary>
private static string CreateTempWorkspaceRoot()
private static string CreateTempConfigRoot()
{
var tempRoot = Path.Combine(Path.GetTempPath(), "flyshot-runtime-tests", Guid.NewGuid().ToString("N"));
var modelDir = Path.Combine(tempRoot, "FlyingShot", "FlyingShot", "Models");
var configRoot = Path.Combine(Path.GetTempPath(), "flyshot-runtime-tests", Guid.NewGuid().ToString("N"), "Config");
var modelDir = Path.Combine(configRoot, "Models");
Directory.CreateDirectory(modelDir);
var sourceModel = Path.Combine(
TestRobotFactory.GetWorkspaceRoot(),
"FlyingShot",
"FlyingShot",
TestRobotFactory.GetReplacementRoot(),
"Config",
"Models",
"LR_Mate_200iD_7L.robot");
File.Copy(sourceModel, Path.Combine(modelDir, "LR_Mate_200iD_7L.robot"));
return tempRoot;
return configRoot;
}
}
@@ -382,7 +381,7 @@ internal static class TestRobotFactory
{
var options = new ControllerClientCompatOptions
{
WorkspaceRoot = GetWorkspaceRoot()
ConfigRoot = GetConfigRoot()
};
return new ControllerClientCompatService(
@@ -394,6 +393,35 @@ internal static class TestRobotFactory
new InMemoryFlyshotTrajectoryStore());
}
/// <summary>
/// 定位 replacement 仓库内的运行配置根目录。
/// </summary>
/// <returns>当前仓库 Config 目录。</returns>
public static string GetConfigRoot()
{
return Path.Combine(GetReplacementRoot(), "Config");
}
/// <summary>
/// 定位 replacement 仓库根目录,供测试读取仓库内固化配置。
/// </summary>
/// <returns>replacement 仓库根目录。</returns>
public static string GetReplacementRoot()
{
var current = new DirectoryInfo(AppContext.BaseDirectory);
while (current is not null)
{
if (File.Exists(Path.Combine(current.FullName, "FlyshotReplacement.sln")))
{
return current.FullName;
}
current = current.Parent;
}
throw new DirectoryNotFoundException("Unable to locate the flyshot replacement root.");
}
/// <summary>
/// 定位父工作区根目录,供兼容服务加载真实机器人模型。
/// </summary>