✨ feat(server): 添加静态状态页与调试入口
- 将状态页、调试页改为 `wwwroot` 静态资源 - 补充调试配置接口与前端脚本 - 为兼容层、规划层和运行时补充日志 - 更新集成测试覆盖新入口
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
using Flyshot.Core.Config;
|
||||
using Flyshot.Core.Domain;
|
||||
using Flyshot.Runtime.Common;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.ControllerClientCompat;
|
||||
|
||||
@@ -17,6 +18,7 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
private readonly ControllerClientTrajectoryOrchestrator _trajectoryOrchestrator;
|
||||
private readonly RobotConfigLoader _configLoader;
|
||||
private readonly IFlyshotTrajectoryStore _trajectoryStore;
|
||||
private readonly ILogger<ControllerClientCompatService>? _logger;
|
||||
private RobotProfile? _activeRobotProfile;
|
||||
private string? _configuredRobotName;
|
||||
private CompatibilityRobotSettings? _robotSettings;
|
||||
@@ -35,13 +37,15 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
/// <param name="trajectoryOrchestrator">轨迹规划与触发编排器。</param>
|
||||
/// <param name="configLoader">旧版 RobotConfig.json 加载器。</param>
|
||||
/// <param name="trajectoryStore">已上传轨迹持久化存储。</param>
|
||||
/// <param name="logger">日志记录器;允许测试直接构造时传入 null。</param>
|
||||
public ControllerClientCompatService(
|
||||
ControllerClientCompatOptions options,
|
||||
ControllerClientCompatRobotCatalog robotCatalog,
|
||||
IControllerRuntime runtime,
|
||||
ControllerClientTrajectoryOrchestrator trajectoryOrchestrator,
|
||||
RobotConfigLoader configLoader,
|
||||
IFlyshotTrajectoryStore trajectoryStore)
|
||||
IFlyshotTrajectoryStore trajectoryStore,
|
||||
ILogger<ControllerClientCompatService>? logger = null)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_robotCatalog = robotCatalog ?? throw new ArgumentNullException(nameof(robotCatalog));
|
||||
@@ -49,6 +53,7 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
_trajectoryOrchestrator = trajectoryOrchestrator ?? throw new ArgumentNullException(nameof(trajectoryOrchestrator));
|
||||
_configLoader = configLoader ?? throw new ArgumentNullException(nameof(configLoader));
|
||||
_trajectoryStore = trajectoryStore ?? throw new ArgumentNullException(nameof(trajectoryStore));
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -90,6 +95,8 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
_connectedServerIp = serverIp;
|
||||
_connectedServerPort = port;
|
||||
}
|
||||
|
||||
_logger?.LogInformation("ConnectServer 完成: {ServerIp}:{Port}", serverIp, port);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -107,6 +114,8 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
/// <inheritdoc />
|
||||
public void SetUpRobot(string robotName)
|
||||
{
|
||||
_logger?.LogInformation("SetUpRobot 开始: robotName={RobotName}", robotName);
|
||||
|
||||
var robotSettings = TryLoadRobotSettings() ?? CreateDefaultRobotSettings();
|
||||
var robotProfile = _robotCatalog.LoadProfile(
|
||||
robotName,
|
||||
@@ -129,6 +138,14 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
_uploadedTrajectories[saved.Key] = saved.Value;
|
||||
}
|
||||
}
|
||||
|
||||
_logger?.LogInformation(
|
||||
"SetUpRobot 完成: robotName={RobotName}, dof={Dof}, accLimit={AccLimit}, jerkLimit={JerkLimit}, 恢复轨迹数={TrajCount}",
|
||||
robotName,
|
||||
robotProfile.DegreesOfFreedom,
|
||||
robotSettings.AccLimitScale,
|
||||
robotSettings.JerkLimitScale,
|
||||
_uploadedTrajectories.Count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -184,11 +201,15 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("控制器 IP 不能为空。", nameof(robotIp));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("Connect 开始: robotIp={RobotIp}", robotIp);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
EnsureRobotSetup();
|
||||
_runtime.Connect(robotIp);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("Connect 完成: robotIp={RobotIp}", robotIp);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -204,31 +225,37 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
/// <inheritdoc />
|
||||
public void EnableRobot(int bufferSize)
|
||||
{
|
||||
_logger?.LogInformation("EnableRobot 开始: bufferSize={BufferSize}", bufferSize);
|
||||
lock (_stateLock)
|
||||
{
|
||||
EnsureRobotSetup();
|
||||
_runtime.EnableRobot(bufferSize);
|
||||
}
|
||||
_logger?.LogInformation("EnableRobot 完成");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void DisableRobot()
|
||||
{
|
||||
_logger?.LogInformation("DisableRobot 开始");
|
||||
lock (_stateLock)
|
||||
{
|
||||
EnsureRobotSetup();
|
||||
_runtime.DisableRobot();
|
||||
}
|
||||
_logger?.LogInformation("DisableRobot 完成");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void StopMove()
|
||||
{
|
||||
_logger?.LogInformation("StopMove 开始");
|
||||
lock (_stateLock)
|
||||
{
|
||||
EnsureRobotSetup();
|
||||
_runtime.StopMove();
|
||||
}
|
||||
_logger?.LogInformation("StopMove 完成");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -335,6 +362,9 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(jointPositions);
|
||||
|
||||
_logger?.LogInformation("MoveJoint 开始: 目标关节数={JointCount}", jointPositions.Count);
|
||||
_logger?.LogDebug("MoveJoint 目标关节: {Joints}", string.Join(", ", jointPositions.Select(j => j.ToString("F4"))));
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
var robot = RequireActiveRobot();
|
||||
@@ -345,8 +375,15 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
|
||||
var speedRatio = _runtime.GetSnapshot().SpeedRatio;
|
||||
var moveResult = MoveJointTrajectoryGenerator.CreateResult(robot, currentJointPositions, jointPositions, speedRatio);
|
||||
_logger?.LogInformation(
|
||||
"MoveJoint 规划完成: 当前速度倍率={SpeedRatio}, 规划时长={Duration}s, 采样点数={SampleCount}",
|
||||
speedRatio,
|
||||
moveResult.Duration.TotalSeconds,
|
||||
moveResult.DenseJointTrajectory?.Count ?? 0);
|
||||
_runtime.ExecuteTrajectory(moveResult, jointPositions);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("MoveJoint 完成");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -359,6 +396,11 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("轨迹路点不能为空。", nameof(waypoints));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("ExecuteTrajectory 开始: 路点数={WaypointCount}, method={Method}, saveTraj={SaveTraj}",
|
||||
waypoints.Count, options.Method, options.SaveTrajectory);
|
||||
_logger?.LogDebug("ExecuteTrajectory 路点详情: {Waypoints}",
|
||||
string.Join(" | ", waypoints.Select(wp => $"[{string.Join(", ", wp.Select(j => j.ToString("F4")))}]")));
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
var robot = RequireActiveRobot();
|
||||
@@ -366,9 +408,17 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
|
||||
// 普通轨迹必须按调用方指定 method 规划,再把规划结果交给运行时执行。
|
||||
var bundle = _trajectoryOrchestrator.PlanOrdinaryTrajectory(robot, waypoints, options);
|
||||
_logger?.LogInformation(
|
||||
"ExecuteTrajectory 规划完成: method={Method}, 时长={Duration}s, 有效={IsValid}, 采样点数={SampleCount}",
|
||||
bundle.Result.Method,
|
||||
bundle.Result.Duration.TotalSeconds,
|
||||
bundle.Result.IsValid,
|
||||
bundle.Result.DenseJointTrajectory?.Count ?? 0);
|
||||
var finalJointPositions = bundle.PlannedTrajectory.PlannedWaypoints[^1].Positions;
|
||||
_runtime.ExecuteTrajectory(bundle.Result, finalJointPositions);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("ExecuteTrajectory 完成");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -386,6 +436,12 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(trajectory);
|
||||
|
||||
_logger?.LogInformation(
|
||||
"UploadTrajectory 开始: name={Name}, waypoints={WaypointCount}, shotFlags={ShotCount}",
|
||||
trajectory.Name,
|
||||
trajectory.Waypoints.Count,
|
||||
trajectory.ShotFlags.Count(static f => f));
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
EnsureRuntimeEnabled();
|
||||
@@ -395,6 +451,8 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
var settings = _robotSettings ?? CreateDefaultRobotSettings();
|
||||
_trajectoryStore.Save(robotName, settings, trajectory);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("UploadTrajectory 完成: name={Name}", trajectory.Name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -415,6 +473,10 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("轨迹名称不能为空。", nameof(name));
|
||||
}
|
||||
|
||||
_logger?.LogInformation(
|
||||
"ExecuteTrajectoryByName 开始: name={Name}, method={Method}, moveToStart={MoveToStart}, useCache={UseCache}",
|
||||
name, options.Method, options.MoveToStart, options.UseCache);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
var robot = RequireActiveRobot();
|
||||
@@ -422,24 +484,37 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
|
||||
if (!_uploadedTrajectories.TryGetValue(name, out var trajectory))
|
||||
{
|
||||
_logger?.LogWarning("ExecuteTrajectoryByName 失败: 轨迹不存在 name={Name}", name);
|
||||
throw new InvalidOperationException("FlyShot trajectory does not exist.");
|
||||
}
|
||||
|
||||
if (trajectory.Waypoints.Count == 0)
|
||||
{
|
||||
_logger?.LogWarning("ExecuteTrajectoryByName 失败: 轨迹无路点 name={Name}", name);
|
||||
throw new InvalidOperationException("FlyShot trajectory contains no waypoints.");
|
||||
}
|
||||
|
||||
// 已上传飞拍轨迹必须按调用方指定 method 生成 shot timeline 后再交给运行时。
|
||||
var bundle = _trajectoryOrchestrator.PlanUploadedFlyshot(robot, trajectory, options, RequireRobotSettings());
|
||||
_logger?.LogInformation(
|
||||
"ExecuteTrajectoryByName 规划完成: name={Name}, method={Method}, 时长={Duration}s, 触发事件数={TriggerCount}, 使用缓存={UsedCache}",
|
||||
name,
|
||||
bundle.Result.Method,
|
||||
bundle.Result.Duration.TotalSeconds,
|
||||
bundle.Result.TriggerTimeline.Count,
|
||||
bundle.Result.UsedCache);
|
||||
|
||||
if (options.MoveToStart)
|
||||
{
|
||||
_logger?.LogInformation("ExecuteTrajectoryByName 先移动到起点");
|
||||
_runtime.ExecuteTrajectory(CreateImmediateMoveResult(), bundle.PlannedTrajectory.PlannedWaypoints[0].Positions);
|
||||
}
|
||||
|
||||
var finalJointPositions = bundle.PlannedTrajectory.PlannedWaypoints[^1].Positions;
|
||||
_runtime.ExecuteTrajectory(bundle.Result, finalJointPositions);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("ExecuteTrajectoryByName 完成: name={Name}", name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -450,11 +525,14 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("轨迹名称不能为空。", nameof(name));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("SaveTrajectoryInfo 开始: name={Name}, method={Method}", name, method);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
var robot = RequireActiveRobot();
|
||||
if (!_uploadedTrajectories.TryGetValue(name, out var trajectory))
|
||||
{
|
||||
_logger?.LogWarning("SaveTrajectoryInfo 失败: 轨迹不存在 name={Name}", name);
|
||||
throw new InvalidOperationException("FlyShot trajectory does not exist.");
|
||||
}
|
||||
|
||||
@@ -469,6 +547,8 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
var settings = _robotSettings ?? CreateDefaultRobotSettings();
|
||||
_trajectoryStore.Save(robotName, settings, trajectory);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("SaveTrajectoryInfo 完成: name={Name}", name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -479,11 +559,14 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("轨迹名称不能为空。", nameof(name));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("IsFlyshotTrajectoryValid 开始: name={Name}, method={Method}", name, method);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
var robot = RequireActiveRobot();
|
||||
if (!_uploadedTrajectories.TryGetValue(name, out var trajectory))
|
||||
{
|
||||
_logger?.LogWarning("IsFlyshotTrajectoryValid 失败: 轨迹不存在 name={Name}", name);
|
||||
throw new InvalidOperationException("FlyShot trajectory does not exist.");
|
||||
}
|
||||
|
||||
@@ -494,6 +577,9 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
RequireRobotSettings());
|
||||
|
||||
duration = bundle.Result.Duration;
|
||||
_logger?.LogInformation(
|
||||
"IsFlyshotTrajectoryValid 结果: name={Name}, valid={Valid}, duration={Duration}s",
|
||||
name, bundle.Result.IsValid, duration.TotalSeconds);
|
||||
return bundle.Result.IsValid;
|
||||
}
|
||||
}
|
||||
@@ -506,16 +592,21 @@ public sealed class ControllerClientCompatService : IControllerClientCompatServi
|
||||
throw new ArgumentException("轨迹名称不能为空。", nameof(name));
|
||||
}
|
||||
|
||||
_logger?.LogInformation("DeleteTrajectory 开始: name={Name}", name);
|
||||
|
||||
lock (_stateLock)
|
||||
{
|
||||
if (!_uploadedTrajectories.Remove(name))
|
||||
{
|
||||
_logger?.LogWarning("DeleteTrajectory 失败: 轨迹不存在 name={Name}", name);
|
||||
throw new InvalidOperationException("DeleteFlyShotTraj failed");
|
||||
}
|
||||
|
||||
var robotName = _configuredRobotName ?? throw new InvalidOperationException("Robot has not been setup.");
|
||||
_trajectoryStore.Delete(robotName, name);
|
||||
}
|
||||
|
||||
_logger?.LogInformation("DeleteTrajectory 完成: name={Name}", name);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user