12b981dc1b
The capability matrix admits #0..99999 on 30i/31i/32i (and anything on Unknown), and the FOCAS/2 request block has always carried 32-bit arguments, but WireFocasClient cast every number to short: MACRO:99999 asked the CNC for #-31073 and published that variable at Good under the authored tag. FocasWireClient's macro/parameter reads, WireMacro.Number and WireParameter.DataNumber are now int; the request blocks come from MacroRequest / ParameterRequest builders (byte-identical to the live 31i-B capture for #500). Same class on PMC: a range whose last byte passes 65535 (Unknown series admits it) is now refused BadOutOfRange instead of wrapping onto a different 16-bit address. The wire client is in-process (the other end is the CNC itself), so this is not a cross-repo contract change. Whether a real 30i honours cnc_rdmacro Arg1 > 32767 is the hardware userGate. Refs #740 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
85 lines
3.4 KiB
C#
85 lines
3.4 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
|
|
|
|
/// <summary>
|
|
/// #740 / R07:H14 — the capability matrix admits macro numbers up to 99999 (30i/31i/32i) and parameters up
|
|
/// to 29999 (Unknown: anything), and the FOCAS/2 request block carries 32-bit arguments, but the adapter
|
|
/// cast every number to <c>short</c>: <c>MACRO:99999</c> asked the CNC for <c>#-31073</c> and published
|
|
/// that variable Good under the authored tag. Numbers now travel as <c>int</c> end to end.
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class FocasWideNumberTests
|
|
{
|
|
/// <summary>The macro request carries the full number in both 32-bit argument slots.</summary>
|
|
[Theory]
|
|
[InlineData(500)]
|
|
[InlineData(32767)]
|
|
[InlineData(32768)]
|
|
[InlineData(99999)]
|
|
public void Macro_request_carries_the_unnarrowed_number(int number)
|
|
{
|
|
var block = FocasWireClient.MacroRequest(number, pathId: 1);
|
|
block.Command.ShouldBe((ushort)0x0015);
|
|
block.Arg1.ShouldBe(number);
|
|
block.Arg2.ShouldBe(number);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Positive control on the wire encoding: the request for #500 is byte-identical to the one the real
|
|
/// 31i-B answered in the <c>ref-macro-500-0x0015</c> capture, so widening changed nothing below 32768.
|
|
/// </summary>
|
|
[Fact]
|
|
public void Macro_request_for_500_matches_the_live_capture_bytes()
|
|
{
|
|
var body = FocasWireProtocol.BuildRequestBody([FocasWireClient.MacroRequest(500, pathId: 1)]);
|
|
Convert.ToHexString(body).ShouldBe("0001001C0001000100150000 01F4000001F4000000000000000000000000".Replace(" ", ""));
|
|
}
|
|
|
|
/// <summary>A global parameter puts its number in both arguments; an axis parameter puts the axis second.</summary>
|
|
[Theory]
|
|
[InlineData(6711, (short)0, 6711)]
|
|
[InlineData(40000, (short)0, 40000)]
|
|
[InlineData(40000, (short)2, 2)]
|
|
public void Parameter_request_carries_the_unnarrowed_number(int number, short axis, int expectedArg2)
|
|
{
|
|
var block = FocasWireClient.ParameterRequest(number, axis, pathId: 1);
|
|
block.Command.ShouldBe((ushort)0x000e);
|
|
block.Arg1.ShouldBe(number);
|
|
block.Arg2.ShouldBe(expectedArg2);
|
|
}
|
|
|
|
/// <summary>The decoded macro reports the number that was asked for, not a wrapped one.</summary>
|
|
[Fact]
|
|
public void Parsed_macro_keeps_a_number_above_short_range()
|
|
{
|
|
var payload = new byte[] { 0x05, 0xF5, 0xE1, 0x00, 0x00, 0x0A, 0x00, 0x08 };
|
|
var m = FocasWireClient.ParseMacro(99999, payload);
|
|
m.Number.ShouldBe(99999);
|
|
m.Scaled.ShouldBe(1.0);
|
|
}
|
|
|
|
/// <summary>
|
|
/// pmc_rdpmcrng addresses are 16-bit on the wire; a PMC number whose slot runs past 65535 is refused
|
|
/// rather than wrapped onto a different address (Unknown series admits any number).
|
|
/// </summary>
|
|
[Theory]
|
|
[InlineData(100, 1, true)]
|
|
[InlineData(65535, 1, true)]
|
|
[InlineData(65534, 2, true)]
|
|
[InlineData(65535, 2, false)]
|
|
[InlineData(70000, 1, false)]
|
|
public void Pmc_range_outside_16_bits_is_refused_not_wrapped(int number, int width, bool ok)
|
|
{
|
|
WireFocasClient.TryPmcRange(number, width, out var start, out var end).ShouldBe(ok);
|
|
if (ok)
|
|
{
|
|
((int)start).ShouldBe(number);
|
|
((int)end).ShouldBe(number + width - 1);
|
|
}
|
|
}
|
|
}
|