✨ feat(*): 添加轨迹产物导出与规划速度倍率隔离
* 新增 FlyshotTrajectoryArtifactWriter,支持 saveTrajectory 将规划结果导出到 Config/Data/name(JointTraj、CartTraj、 ShotEvents 等) * RobotConfig 新增 PlanningSpeedScale,区分规划阶段限速倍率 与运行时 J519 下发倍率 * 轨迹缓存键纳入 planningSpeedScale,避免降速规划误用缓存 * 完善 FanucCommandClient 命令参数日志与状态通道重连 * 补充 RuntimeOrchestrationTests 覆盖产物导出与倍率隔离 * 更新 README 进度文档
This commit is contained in:
@@ -7,38 +7,9 @@ using Microsoft.Extensions.Logging;
|
||||
namespace Flyshot.ControllerClientCompat;
|
||||
|
||||
/// <summary>
|
||||
/// 定义已上传飞拍轨迹的持久化存储契约。
|
||||
/// 使用运行目录 Config/RobotConfig.json 持久化单机器人飞拍轨迹和机器人配置。
|
||||
/// </summary>
|
||||
public interface IFlyshotTrajectoryStore
|
||||
{
|
||||
/// <summary>
|
||||
/// 将单条轨迹持久化到本地 JSON,同时更新所属机器人配置段。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称。</param>
|
||||
/// <param name="settings">当前机器人级兼容配置。</param>
|
||||
/// <param name="trajectory">要保存的已上传轨迹。</param>
|
||||
void Save(string robotName, CompatibilityRobotSettings settings, ControllerClientCompatUploadedTrajectory trajectory);
|
||||
|
||||
/// <summary>
|
||||
/// 从本地 JSON 删除指定名称的轨迹。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称。</param>
|
||||
/// <param name="trajectoryName">要删除的轨迹名称。</param>
|
||||
void Delete(string robotName, string trajectoryName);
|
||||
|
||||
/// <summary>
|
||||
/// 加载指定机器人名下所有已持久化的轨迹,并回传保存时的机器人配置。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称。</param>
|
||||
/// <param name="settings">输出保存时的机器人配置;若文件不存在或解析失败则为 null。</param>
|
||||
/// <returns>按轨迹名称索引的已上传轨迹集合。</returns>
|
||||
IReadOnlyDictionary<string, ControllerClientCompatUploadedTrajectory> LoadAll(string robotName, out CompatibilityRobotSettings? settings);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用与旧版 RobotConfig.json 一致的 JSON 格式在运行目录 Config 中持久化飞拍轨迹和机器人配置。
|
||||
/// </summary>
|
||||
public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
public sealed class JsonFlyshotTrajectoryStore
|
||||
{
|
||||
private readonly ControllerClientCompatOptions _options;
|
||||
private readonly RobotConfigLoader _configLoader;
|
||||
@@ -57,19 +28,24 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// 将单条轨迹持久化到统一 RobotConfig.json,同时更新机器人配置段。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称,仅用于日志诊断。</param>
|
||||
/// <param name="settings">当前机器人级兼容配置。</param>
|
||||
/// <param name="trajectory">要保存的已上传轨迹。</param>
|
||||
public void Save(string robotName, CompatibilityRobotSettings settings, ControllerClientCompatUploadedTrajectory trajectory)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(settings);
|
||||
ArgumentNullException.ThrowIfNull(trajectory);
|
||||
|
||||
_logger?.LogInformation(
|
||||
"TrajectoryStore 保存轨迹: robot={RobotName}, name={TrajectoryName}, waypoints={WaypointCount}",
|
||||
"RobotConfig 保存轨迹: robot={RobotName}, name={TrajectoryName}, waypoints={WaypointCount}",
|
||||
robotName,
|
||||
trajectory.Name,
|
||||
trajectory.Waypoints.Count);
|
||||
|
||||
var path = ResolveStorePath(robotName);
|
||||
var path = ResolveStorePath();
|
||||
var directory = Path.GetDirectoryName(path)!;
|
||||
Directory.CreateDirectory(directory);
|
||||
|
||||
@@ -103,10 +79,14 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
};
|
||||
File.WriteAllText(path, root.ToJsonString(writeOptions));
|
||||
|
||||
_logger?.LogInformation("TrajectoryStore 轨迹已保存到 {Path}", path);
|
||||
_logger?.LogInformation("RobotConfig 轨迹已保存到 {Path}", path);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// 从统一 RobotConfig.json 删除指定名称的轨迹。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称,仅用于日志诊断。</param>
|
||||
/// <param name="trajectoryName">要删除的轨迹名称。</param>
|
||||
public void Delete(string robotName, string trajectoryName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(trajectoryName))
|
||||
@@ -114,12 +94,12 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
throw new ArgumentException("轨迹名称不能为空。", nameof(trajectoryName));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("TrajectoryStore 删除轨迹: robot={RobotName}, name={TrajectoryName}", robotName, trajectoryName);
|
||||
_logger?.LogInformation("RobotConfig 删除轨迹: robot={RobotName}, name={TrajectoryName}", robotName, trajectoryName);
|
||||
|
||||
var path = ResolveStorePath(robotName);
|
||||
var path = ResolveStorePath();
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
_logger?.LogWarning("TrajectoryStore 删除失败: 文件不存在 {Path}", path);
|
||||
_logger?.LogWarning("RobotConfig 删除失败: 文件不存在 {Path}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -127,7 +107,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
var root = JsonNode.Parse(existingJson)?.AsObject();
|
||||
if (root is null)
|
||||
{
|
||||
_logger?.LogWarning("TrajectoryStore 删除失败: 无法解析 JSON {Path}", path);
|
||||
_logger?.LogWarning("RobotConfig 删除失败: 无法解析 JSON {Path}", path);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,29 +122,34 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
|
||||
};
|
||||
File.WriteAllText(path, root.ToJsonString(writeOptions));
|
||||
_logger?.LogInformation("TrajectoryStore 轨迹已删除: {TrajectoryName}", trajectoryName);
|
||||
_logger?.LogInformation("RobotConfig 轨迹已删除: {TrajectoryName}", trajectoryName);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning("TrajectoryStore 删除失败: 轨迹不存在 {TrajectoryName}", trajectoryName);
|
||||
_logger?.LogWarning("RobotConfig 删除失败: 轨迹不存在 {TrajectoryName}", trajectoryName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// 加载统一 RobotConfig.json 中的所有轨迹,并回传机器人配置。
|
||||
/// </summary>
|
||||
/// <param name="robotName">当前已初始化的机器人名称,仅用于日志诊断。</param>
|
||||
/// <param name="settings">输出 RobotConfig.json 中的机器人配置;若文件不存在或解析失败则为 null。</param>
|
||||
/// <returns>按轨迹名称索引的已上传轨迹集合。</returns>
|
||||
public IReadOnlyDictionary<string, ControllerClientCompatUploadedTrajectory> LoadAll(string robotName, out CompatibilityRobotSettings? settings)
|
||||
{
|
||||
var path = ResolveStorePath(robotName);
|
||||
var path = ResolveStorePath();
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
_logger?.LogInformation("TrajectoryStore 无持久化数据: {Path}", path);
|
||||
_logger?.LogInformation("RobotConfig 无持久化数据: {Path}", path);
|
||||
settings = null;
|
||||
return new Dictionary<string, ControllerClientCompatUploadedTrajectory>(StringComparer.Ordinal);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger?.LogInformation("TrajectoryStore 正在加载: {Path}", path);
|
||||
_logger?.LogInformation("RobotConfig 正在加载: {Path}", path);
|
||||
|
||||
var loaded = _configLoader.Load(path, _options.ResolveConfigRoot());
|
||||
settings = loaded.Robot;
|
||||
@@ -182,7 +167,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
}
|
||||
|
||||
_logger?.LogInformation(
|
||||
"TrajectoryStore 加载完成: robot={RobotName}, 轨迹数={Count}, useDo={UseDo}, ioKeepCycles={IoKeepCycles}",
|
||||
"RobotConfig 加载完成: robot={RobotName}, 轨迹数={Count}, useDo={UseDo}, ioKeepCycles={IoKeepCycles}",
|
||||
robotName,
|
||||
dict.Count,
|
||||
settings?.UseDo,
|
||||
@@ -192,7 +177,7 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "TrajectoryStore 加载失败: {Path}", path);
|
||||
_logger?.LogError(ex, "RobotConfig 加载失败: {Path}", path);
|
||||
settings = null;
|
||||
return new Dictionary<string, ControllerClientCompatUploadedTrajectory>(StringComparer.Ordinal);
|
||||
}
|
||||
@@ -229,11 +214,10 @@ public sealed class JsonFlyshotTrajectoryStore : IFlyshotTrajectoryStore
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 解析当前机器人对应的持久化文件路径。
|
||||
/// 解析单程序单机器人的统一配置文件路径。
|
||||
/// </summary>
|
||||
private string ResolveStorePath(string robotName)
|
||||
private string ResolveStorePath()
|
||||
{
|
||||
var storeDir = Path.Combine(_options.ResolveConfigRoot(), "TrajectoryStore");
|
||||
return Path.Combine(storeDir, $"{robotName}_trajectories.json");
|
||||
return Path.Combine(_options.ResolveConfigRoot(), "RobotConfig.json");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user