♻️ refactor(compat): 替换 MoveJoint 时间律为解析式 7 阶平滑函数并添加离散限位校验
* 将预捕获 alpha 数据表替换为解析式 7 阶平滑点到点时间律 s(u)=35u⁴-84u⁵+70u⁶-20u⁷,形状系数按 1~3 阶导数最大值重算 * 新增离散限位校验:按真实 8ms 采样点反算速度/加速度/jerk, 不满足时自动拉长总时长后重采样,最多迭代 10000 次 * 实发轨迹落盘:ActualSendJointTraj.txt(角度制)、 ActualSendJerkStats.txt(点间跃度统计),按时间目录归档 * J519 AcceptsCommand 门控:只有机器人就绪时才发送下一帧, 减少无效下发;状态日志附带最近发送目标关节轴 * FanucControllerRuntime 构造函数改为必选 ILogger 注入, 确保 DI 解析时稳定拿到日志实例 * LegacyHttpApiController 移除已废弃的 ConnectServer 调用, EnableRobot 参数从 2 改为 4 * 新增跃度报警分析文档和六轴限值表,补充反馈远离拒绝测试 Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ using System.Buffers.Binary;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Flyshot.Runtime.Fanuc.Protocol;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flyshot.Core.Tests;
|
||||
|
||||
@@ -287,6 +288,31 @@ public sealed class FanucJ519ClientTests : IDisposable
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证状态变化日志会附带最近一次实际发送的目标关节轴,便于联调时对照控制目标。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task ReceiveLoop_LogsLastSentTargetJointsWhenStatusChanges()
|
||||
{
|
||||
var logger = new CapturingLogger<FanucJ519Client>();
|
||||
using var client = new FanucJ519Client(logger);
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var initResult = await _server.ReceiveAsync(_cts.Token);
|
||||
|
||||
client.UpdateCommand(new FanucJ519Command(sequence: 1, targetJoints: [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]));
|
||||
client.StartMotion();
|
||||
|
||||
await SendStatusPacketAsync(initResult.RemoteEndPoint, sequence: 42);
|
||||
_ = await _server.ReceiveAsync(_cts.Token);
|
||||
await Task.Delay(200, _cts.Token);
|
||||
|
||||
Assert.Contains(
|
||||
logger.Entries,
|
||||
entry => entry.Level == LogLevel.Information
|
||||
&& entry.Message.Contains("J519 最后一条发送目标关节轴", StringComparison.Ordinal)
|
||||
&& entry.Message.Contains("1.000, 2.000, 3.000, 4.000, 5.000, 6.000", StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向被测 J519 客户端发送一帧最小状态包,用机器人侧 status sequence 驱动下一帧命令。
|
||||
/// </summary>
|
||||
@@ -299,4 +325,68 @@ public sealed class FanucJ519ClientTests : IDisposable
|
||||
responsePacket[0x0c] = 15;
|
||||
await _server.SendAsync(responsePacket, clientEndpoint, _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 收集测试过程中的结构化日志,便于断言运行期输出内容。
|
||||
/// </summary>
|
||||
private sealed class CapturingLogger<T> : ILogger<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取已记录的日志条目。
|
||||
/// </summary>
|
||||
public List<LogEntry> Entries { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// 开始日志作用域;当前测试无需作用域,直接返回空对象。
|
||||
/// </summary>
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
where TState : notnull
|
||||
{
|
||||
return NullScope.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 指示所有日志级别均启用,便于测试完整捕获输出。
|
||||
/// </summary>
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 记录一条格式化后的日志消息。
|
||||
/// </summary>
|
||||
public void Log<TState>(
|
||||
LogLevel logLevel,
|
||||
EventId eventId,
|
||||
TState state,
|
||||
Exception? exception,
|
||||
Func<TState, Exception?, string> formatter)
|
||||
{
|
||||
Entries.Add(new LogEntry(logLevel, formatter(state, exception)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 表示一次日志记录。
|
||||
/// </summary>
|
||||
public sealed record LogEntry(LogLevel Level, string Message);
|
||||
|
||||
/// <summary>
|
||||
/// 提供空日志作用域,避免测试中额外分配。
|
||||
/// </summary>
|
||||
private sealed class NullScope : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取单例空作用域。
|
||||
/// </summary>
|
||||
public static NullScope Instance { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 释放空作用域;无需实际动作。
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user