#148 introduced auto-prohibited coalesced ranges that persist for the
driver lifetime. Long-running deployments with transient PLC permission
changes (firmware update unlocking a previously-protected register,
operator reconfiguring the device) had no recovery short of operator
restart.
Adds an opt-in background loop that re-probes each prohibition periodically:
- ModbusDriverOptions.AutoProhibitReprobeInterval (TimeSpan?, default null
= disabled). Set to e.g. TimeSpan.FromHours(1) to opt in.
- _autoProhibited refactored from HashSet<key> to Dictionary<key, DateTime>
so each entry tracks its last failure / last re-probe timestamp.
- ReprobeLoopAsync runs on the same Task.Run pattern as ProbeLoopAsync;
cancelled by ShutdownAsync. Each tick snapshots the prohibition set
and issues a one-shot coalesced read per range. Successful re-probes
drop the prohibition; failed ones bump the timestamp + leave the
prohibition in place.
- Communication failures during re-probe (transport-level) are treated
the same as PLC-exception failures — the prohibition stays, but isn't
upgraded to "permanent" since transports recover. The driver-instance
health surface picks up the failure separately.
- ShutdownAsync explicitly clears the prohibition set so a manual restart
via ReinitializeAsync starts with a clean slate (matches the old
"restart to clear" semantics).
- Factory DTO + JSON binding extended with AutoProhibitReprobeMs field.
Tests (2 new, additive to the 3 in ModbusCoalescingAutoRecoveryTests):
- Reprobe_Clears_Prohibition_When_Range_Becomes_Healthy — protected
register at 102 records prohibition; clearing the simulated protection
+ invoking the re-probe drops the prohibition.
- Reprobe_Leaves_Prohibition_When_Range_Is_Still_Bad — re-probe on a
still-failing range keeps the prohibition in place.
Tests use a new internal RunReprobeOnceForTestAsync helper to fire one
re-probe pass synchronously, so the suite doesn't have to wait on the
background timer (the loop's timer behaviour is exercised implicitly via
the InitializeAsync wire-up + the synchronous helper sharing the actual
re-probe code path).
234 + 2 = 236 unit tests green.