feat(focas): coalesce adjacent PMC reads into one pmc_rdpmcrng (#266)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 16:59:07 -04:00
parent 5b36c717de
commit 8d916f2af7
11 changed files with 790 additions and 11 deletions
@@ -73,6 +73,21 @@ public interface IFocasClient : IDisposable
/// <returns>The list of currently active alarms; empty when none are active.</returns>
Task<IReadOnlyList<FocasActiveAlarm>> ReadAlarmsAsync(CancellationToken cancellationToken);
/// <summary>
/// Read one PMC range (<c>pmc_rdpmcrng</c>) as raw slots: <c>(end - start + 1)</c> bytes split into
/// slots of <paramref name="slotType"/>'s width, from <paramref name="start"/>. The primitive behind PMC
/// range coalescing (Gitea #266) — the driver slices individual tag values out of the returned slots with
/// the same decode a per-tag read uses.
/// </summary>
/// <param name="letter">PMC area letter.</param>
/// <param name="slotType">Decides the slot width (Bit/Byte = 1, Int16 = 2, Int32/Float32 = 4, Float64 = 8).</param>
/// <param name="start">First byte address.</param>
/// <param name="end">Last byte address, inclusive; a whole number of slots from <paramref name="start"/>.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The raw slot values (null when not Good) and the mapped OPC UA status.</returns>
Task<(IReadOnlyList<long>? slots, uint status)> ReadPmcSlotsAsync(
string letter, FocasDataType slotType, int start, int end, CancellationToken cancellationToken);
// ---- Fixed-tree T1 (identity + axis discovery + fast-poll dynamic bundle) ----
/// <summary>