fix(focas): a missing O-number or refused program sub-read goes Bad, not a Good 0

WireFocasClient.GetProgramInfoAsync defaulted every field the CNC did not report:
ONumber ?? 0 when the cnc_exeprgname2 payload was too short to carry it, and
Name '' / BlockCount 0 / Mode 0 when the sub-read was refused. 0 is a genuine
O-number (a 31i reports 0 for a path-executed program -- live Z-34184), a
genuine block count, and Mode 0 is MDI, a real operating mode.

FocasProgramInfo fields are now nullable; MapProgramInfo leaves unreported
fields null and the fixed tree publishes them BadNoData while the bundle's
other fields stay Good.

Ported from the sister ignitionoee FOCAS driver (101a3b1, 2026-08-12).
This commit is contained in:
Joseph Doherty
2026-10-07 14:08:26 -04:00
parent 1dba46bd7b
commit 26b512adba
4 changed files with 135 additions and 11 deletions
@@ -238,12 +238,15 @@ public sealed record FocasSpindleName(string Name, string Suffix1, string Suffix
/// Program + operation-mode snapshot. Name is the currently-executing
/// program filename (e.g. "O0001.NC"); ONumber is its Fanuc O-number (1-9999).
/// Mode is the numeric code from <c>cnc_rdopmode</c> — see <see cref="FocasOpMode"/>.
/// <b>A null field means the CNC did not report it</b> (its sub-read was refused, or the
/// payload was too short to carry it); the driver publishes that node Bad. It is never
/// defaulted to <c>0</c>, which is a genuine O-number, block count, and mode (MDI).
/// </summary>
public sealed record FocasProgramInfo(
string Name,
int ONumber,
int BlockCount,
int Mode);
string? Name,
int? ONumber,
int? BlockCount,
int? Mode);
/// <summary>Human-readable text for the <see cref="FocasProgramInfo.Mode"/> integer.</summary>
public static class FocasOpMode