✨ feat(planning): 添加离线飞拍规划能力
* 固化离线飞拍轨迹加载、保存和规划结果。 * 真机初始化失败时保留模型加载后的离线规划能力。 * 更新状态接口、调试页和回归测试覆盖。
This commit is contained in:
@@ -1,10 +1,147 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Runtime.Common;
|
||||
using Microsoft.AspNetCore.Mvc.Testing;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace Flyshot.Server.IntegrationTests;
|
||||
|
||||
public sealed class FlyshotServerFactory : WebApplicationFactory<Program>;
|
||||
/// <summary>
|
||||
/// 使用可控 fake runtime 启动测试宿主,避免集成测试访问真实 FANUC 控制器。
|
||||
/// </summary>
|
||||
public sealed class FlyshotServerFactory : WebApplicationFactory<Program>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void ConfigureWebHost(IWebHostBuilder builder)
|
||||
{
|
||||
builder.ConfigureServices(services =>
|
||||
{
|
||||
services.RemoveAll<IControllerRuntime>();
|
||||
services.AddSingleton<IControllerRuntime, IntegrationFakeControllerRuntime>();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为 HTTP 集成测试提供不访问硬件的控制器运行时。
|
||||
/// </summary>
|
||||
internal sealed class IntegrationFakeControllerRuntime : IControllerRuntime
|
||||
{
|
||||
private RobotProfile? _robot;
|
||||
private bool _connected;
|
||||
private bool _enabled;
|
||||
private double _speedRatio = 1.0;
|
||||
private double[] _tcp = [0.0, 0.0, 0.0];
|
||||
private double[] _joints = Array.Empty<double>();
|
||||
private double[] _pose = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0];
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetRobot(RobotProfile robot, string robotName)
|
||||
{
|
||||
_robot = robot;
|
||||
_connected = false;
|
||||
_enabled = false;
|
||||
_joints = new double[robot.DegreesOfFreedom];
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Connect(string robotIp)
|
||||
{
|
||||
_ = _robot ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
_connected = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Disconnect()
|
||||
{
|
||||
_connected = false;
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void EnableRobot(int bufferSize)
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
throw new InvalidOperationException("Controller has not been connected.");
|
||||
}
|
||||
|
||||
_enabled = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DisableRobot() => _enabled = false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void StopMove()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public double GetSpeedRatio() => _speedRatio;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetSpeedRatio(double ratio) => _speedRatio = ratio;
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<double> GetTcp() => _tcp.ToArray();
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetTcp(double x, double y, double z) => _tcp = [x, y, z];
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool GetIo(int port, string ioType) => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void SetIo(int port, bool value, string ioType)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<double> GetJointPositions() => _joints.ToArray();
|
||||
|
||||
/// <inheritdoc />
|
||||
public IReadOnlyList<double> GetPose() => _pose.ToArray();
|
||||
|
||||
/// <inheritdoc />
|
||||
public ControllerStateSnapshot GetSnapshot()
|
||||
{
|
||||
return new ControllerStateSnapshot(
|
||||
DateTimeOffset.UtcNow,
|
||||
_connected ? "Connected" : "Disconnected",
|
||||
_enabled,
|
||||
false,
|
||||
_speedRatio,
|
||||
_joints,
|
||||
_pose,
|
||||
Array.Empty<RuntimeAlarm>());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExecuteTrajectory(TrajectoryResult result, IReadOnlyList<double> finalJointPositions)
|
||||
{
|
||||
if (!_enabled)
|
||||
{
|
||||
throw new InvalidOperationException("Robot has not been enabled.");
|
||||
}
|
||||
|
||||
_joints = finalJointPositions.ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ExecuteCartesianTrajectory(TrajectoryResult result, IReadOnlyList<double> finalPose)
|
||||
{
|
||||
if (!_enabled)
|
||||
{
|
||||
throw new InvalidOperationException("Robot has not been enabled.");
|
||||
}
|
||||
|
||||
_pose = [.. finalPose, 1.0];
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class HealthEndpointTests(FlyshotServerFactory factory) : IClassFixture<FlyshotServerFactory>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user