Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests/WireFixtures.cs
T
Joseph Doherty 665ecd55c2 fix(focas): decode cnc_rdmacro dec_val at offset 6; vacant/fractional macros go Bad
The ODBM wire payload is [i32 mantissa][i16 length, a constant 10][i16 dec_val].
ReadMacroAsync read the decimal at offset 4, so every macro decoded without
failing but wrongly: the captured #500 = 1.0 (05 f5 e1 00 00 0a 00 08) published
0.01. FANUC normalises macros to a 9-significant-digit mantissa, so the error
was never small.

- dec_val is read at [6..7]; a payload < 8 bytes is refused, not guessed at.
- A vacant variable (dec_val 0xFFFF) publishes BadNoData, never a Good 0.
- Integer-typed tags get the exact whole value (integer arithmetic); a
  fractional macro on an integer tag is BadTypeMismatch, a whole value that
  does not fit the type BadOutOfRange. Float tags get mantissa / 10^dec.

Ported from the sister ignitionoee FOCAS driver v0.15.0 (Focas.Wire-013,
Focas-017/018); live vectors from Makino Z-34184 (31i-B, G431).
Adds a WireFixtures helper that loads the raw 31i-B captures in Fixtures/v3.
2026-10-07 13:57:11 -04:00

32 lines
1.6 KiB
C#

using ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Wire;
namespace ZB.MOM.WW.OtOpcUa.Driver.FOCAS.Tests;
/// <summary>
/// Loads the raw FOCAS response PDUs under <c>Fixtures/v3/</c> — byte-for-byte captures from a
/// real FANUC 31i-B (Makino, series G431) — and splits them into response blocks with the
/// production <see cref="FocasWireProtocol.ParseResponseBlocks"/>. The <c>live-*</c> captures
/// were taken by the sister ignitionoee FOCAS driver's read-only probes of Z-34184 (2026-08-12)
/// and are shared verbatim; the <c>ref-*</c> ones are this repo's own 2026-06-25 capture.
/// </summary>
internal static class WireFixtures
{
private const int PduHeaderLength = 10;
/// <summary>Every response block of the named capture, in wire order.</summary>
/// <param name="name">File name under <c>Fixtures/v3/</c>.</param>
/// <returns>The parsed response blocks.</returns>
public static IReadOnlyList<ResponseBlock> Blocks(string name)
{
var raw = File.ReadAllBytes(Path.Combine(AppContext.BaseDirectory, "Fixtures", "v3", name));
return FocasWireProtocol.ParseResponseBlocks(raw.AsSpan(PduHeaderLength));
}
/// <summary>The payload of the first block carrying <paramref name="command"/>.</summary>
/// <param name="name">File name under <c>Fixtures/v3/</c>.</param>
/// <param name="command">The FOCAS command id to locate.</param>
/// <returns>That block's payload bytes.</returns>
public static byte[] Payload(string name, ushort command) =>
Blocks(name).First(b => b.Command == command).Payload;
}