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.
This commit is contained in:
@@ -577,9 +577,33 @@ public sealed class FocasWireClient : IAsyncDisposable, IDisposable
|
||||
ushort? pathId = null)
|
||||
=> ReadSingleWithTimeoutAsync(
|
||||
0x0015,
|
||||
payload => new WireMacro(number, payload.Length >= 4 ? ReadInt32(payload, 0) : 0, payload.Length >= 6 ? ReadInt16(payload, 4) : (short)0),
|
||||
payload => ParseMacro(number, payload),
|
||||
cancellationToken, timeout, EffectivePathId(pathId), number, number);
|
||||
|
||||
/// <summary>
|
||||
/// Decode a <c>cnc_rdmacro</c> (0x0015) payload. The on-wire <c>ODBM</c> record is 8
|
||||
/// big-endian bytes: <c>[0..3] i32 mantissa</c>, <c>[4..5] i16</c> length field (a
|
||||
/// <b>constant 10</b> on every observed response), <c>[6..7] i16 dec_val</c>
|
||||
/// (<c>0xFFFF</c> = vacant).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The middle field is the trap: it is not the decimal count but looks like a plausible one,
|
||||
/// so reading the decimal at offset 4 decoded every macro on every control without ever
|
||||
/// failing — just wrongly (the captured <c>#500 = 1.0</c> published <c>0.01</c>). Pinned by
|
||||
/// a live Makino 31i-B capture; found and fixed by the sister ignitionoee driver
|
||||
/// (Focas.Wire-013, 2026-08-05). A payload shorter than 8 bytes carries no
|
||||
/// <c>dec_val</c> and is refused rather than guessed at.
|
||||
/// </remarks>
|
||||
/// <param name="number">The macro number the request asked for.</param>
|
||||
/// <param name="payload">The response-block payload.</param>
|
||||
/// <returns>The decoded macro.</returns>
|
||||
/// <exception cref="FocasWireException">The payload is shorter than 8 bytes.</exception>
|
||||
internal static WireMacro ParseMacro(short number, byte[] payload)
|
||||
{
|
||||
RequireLength(payload, 8, "cnc_rdmacro");
|
||||
return new WireMacro(number, ReadInt32(payload, 0), ReadInt16(payload, 6));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a PMC range via <c>pmc_rdpmcrng</c>. <paramref name="area"/> is the numeric
|
||||
/// address-letter code (see <see cref="FocasPmcArea"/>); <paramref name="dataType"/>
|
||||
|
||||
Reference in New Issue
Block a user