#570
- STRING: the built-in 88-byte Logix STRING holds 82 characters (LEN + DATA[82] + 2 pad), not 84; the
write still sends the whole 88-byte structure and a read never decodes the pad as characters.
- Mistype churn: a type refusal (CIP 0xFF/0x2107) on the existence read no longer throws from
InitializeAsync, so the runtime is not disposed/re-created (and re-Forward-Opened) every poll; the driver
also keeps a runtime that throws TypeMismatch from init and records the device as reachable.
- Malformed tag name: a TagPath that does not parse, or a name the CIP symbolic encoder refuses (now also
non-ASCII, empty member, >255-char member, unterminated subscript), is AbCipTagConfigurationException ->
per-tag BadConfigurationError (named const in AbCipStatusMapper), warned once, never feeding the device
backoff or health; siblings keep reading. A malformed probe tag no longer feeds the backoff either.
- Micro800 BYTE/WORD/LWORD (0xD1/0xD2/0xD4) are type-checked as integers of their width: REAL/LREAL or a
narrower integer over LWORD is TypeMismatch, not decoded garbage.
#579
- ConnectionLease.ReleaseInBackground: one non-blocking release shape for both native AB runtimes. The
AbCip runtime's Dispose no longer waits up to 2 s under the per-tag lock; the pool still registers the
close synchronously so a re-create cannot overlap it. Offline soak tests (25 cycles, AbCip + AbLegacy)
prove no leaked connection; FakeEipServer gains OpenConnections.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
#572: listing continues at max(instance in page)+1 (no ascending-order assumption, no-progress guard kept);
browsePageDelayMs (default 5 ms) paces pages; DiscoverAsync reads templates through one batch reader
(IAbCipTemplateReaderFactory.CreateBatch) holding one lease.
#575: differential harness reports a same-status template refusal as an equal refusal; offline test pins the
open-attempt budget without relying on the abort ending the phase.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
CIP codec (#564): PatchU16 bounds guard; CipReply content equality; CipPath
port/length/ASCII/odd/oversize validation with ArgumentException for malformed
tag names and routes; CipPath.MessageRouter/ConnectionManager are now
ReadOnlySpan<byte>; refused Forward Open surfaces ForwardOpenRefusedException
with structured general/extended status; test CaptureFile uses BinaryPrimitives.
EncapsulationClient (#565): frame write bounded by the exchange timeout, which
faults (never cancels) the connection; finite positive timeouts enforced at
every entry point; gate wait bounded by the timeout; deterministic reorder of
the 300 ms test; new dispose-during-drain, gate-wait and RegisterSession
zero-context tests.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
ConfigureSharedLogger had no production caller, so the pool's only
diagnostics (first-wins options conflicts, scheduler/close failures)
went to NullLogger. DriverFactoryBootstrap now configures it from the
driver registry factory and from the AbCip/AbLegacy probe factories
(admin-only nodes reach the pool only through Test Connect). Wiring tests
reset the pool to a null logger first, so removing the call goes red
(verified).
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
On a wire the native PCCC path is proven only for N-file reads/writes and
one F8 read (ab_server). An absent transport key now resolves to Libplctag
for every family; explicit Native still wins and still warns once per
instance on PLC-5/LogixPccc. AdminUI dropdown, CLI help, evidence claims
(PcccFileType, AbLegacy.md wire-evidence table, release note) corrected:
S:0 (default probe) and B/T/C/R/S/L/ST/A/O/I are unverified natively.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
CipConnection gains two process-wide gate-diagnostic counters next to ForwardOpenCount: ForwardClosesSent (incremented as the request goes on the wire) and ForwardClosesAcked (incremented when the reply arrives within the 1 s budget).
It also records round-trip statistics for completed exchanges: ResetExchangeStats() clears them and ReadExchangeStats() returns count, min, max and mean. The cost is two Stopwatch reads and a few interlocked operations per request. No behaviour changes.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
ab_server answers every Execute PCCC with a fixed requestor id (pccc.c
PCCC_RESP_PREFIX: 07 3D F3 45 43 50 21) instead of echoing the request's, so
the R14-3 echo check rejected every native reply sent under the 0x1337 CIP
originator identity as BadReply. The pooled runtime now sends libplctag's
identity (PcccServices.RequestorVendor/RequestorSerial), which passes the echo
check against ab_server and against a real controller that echoes. Pinned by a
FakeEipServer round trip that answers with ab_server's fixed prefix.
ab_server's RegisterSession sanity check rejects any non-zero sender context
with encapsulation status 1 and a header-only reply, so every native session
against the simulator failed with BadCommunicationError. libplctag sends zero
(logix-register-session.hex); the codec test now matches that capture byte for
byte instead of excluding the context, and a client test pins the zero on the wire.