* 扩展 ControllerClient 兼容层的执行参数和运行时编排 * 新增 /status 页面与 /api/status/snapshot 状态快照接口 * 补充 FANUC 协议、客户端和状态接口的最小验证测试 * 更新 README、兼容要求和真机 Socket 通信实现计划
196 lines
7.0 KiB
C#
196 lines
7.0 KiB
C#
using Flyshot.ControllerClientCompat;
|
|
using Flyshot.Core.Config;
|
|
using Flyshot.Core.Domain;
|
|
using Flyshot.Runtime.Fanuc;
|
|
|
|
namespace Flyshot.Core.Tests;
|
|
|
|
/// <summary>
|
|
/// 验证最小运行时编排链路会把规划结果交给控制器运行时,而不是停留在兼容层内存状态。
|
|
/// </summary>
|
|
public sealed class RuntimeOrchestrationTests
|
|
{
|
|
/// <summary>
|
|
/// 验证 FANUC 最小运行时执行轨迹后会更新状态快照与最终关节位置。
|
|
/// </summary>
|
|
[Fact]
|
|
public void FanucControllerRuntime_ExecuteTrajectory_UpdatesSnapshotAndFinalJointPositions()
|
|
{
|
|
var runtime = new FanucControllerRuntime();
|
|
var robot = TestRobotFactory.CreateRobotProfile();
|
|
runtime.ResetRobot(robot, "FANUC_LR_Mate_200iD");
|
|
runtime.SetActiveController(sim: true);
|
|
runtime.Connect("192.168.10.101");
|
|
runtime.EnableRobot(bufferSize: 2);
|
|
|
|
var result = new TrajectoryResult(
|
|
programName: "demo",
|
|
method: PlanningMethod.Icsp,
|
|
isValid: true,
|
|
duration: TimeSpan.FromSeconds(1.2),
|
|
shotEvents: Array.Empty<ShotEvent>(),
|
|
triggerTimeline: Array.Empty<TrajectoryDoEvent>(),
|
|
artifacts: Array.Empty<TrajectoryArtifact>(),
|
|
failureReason: null,
|
|
usedCache: false,
|
|
originalWaypointCount: 4,
|
|
plannedWaypointCount: 4);
|
|
|
|
runtime.ExecuteTrajectory(result, [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
|
|
|
|
var snapshot = runtime.GetSnapshot();
|
|
Assert.Equal("Connected", snapshot.ConnectionState);
|
|
Assert.False(snapshot.IsInMotion);
|
|
Assert.Equal([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], snapshot.JointPositions);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 验证普通轨迹会先进入 ICSP 规划,并沿用 ICSP 对示教点数量的约束。
|
|
/// </summary>
|
|
[Fact]
|
|
public void ControllerClientTrajectoryOrchestrator_PlanOrdinaryTrajectory_RejectsThreeTeachPoints()
|
|
{
|
|
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
|
var robot = TestRobotFactory.CreateRobotProfile();
|
|
|
|
void Act() =>
|
|
orchestrator.PlanOrdinaryTrajectory(
|
|
robot,
|
|
[
|
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[0.5, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[1.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
|
]);
|
|
|
|
Assert.Throws<ArgumentException>(Act);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 验证已上传飞拍轨迹会经过 self-adapt-icsp 并生成拍照触发时间轴。
|
|
/// </summary>
|
|
[Fact]
|
|
public void ControllerClientTrajectoryOrchestrator_PlanUploadedFlyshot_BuildsShotTimeline()
|
|
{
|
|
var orchestrator = new ControllerClientTrajectoryOrchestrator();
|
|
var robot = TestRobotFactory.CreateRobotProfile();
|
|
var uploaded = TestRobotFactory.CreateUploadedTrajectoryWithSingleShot();
|
|
|
|
var bundle = orchestrator.PlanUploadedFlyshot(robot, uploaded);
|
|
|
|
Assert.True(bundle.Result.IsValid);
|
|
Assert.Single(bundle.Result.ShotEvents);
|
|
Assert.Single(bundle.Result.TriggerTimeline);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 验证兼容服务执行普通轨迹时会进入规划链路,而不是直接把最后一个路点写入状态。
|
|
/// </summary>
|
|
[Fact]
|
|
public void ControllerClientCompatService_ExecuteTrajectory_RejectsThreeTeachPointsAfterPlanningIsIntroduced()
|
|
{
|
|
var service = TestRobotFactory.CreateCompatService();
|
|
service.SetUpRobot("FANUC_LR_Mate_200iD");
|
|
service.SetActiveController(sim: true);
|
|
service.Connect("192.168.10.101");
|
|
service.EnableRobot(2);
|
|
|
|
void Act() =>
|
|
service.ExecuteTrajectory(
|
|
[
|
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[0.5, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[1.0, 0.0, 0.0, 0.0, 0.0, 0.0]
|
|
]);
|
|
|
|
Assert.Throws<ArgumentException>(Act);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为运行时编排测试构造稳定的最小领域对象。
|
|
/// </summary>
|
|
internal static class TestRobotFactory
|
|
{
|
|
/// <summary>
|
|
/// 构造六轴测试机器人配置,避免运行时测试依赖真实 .robot 文件。
|
|
/// </summary>
|
|
/// <returns>可用于规划和运行时状态校验的机器人配置。</returns>
|
|
public static RobotProfile CreateRobotProfile()
|
|
{
|
|
return new RobotProfile(
|
|
name: "TestRobot",
|
|
modelPath: "Models/Test.robot",
|
|
degreesOfFreedom: 6,
|
|
jointLimits: Enumerable.Range(1, 6)
|
|
.Select(static index => new JointLimit($"J{index}", 10.0, 20.0, 100.0))
|
|
.ToArray(),
|
|
jointCouplings: Array.Empty<JointCoupling>(),
|
|
servoPeriod: TimeSpan.FromMilliseconds(8),
|
|
triggerPeriod: TimeSpan.FromMilliseconds(8));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 构造一条含单个拍照点的上传飞拍轨迹。
|
|
/// </summary>
|
|
/// <returns>可用于触发时间轴测试的上传轨迹。</returns>
|
|
public static ControllerClientCompatUploadedTrajectory CreateUploadedTrajectoryWithSingleShot()
|
|
{
|
|
return new ControllerClientCompatUploadedTrajectory(
|
|
name: "demo-flyshot",
|
|
waypoints:
|
|
[
|
|
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[0.1, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[0.2, 0.0, 0.0, 0.0, 0.0, 0.0],
|
|
[0.3, 0.0, 0.0, 0.0, 0.0, 0.0]
|
|
],
|
|
shotFlags: [false, true, false, false],
|
|
offsetValues: [0, 1, 0, 0],
|
|
addressGroups:
|
|
[
|
|
Array.Empty<int>(),
|
|
[7, 8],
|
|
Array.Empty<int>(),
|
|
Array.Empty<int>()
|
|
]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 构造一份真实依赖注入等价的兼容服务,覆盖运行时和编排器协作。
|
|
/// </summary>
|
|
/// <returns>可执行 ControllerClient 兼容语义的服务实例。</returns>
|
|
public static ControllerClientCompatService CreateCompatService()
|
|
{
|
|
var options = new ControllerClientCompatOptions
|
|
{
|
|
WorkspaceRoot = GetWorkspaceRoot()
|
|
};
|
|
|
|
return new ControllerClientCompatService(
|
|
options,
|
|
new ControllerClientCompatRobotCatalog(options, new RobotModelLoader()),
|
|
new FanucControllerRuntime(),
|
|
new ControllerClientTrajectoryOrchestrator());
|
|
}
|
|
|
|
/// <summary>
|
|
/// 定位父工作区根目录,供兼容服务加载真实机器人模型。
|
|
/// </summary>
|
|
/// <returns>父工作区根目录。</returns>
|
|
private static string GetWorkspaceRoot()
|
|
{
|
|
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.");
|
|
}
|
|
}
|