fix(focas): scale positions from each cnc_rddynamic2 block; delete the fabricated cnc_getfigure

Every 0x0026 position block is self-describing: [i32 raw][u16 base][u16 exponent],
value = raw / base^exponent. The live 31i-B capture (ref-dynamic2-axis1.bin)
reports base 10 / exponent 4 on every block, so axis 1 is 280.1574 mm, not
2801574. The driver ignored the envelope and instead sent 'cnc_getfigure'
(0x00D3) -- a command that does not exist in the FOCAS wire protocol, served only
by the in-tree mock; the real control refuses it with rc=1, so on hardware every
position fell back to PositionDecimalPlaces (default 0) and published raw counts.

- ParsePositionBlock decodes the envelope into FocasPosition; the driver applies
  raw / base^exponent at publish. PositionDecimalPlaces is now only the fallback
  for a scale-less (4-byte) block.
- A block describing no position (0xFFFF vacant marker, base not 2/10, exponent
  > 18 -- raw / 10^400 would read as a Good 0.0) publishes BadNoData on that node
  alone; siblings are untouched.
- ReadPositionFiguresAsync / IFocasClient.GetPositionFiguresAsync and the
  per-axis figure cache are deleted.

Ported from the sister ignitionoee FOCAS driver v0.17.0 (+ Focas.Wire-014);
live axis-scoped captures live-dynamic2-axis{1,2}-0x0026.bin (Z-34184 X 0.0374,
Y 370.177) added as fixtures.
This commit is contained in:
Joseph Doherty
2026-10-07 14:03:08 -04:00
parent 9460da513c
commit 69e0a622f3
16 changed files with 380 additions and 356 deletions
@@ -290,9 +290,9 @@ public static class FocasDriverFactoryExtensions
public string? Series { get; init; }
/// <summary>
/// Gets or sets the axis-position decimal places. <c>cnc_rddynamic2</c> returns
/// positions as scaled integers; the driver divides by <c>10^PositionDecimalPlaces</c>
/// so they surface in engineering units. Omitted / <c>0</c> = no scaling (legacy).
/// Gets or sets the fallback axis-position decimal places, applied only to a
/// <c>cnc_rddynamic2</c> position block that carries no scale of its own (a real CNC's
/// block does, and its scale wins). Omitted / <c>0</c> = no scaling.
/// </summary>
public int? PositionDecimalPlaces { get; init; }
}