AB CIP PR 4 — IWritable #111

Merged
dohertj2 merged 1 commits from abcip-pr4-iwritable into v2 2026-04-19 16:59:59 -04:00
Owner

Summary

PR 4 of the AB CIP sequence.

LibplctagTagRuntime.EncodeValue — complete switch for every atomic Logix type (Bool standalone, SInt/USInt, Int/UInt, DInt/UDInt, LInt/ULInt, Real, LReal, String, Dt).

Deferred intentionally:

  • BOOL-within-DINT (read-modify-write, matches Modbus BitInRegister pattern)
  • Structure / UDT writes (PR 6)

AbCipDriver.WriteAsync — iterates preserving order, lazy-init via shared EnsureTagRuntimeAsync, maps libplctag status codes, maps exceptions:

  • NotSupportedExceptionBadNotSupported
  • FormatException / InvalidCastExceptionBadTypeMismatch (new status constant)
  • OverflowExceptionBadOutOfRange
  • anything else → BadCommunicationError
  • OperationCanceledException rethrows

Writes do NOT auto-retry in the driver — that's the resilience-layer's job per plan decisions #44/#45/#143.

Test plan

  • 10 new unit tests in AbCipDriverWriteTests
    • unknown ref, non-writable tag, successful write with encoded value + WriteCount
    • BOOL-in-DINT rejection, libplctag error mapping
    • FormatException → BadTypeMismatch, OverflowException → BadOutOfRange
    • generic exception → BadCommunicationError
    • batch preserves order across mixed outcomes
    • cancellation propagation
  • 98/98 AbCip tests pass (+10 from PR 3's 88)
  • Full solution builds 0 errors
  • Modbus / other drivers untouched

Follow-ups

  • BOOL-in-DINT read-modify-write — defer until real hardware needs
  • Structure writes — ship with PR 6 (UDT)

Merges to v2.

## Summary PR 4 of the AB CIP sequence. **`LibplctagTagRuntime.EncodeValue`** — complete switch for every atomic Logix type (Bool standalone, SInt/USInt, Int/UInt, DInt/UDInt, LInt/ULInt, Real, LReal, String, Dt). **Deferred intentionally:** - BOOL-within-DINT (read-modify-write, matches Modbus `BitInRegister` pattern) - Structure / UDT writes (PR 6) **`AbCipDriver.WriteAsync`** — iterates preserving order, lazy-init via shared `EnsureTagRuntimeAsync`, maps libplctag status codes, maps exceptions: - `NotSupportedException` → `BadNotSupported` - `FormatException` / `InvalidCastException` → `BadTypeMismatch` (new status constant) - `OverflowException` → `BadOutOfRange` - anything else → `BadCommunicationError` - `OperationCanceledException` rethrows Writes do NOT auto-retry in the driver — that's the resilience-layer's job per plan decisions #44/#45/#143. ## Test plan - [x] 10 new unit tests in `AbCipDriverWriteTests` - unknown ref, non-writable tag, successful write with encoded value + WriteCount - BOOL-in-DINT rejection, libplctag error mapping - FormatException → BadTypeMismatch, OverflowException → BadOutOfRange - generic exception → BadCommunicationError - batch preserves order across mixed outcomes - cancellation propagation - [x] **98/98 AbCip tests pass** (+10 from PR 3's 88) - [x] Full solution builds 0 errors - [x] Modbus / other drivers untouched ## Follow-ups - BOOL-in-DINT read-modify-write — defer until real hardware needs - Structure writes — ship with PR 6 (UDT) Merges to `v2`.
dohertj2 added 1 commit 2026-04-19 16:59:49 -04:00
AB CIP PR 4 — IWritable implementation. LibplctagTagRuntime.EncodeValue fills in the switch for every atomic Logix type the driver currently surfaces — Bool (standalone BOOL via SetInt8 0/1), SInt/USInt (SetInt8/SetUInt8), Int/UInt (SetInt16/SetUInt16), DInt/UDInt (SetInt32/SetUInt32), LInt/ULInt (SetInt64/SetUInt64), Real (SetFloat32), LReal (SetFloat64), String (SetString 0), Dt (epoch DINT via SetInt32). BOOL-within-DINT writes throw NotSupportedException with a code comment matching the Modbus BitInRegister pattern at ModbusDriver.cs line 640 — the read-modify-write logic + lock-per-DINT discipline is a follow-up PR rather than squeezing it into the initial wire plumbing. Structure writes throw NotSupportedException pointing at PR 6 when UDT support lands. AbCipDriver now implements IWritable. WriteAsync iterates writes preserving order, short-circuits on unknown reference → BadNodeIdUnknown, on non-writable tag definition → BadNotWritable, on unknown device → BadNodeIdUnknown. Happy path materialises the cached runtime via EnsureTagRuntimeAsync (shares PR 3's lazy-init path so read+write on the same tag hits one native handle), EncodeValue into the tag's buffer, WriteAsync flushes, GetStatus confirms the wire status, maps libplctag error codes via AbCipStatusMapper.MapLibplctagStatus, sets health Healthy on success. Per plan decisions #44, #45, #143 the driver does NOT auto-retry writes — that's a resilience-layer concern (Polly pipeline sitting above) keyed on the tag's WriteIdempotent flag. Exception-mapping table — OperationCanceledException rethrows (honors cancellation), NotSupportedException → BadNotSupported (bit-in-DINT, Structure, future unsupported types), FormatException → BadTypeMismatch (Convert.ToInt32 of a non-numeric string), InvalidCastException → BadTypeMismatch (caller passed an object incompatible with the conversion target), OverflowException → BadOutOfRange (value exceeds target type range, e.g. Int16 write of 1_000_000), any other Exception → BadCommunicationError (wire drop, libplctag-internal failure). Health surface updates Degraded on every non-Cancellation exception path, Healthy on success. Introduces AbCipStatusMapper.BadTypeMismatch (0x80730000). 10 new unit tests in AbCipDriverWriteTests covering — unknown ref → BadNodeIdUnknown, non-writable tag → BadNotWritable, successful DInt write encodes + flushes the value + marks WriteCount=1, BOOL-in-DINT rejected as BadNotSupported (separate ThrowingBoolBitFake mirrors LibplctagTagRuntime's runtime check), non-zero libplctag status after write mapped via AbCipStatusMapper (timeout -5 → BadTimeout), FormatException from non-numeric-string write → BadTypeMismatch (RealConvertFake exercises real Convert.ToInt32), OverflowException from Int16 write of 1_000_000 → BadOutOfRange, generic exception during write → BadCommunicationError + health Degraded, batch with mixed success+failure preserves order across four request types, cancellation propagates as OperationCanceledException. FakeAbCipTag's test-fake base class methods made virtual so override hooks work correctly through the IAbCipTagRuntime interface (new-shadow was silently falling through to the base implementation). Total AbCip unit tests now 98/98 passing; Modbus + other existing tests untouched; full solution builds 0 errors. 257f4fd3f5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit cee52a9134 into v2 2026-04-19 16:59:59 -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#111