🐛 fix(fanuc): 修复 J519 队列等待与日志注入

* 为 FANUC 命令、状态和 J519 客户端注入独立日志记录器
* 补充不可接收命令状态下队列完成等待的回归测试
* 调整 J519 命令流结束后的状态处理语义
This commit is contained in:
2026-06-30 16:05:23 +08:00
parent 9a01ce851d
commit e0162b72b3
4 changed files with 101 additions and 21 deletions

View File

@@ -1,5 +1,6 @@
using Flyshot.Runtime.Common;
using Flyshot.Runtime.Fanuc;
using Flyshot.Runtime.Fanuc.Protocol;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
@@ -14,7 +15,7 @@ public sealed class FanucControllerRuntimeLoggingRegistrationTests
/// 验证宿主按 IControllerRuntime 解析 FANUC 运行时时,不会因为可选日志参数而退回到无日志实例。
/// </summary>
[Fact]
public void ServiceProvider_Resolves_FanucControllerRuntime_WithLogger()
public void ServiceProvider_Resolves_FanucControllerRuntime_WithAllClientLoggers()
{
var services = new ServiceCollection();
services.AddLogging();
@@ -28,5 +29,27 @@ public sealed class FanucControllerRuntimeLoggingRegistrationTests
Assert.NotNull(loggerField);
Assert.NotNull(loggerField!.GetValue(concreteRuntime));
AssertClientLoggerIsInjected<FanucCommandClient>(concreteRuntime, "_commandClient");
AssertClientLoggerIsInjected<FanucStateClient>(concreteRuntime, "_stateClient");
AssertClientLoggerIsInjected<FanucJ519Client>(concreteRuntime, "_j519Client");
}
/// <summary>
/// 断言运行时持有的底层协议客户端已注入对应类型的日志记录器。
/// </summary>
/// <typeparam name="TClient">待检查的协议客户端类型。</typeparam>
/// <param name="runtime">已经通过依赖注入解析出的 FANUC 运行时。</param>
/// <param name="clientFieldName">运行时内部保存该客户端实例的字段名。</param>
private static void AssertClientLoggerIsInjected<TClient>(FanucControllerRuntime runtime, string clientFieldName)
{
var clientField = typeof(FanucControllerRuntime).GetField(clientFieldName, BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(clientField);
var client = Assert.IsType<TClient>(clientField!.GetValue(runtime));
var loggerField = typeof(TClient).GetField("_logger", BindingFlags.Instance | BindingFlags.NonPublic);
Assert.NotNull(loggerField);
Assert.NotNull(loggerField!.GetValue(client));
}
}

View File

@@ -279,6 +279,34 @@ public sealed class FanucJ519ClientTests : IDisposable
Assert.True(client.IsCommandQueueDrainedForTests());
}
/// <summary>
/// 验证机器人持续不可接收命令时,后续装载的命令队列会被完成,避免执行请求永久等待。
/// </summary>
[Fact]
public async Task LoadCommandQueue_WhenLatestStatusDoesNotAcceptCommands_CompletesQueueWait()
{
using var client = new FanucJ519Client();
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
var initResult = await _server.ReceiveAsync(_cts.Token);
client.StartMotion();
await SendStatusPacketAsync(initResult.RemoteEndPoint, sequence: 900, status: 0x04);
await Task.Delay(100, _cts.Token);
client.LoadCommandQueue(
[
new FanucJ519Command(sequence: 0, targetJoints: [1.0, 0.0, 0.0, 0.0, 0.0, 0.0])
]);
await SendStatusPacketAsync(initResult.RemoteEndPoint, sequence: 901, status: 0x04);
await client.WaitForCommandQueueDrainedAsync(_cts.Token).WaitAsync(TimeSpan.FromSeconds(1), _cts.Token);
await Assert.ThrowsAsync<TimeoutException>(
() => _server.ReceiveAsync(_cts.Token).AsTask().WaitAsync(TimeSpan.FromMilliseconds(120)));
Assert.True(client.IsCommandQueueDrainedForTests());
await client.StopMotionAsync(_cts.Token);
}
/// <summary>
/// 验证启用命令流结束开关后,LastData 结束帧发送完成会进入仅监控状态,不再继续回发保姿命令。
/// </summary>
@@ -572,13 +600,14 @@ public sealed class FanucJ519ClientTests : IDisposable
private async Task SendStatusPacketAsync(
IPEndPoint clientEndpoint,
uint sequence,
IReadOnlyList<double>? jointDegrees = null)
IReadOnlyList<double>? jointDegrees = null,
byte status = 15)
{
var responsePacket = new byte[FanucJ519Protocol.ResponsePacketLength];
BinaryPrimitives.WriteUInt32BigEndian(responsePacket.AsSpan(0x00, 4), 0);
BinaryPrimitives.WriteUInt32BigEndian(responsePacket.AsSpan(0x04, 4), 1);
BinaryPrimitives.WriteUInt32BigEndian(responsePacket.AsSpan(0x08, 4), sequence);
responsePacket[0x0c] = 15;
responsePacket[0x0c] = status;
if (jointDegrees is not null)
{