Bit-in-word RMW pass 2 � AbCip + AbLegacy #129

Merged
dohertj2 merged 1 commits from rmw-abcip-ablegacy into v2 2026-04-19 20:36:22 -04:00
Owner

Closes task #181. AbCip BOOL-within-DINT + AbLegacy N-file bit writes now route through driver-level WriteBitInDIntAsync / WriteBitInWordAsync via a parallel parent-word IAbCipTagRuntime / IAbLegacyTagRuntime with per-parent SemaphoreSlim. 9 new tests + 2 flipped pre-existing tests. AbCip 166/166, AbLegacy 96/96 passing. All four drivers (Modbus + AbCip + AbLegacy + FOCAS) now do proper per-word RMW. Merges to v2.

Closes task #181. AbCip BOOL-within-DINT + AbLegacy N-file bit writes now route through driver-level WriteBitInDIntAsync / WriteBitInWordAsync via a parallel parent-word IAbCipTagRuntime / IAbLegacyTagRuntime with per-parent SemaphoreSlim. 9 new tests + 2 flipped pre-existing tests. AbCip 166/166, AbLegacy 96/96 passing. All four drivers (Modbus + AbCip + AbLegacy + FOCAS) now do proper per-word RMW. Merges to v2.
dohertj2 added 1 commit 2026-04-19 20:36:19 -04:00
RMW pass 2 — AbCip BOOL-within-DINT + AbLegacy bit-within-word. Closes task #181. AbCip — AbCipDriver.WriteAsync now detects BOOL writes with a bit index + routes them through WriteBitInDIntAsync: strip the .N suffix to form the parent DINT tag path (via AbCipTagPath with BitIndex=null + ToLibplctagName), get/create a cached parent IAbCipTagRuntime via EnsureParentRuntimeAsync (distinct from the bit-selector tag runtime so read + write target the DINT directly), acquire a per-parent-name SemaphoreSlim, Read → Convert.ToInt32 the current DINT → (current | 1<<bit) or (current & ~(1<<bit)) → Write via EncodeValue(DInt, updated). Per-parent lock prevents concurrent writers to the same DINT from losing updates — parallels Modbus + FOCAS pass 1. DeviceState gains ParentRuntimes dict + GetRmwLock helper + _rmwLocks ConcurrentDictionary. DisposeHandles now walks ParentRuntimes too. LibplctagTagRuntime.EncodeValue's BOOL-with-bitIndex branch stays as a defensive throw (message updated to point at the new driver-level dispatch) so an accidental bypass fails loudly rather than silently clobbering the whole DINT. AbLegacy — identical pattern for PCCC N-file bit writes. AbLegacyDriver.WriteAsync detects Bit with bitIndex + PMC letter not in {B, I, O} (B-file + I/O use their own bit-addressable semantics so don't RMW at N-file word level), routes through WriteBitInWordAsync which uses Int16 for the parent word, creates + caches a parent runtime with the suffix-stripped N7:0 address, acquires per-parent lock, RMW. DeviceState extended the same way as AbCip (ParentRuntimes + GetRmwLock). LibplctagLegacyTagRuntime.EncodeValue Bit-with-bitIndex branch points at the driver dispatch. Tests — 5 new AbCipBoolInDIntRmwTests (bit set ORs + preserves, bit clear ANDs + preserves, 8-way concurrent writes to same parent compose to 0xFF, different-parent writes get separate runtimes, repeat bit writes reuse the parent runtime init-count 1 + write-count 2), 4 new AbLegacyBitRmwTests (bit set preserves, bit clear preserves 0xFFF7, 8-way concurrent 0xFF, repeat writes reuse parent). Two pre-existing tests flipped — AbCipDriverWriteTests.Bit_in_dint_write_returns_BadNotSupported + AbLegacyReadWriteTests.Bit_within_word_write_rejected_as_BadNotSupported both now assert Good instead of BadNotSupported, renamed to _now_succeeds_via_RMW. Total tests — AbCip 166/166, AbLegacy 96/96, full solution builds 0 errors; Modbus + FOCAS + TwinCAT + other drivers untouched. Task #181 done across all four libplctag-backed + non-libplctag drivers (Modbus BitInRegister + AbCip BOOL-in-DINT + AbLegacy N-file bit + FOCAS PMC Bit — all with per-parent-word serialisation). 00a428c444
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 91e6153b5d into v2 2026-04-19 20:36:22 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#129