✨ feat(runtime): 完善 FANUC 命令参数与状态通道重连
* 在 FanucCommandProtocol/Client 中补齐速度倍率、TCP 位姿和 IO 的封包/解析,并引入 FanucIoTypes 字符串到枚举映射 * FanucControllerRuntime 在非仿真模式下接入真机命令通道,本地 缓存仅作为兜底,TCP 操作扩展为 7 维 Pose * FanucStateClient 增加帧超时检测、退避自动重连和诊断状态接口, 超时或重连期间不再把陈旧帧当作当前机器人状态 * FanucStateProtocol 锁定 90B 帧字段为 pose[6]、joint[6]、 external_axes[3] 和 raw_tail_words[4],并保留状态字诊断槽位 * ICspPlanner 增加 global_scale > 1.0 失败判定,self-adapt-icsp 内部禁用该判定以保留补点重试链路 * 同步更新 README/AGENTS/计划文档的 todo 状态和实现说明
This commit is contained in:
@@ -130,6 +130,125 @@ public sealed class FanucCommandClientTests : IDisposable
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 GetSpeedRatio 发送空业务体命令,并按 ratio_int / 100.0 解析倍率。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetSpeedRatioAsync_SendsFrameAndParsesRatio()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackGetSpeedRatioCommand(),
|
||||
FanucCommandProtocol.PackFrame(FanucCommandMessageIds.GetSpeedRatio, Convert.FromHexString("0000005a00000000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.GetSpeedRatioAsync(_cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
Assert.Equal(0.9, response.Ratio, precision: 6);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 SetSpeedRatio 会把 double 倍率夹到 0..100 的整数百分比后下发。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SetSpeedRatioAsync_SendsClampedPercentAndParsesSuccess()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackSetSpeedRatioCommand(2.0),
|
||||
FanucCommandProtocol.PackFrame(FanucCommandMessageIds.SetSpeedRatio, Convert.FromHexString("00000000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.SetSpeedRatioAsync(2.0, _cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 GetTcp 会发送 tcp_id 请求,并解析 result_code + tcp_id + 7 个 float 位姿。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetTcpAsync_SendsFrameAndParsesPose()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackGetTcpCommand(1),
|
||||
FanucCommandProtocol.PackFrame(
|
||||
FanucCommandMessageIds.GetTcp,
|
||||
Convert.FromHexString("00000000000000013f80000040000000404000000000000000000000000000003f800000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.GetTcpAsync(1, _cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
Assert.Equal([1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0], response.Pose);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 SetTcp 会按 tcp_id + 7 个 float 位姿下发并解析结果码。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SetTcpAsync_SendsFrameAndParsesSuccess()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackSetTcpCommand(1, [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0]),
|
||||
FanucCommandProtocol.PackFrame(FanucCommandMessageIds.SetTcp, Convert.FromHexString("00000000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.SetTcpAsync(1, [1.0, 2.0, 3.0, 0.0, 0.0, 0.0, 1.0], _cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 GetIo 会按 io_type、io_index 顺序请求,并解析 float IO 值。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task GetIoAsync_SendsFrameAndParsesValue()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackGetIoCommand(FanucIoTypes.DigitalOutput, 7),
|
||||
FanucCommandProtocol.PackFrame(FanucCommandMessageIds.GetIo, Convert.FromHexString("000000003f800000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.GetIoAsync(7, "DO", _cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
Assert.True(response.Value);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证 SetIo 会按 io_type、io_index、float value 顺序下发并解析结果码。
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task SetIoAsync_SendsFrameAndParsesSuccess()
|
||||
{
|
||||
using var client = new FanucCommandClient();
|
||||
var handlerTask = RunSingleResponseControllerAsync(
|
||||
FanucCommandProtocol.PackSetIoCommand(FanucIoTypes.DigitalOutput, 7, true),
|
||||
FanucCommandProtocol.PackFrame(FanucCommandMessageIds.SetIo, Convert.FromHexString("00000000")),
|
||||
_cts.Token);
|
||||
|
||||
await client.ConnectAsync("127.0.0.1", Port, _cts.Token);
|
||||
var response = await client.SetIoAsync(7, true, "DO", _cts.Token);
|
||||
|
||||
Assert.True(response.IsSuccess);
|
||||
await handlerTask.WaitAsync(TimeSpan.FromSeconds(2), _cts.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 验证命令响应 result_code 非零时,客户端会抛出可诊断异常而不是让上层误判成功。
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user