✨ feat(runtime): 添加轨迹持久化与密集执行链路
* 新增飞拍轨迹文件存储,支持上传、加载与删除 * 接通 ControllerClientCompat 到运行时的轨迹编排 * 完善 FANUC 命令与 J519 客户端发送链路 * 补充密集轨迹执行、运行时编排和协议客户端测试 * 更新 README 与 AGENTS 中的当前实现状态
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Runtime.Common;
|
||||
|
||||
@@ -14,8 +15,11 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
private readonly ControllerClientCompatRobotCatalog _robotCatalog;
|
||||
private readonly IControllerRuntime _runtime;
|
||||
private readonly ControllerClientTrajectoryOrchestrator _trajectoryOrchestrator;
|
||||
private readonly RobotConfigLoader _configLoader;
|
||||
private readonly IFlyshotTrajectoryStore _trajectoryStore;
|
||||
private RobotProfile? _activeRobotProfile;
|
||||
private string? _configuredRobotName;
|
||||
private CompatibilityRobotSettings? _robotSettings;
|
||||
private string? _connectedServerIp;
|
||||
private int _connectedServerPort;
|
||||
private bool _showTcp = true;
|
||||
@@ -29,16 +33,22 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
/// <param name="robotCatalog">机器人模型目录。</param>
|
||||
/// <param name="runtime">控制器运行时。</param>
|
||||
/// <param name="trajectoryOrchestrator">轨迹规划与触发编排器。</param>
|
||||
/// <param name="configLoader">旧版 RobotConfig.json 加载器。</param>
|
||||
/// <param name="trajectoryStore">已上传轨迹持久化存储。</param>
|
||||
public ControllerClientCompatService(
|
||||
ControllerClientCompatOptions options,
|
||||
ControllerClientCompatRobotCatalog robotCatalog,
|
||||
IControllerRuntime runtime,
|
||||
ControllerClientTrajectoryOrchestrator trajectoryOrchestrator)
|
||||
ControllerClientTrajectoryOrchestrator trajectoryOrchestrator,
|
||||
RobotConfigLoader configLoader,
|
||||
IFlyshotTrajectoryStore trajectoryStore)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_robotCatalog = robotCatalog ?? throw new ArgumentNullException(nameof(robotCatalog));
|
||||
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
||||
_trajectoryOrchestrator = trajectoryOrchestrator ?? throw new ArgumentNullException(nameof(trajectoryOrchestrator));
|
||||
_configLoader = configLoader ?? throw new ArgumentNullException(nameof(configLoader));
|
||||
_trajectoryStore = trajectoryStore ?? throw new ArgumentNullException(nameof(trajectoryStore));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -97,7 +107,11 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
/// <inheritdoc />
|
||||
public void SetUpRobot(string robotName)
|
||||
{
|
||||
var robotProfile = _robotCatalog.LoadProfile(robotName);
|
||||
var robotSettings = TryLoadRobotSettings() ?? CreateDefaultRobotSettings();
|
||||
var robotProfile = _robotCatalog.LoadProfile(
|
||||
robotName,
|
||||
robotSettings.AccLimitScale,
|
||||
robotSettings.JerkLimitScale);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
@@ -106,6 +120,14 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
_activeRobotProfile = robotProfile;
|
||||
_uploadedTrajectories.Clear();
|
||||
_runtime.ResetRobot(robotProfile, robotName);
|
||||
_robotSettings = robotSettings;
|
||||
|
||||
// 从持久化存储恢复该机器人名下之前已上传的轨迹。
|
||||
var savedTrajectories = _trajectoryStore.LoadAll(robotName, out _);
|
||||
foreach (var saved in savedTrajectories)
|
||||
{
|
||||
_uploadedTrajectories[saved.Key] = saved.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,6 +383,10 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
{
|
||||
EnsureRuntimeEnabled();
|
||||
_uploadedTrajectories[trajectory.Name] = trajectory;
|
||||
|
||||
var robotName = _configuredRobotName ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
var settings = _robotSettings ?? CreateDefaultRobotSettings();
|
||||
_trajectoryStore.Save(robotName, settings, trajectory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -398,7 +424,7 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
}
|
||||
|
||||
// 已上传飞拍轨迹必须按调用方指定 method 生成 shot timeline 后再交给运行时。
|
||||
var bundle = _trajectoryOrchestrator.PlanUploadedFlyshot(robot, trajectory, options);
|
||||
var bundle = _trajectoryOrchestrator.PlanUploadedFlyshot(robot, trajectory, options, RequireRobotSettings());
|
||||
if (options.MoveToStart)
|
||||
{
|
||||
_runtime.ExecuteTrajectory(CreateImmediateMoveResult(), bundle.PlannedTrajectory.PlannedWaypoints[0].Positions);
|
||||
@@ -425,11 +451,16 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new InvalidOperationException("FlyShot trajectory does not exist.");
|
||||
}
|
||||
|
||||
// 当前阶段没有落地文件导出,先通过 saveTrajectory=true 走规划校验,避免静默接受非法参数。
|
||||
// 先通过规划校验避免静默接受非法参数,同时把轨迹信息强制刷写到本地 JSON。
|
||||
_ = _trajectoryOrchestrator.PlanUploadedFlyshot(
|
||||
robot,
|
||||
trajectory,
|
||||
new FlyshotExecutionOptions(saveTrajectory: true, method: method));
|
||||
new FlyshotExecutionOptions(saveTrajectory: true, method: method),
|
||||
RequireRobotSettings());
|
||||
|
||||
var robotName = _configuredRobotName ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
var settings = _robotSettings ?? CreateDefaultRobotSettings();
|
||||
_trajectoryStore.Save(robotName, settings, trajectory);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,7 +483,8 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
var bundle = _trajectoryOrchestrator.PlanUploadedFlyshot(
|
||||
robot,
|
||||
trajectory,
|
||||
new FlyshotExecutionOptions(method: method, saveTrajectory: saveTrajectory));
|
||||
new FlyshotExecutionOptions(method: method, saveTrajectory: saveTrajectory),
|
||||
RequireRobotSettings());
|
||||
|
||||
duration = bundle.Result.Duration;
|
||||
return bundle.Result.IsValid;
|
||||
@@ -473,6 +505,9 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
{
|
||||
throw new InvalidOperationException("DeleteFlyShotTraj failed");
|
||||
}
|
||||
|
||||
var robotName = _configuredRobotName ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
_trajectoryStore.Delete(robotName, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,6 +538,15 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
return _activeRobotProfile ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前机器人兼容配置;未加载旧配置时回退到现场默认值。
|
||||
/// </summary>
|
||||
/// <returns>当前机器人配置。</returns>
|
||||
private CompatibilityRobotSettings RequireRobotSettings()
|
||||
{
|
||||
return _robotSettings ?? CreateDefaultRobotSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 校验机器人已经完成初始化。
|
||||
/// </summary>
|
||||
@@ -542,4 +586,61 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
originalWaypointCount: 1,
|
||||
plannedWaypointCount: 1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试从工作区加载旧版 RobotConfig.json 获取机器人配置;失败时返回 null。
|
||||
/// </summary>
|
||||
/// <returns>加载到的机器人配置,或 null。</returns>
|
||||
private CompatibilityRobotSettings? TryLoadRobotSettings()
|
||||
{
|
||||
try
|
||||
{
|
||||
var workspaceRoot = !string.IsNullOrWhiteSpace(_options.WorkspaceRoot)
|
||||
? Path.GetFullPath(_options.WorkspaceRoot)
|
||||
: ResolveWorkspaceRootFromBaseDirectory();
|
||||
|
||||
var configPath = PathCompatibility.ResolveConfigPath("RobotConfig.json", workspaceRoot);
|
||||
var loaded = _configLoader.Load(configPath, workspaceRoot);
|
||||
return loaded.Robot;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造与旧现场默认行为一致的机器人兼容配置。
|
||||
/// </summary>
|
||||
/// <returns>默认机器人配置。</returns>
|
||||
private static CompatibilityRobotSettings CreateDefaultRobotSettings()
|
||||
{
|
||||
return new CompatibilityRobotSettings(
|
||||
useDo: false,
|
||||
ioAddresses: Array.Empty<int>(),
|
||||
ioKeepCycles: 2,
|
||||
accLimitScale: 1.0,
|
||||
jerkLimitScale: 1.0,
|
||||
adaptIcspTryNum: 5);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从当前程序基目录向上搜索 FlyshotReplacement.sln 以推断工作区根目录。
|
||||
/// </summary>
|
||||
/// <returns>父工作区根目录。</returns>
|
||||
private static string ResolveWorkspaceRootFromBaseDirectory()
|
||||
{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user