Auto: focas-f2b — multi-path/multi-channel CNC

Adds optional `@N` path suffix to FocasAddress (PARAM:1815@2, R100@3.0,
MACRO:500@2, DIAG:280@2/1) with PathId defaulting to 1 for back-compat.
Per-device PathCount is discovered via cnc_rdpathnum at first connect and
cached on DeviceState; reads with PathId>PathCount return BadOutOfRange.
The driver issues cnc_setpath before each non-default-path read and
tracks LastSetPath so repeat reads on the same path skip the wire call.

Closes #264
This commit is contained in:
Joseph Doherty
2026-04-25 19:42:58 -04:00
parent 3b82f4f5fb
commit 2f3eeecd17
7 changed files with 478 additions and 17 deletions

View File

@@ -162,6 +162,28 @@ public interface IFocasClient : IDisposable
Task<(object? value, uint status)> ReadDiagnosticAsync(
int diagNumber, int axisOrZero, FocasDataType type, CancellationToken cancellationToken)
=> Task.FromResult<(object?, uint)>((null, FocasStatusMapper.BadNotSupported));
/// <summary>
/// Discover the number of CNC paths (channels) the controller exposes via
/// <c>cnc_rdpathnum</c>. Multi-path CNCs (lathe + sub-spindle, dual-turret,
/// etc.) report 2..N; single-path CNCs return 1. The driver caches the result
/// once per device after connect + uses it to validate per-tag <c>PathId</c>
/// values (issue #264). Default returns 1 so transports that haven't extended
/// their wire surface keep behaving as single-path.
/// </summary>
Task<int> GetPathCountAsync(CancellationToken cancellationToken)
=> Task.FromResult(1);
/// <summary>
/// Switch the active CNC path (channel) for subsequent reads via
/// <c>cnc_setpath</c>. Called by the driver before every read whose
/// <c>FocasAddress.PathId</c> differs from the path most recently set on the
/// session — single-path devices (PathId=1 only) skip the wire call entirely.
/// Default is a no-op so transports that haven't extended their wire surface
/// simply read whatever path the CNC has selected (issue #264).
/// </summary>
Task SetPathAsync(int pathId, CancellationToken cancellationToken)
=> Task.CompletedTask;
}
/// <summary>