fix(focas): serialize per-device wire I/O + bound reads; tolerate AdminUI config formats
Equipment tags were stuck at Bad_WaitingForInitialData on the deployed driver: the equipment poll, fixed-tree loop, probe and recycle shared one FOCAS/2 socket with no serialization, and the steady-state read had no timeout — concurrent reads collided and a stalled read hung forever, never overwriting the node's initial-data seed.
- SynchronizedFocasClient: per-device SemaphoreSlim gate + per-call timeout around every wire op (Connect/Probe gated, not double-bounded); wired in EnsureConnectedAsync. ReadAsync/WriteAsync map a per-call timeout to BadCommunicationError instead of rethrowing.
- FlexibleStringConverter on FOCAS config Series: the AdminUI persists the enum as a number ("series":6); accept number-or-string instead of throwing -> stub.
- FocasHostAddress.TryParse tolerates a scheme-less {ip}[:{port}] (AdminUI hostAddress form); canonical focas:// unchanged, malformed schemes still rejected.
247 FOCAS tests green; each fix has a regression test. Live-validated on wonder-app-vd03 (tags read Good).
This commit is contained in:
@@ -38,6 +38,25 @@ public sealed class FocasFactoryConfigTests
|
||||
drv.Options.FixedTree.TimerPollInterval.ShouldBe(TimeSpan.FromSeconds(30));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The AdminUI persists FocasCncSeries as its integer value (e.g. <c>"series":6</c> = Thirty_i) —
|
||||
/// a bare JSON number. The factory must tolerate it (via FlexibleStringConverter) and build the
|
||||
/// real driver, not throw + fall back to a stub. Regression for the 2026-06-26 wonder data-plane
|
||||
/// deploy where the driver stubbed on "Cannot get the value of a token type 'Number' as a string".
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void CreateInstance_accepts_numeric_Series_from_AdminUI_serialization()
|
||||
{
|
||||
const string json = """
|
||||
{"Backend":"wire","series":6,"devices":[{"hostAddress":"10.0.0.5:8193","deviceName":"Makino","series":6,"positionDecimalPlaces":0}]}
|
||||
""";
|
||||
|
||||
var drv = FocasDriverFactoryExtensions.CreateInstance("drv-1", json);
|
||||
|
||||
drv.Options.Devices.ShouldHaveSingleItem();
|
||||
drv.Options.Devices[0].Series.ShouldBe(FocasCncSeries.Thirty_i);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the AlarmProjection configuration section is mapped to driver options.</summary>
|
||||
[Fact]
|
||||
public void CreateInstance_maps_AlarmProjection_section_onto_options()
|
||||
|
||||
Reference in New Issue
Block a user