Commit Graph

694 Commits

Author SHA1 Message Date
Joseph Doherty 461133235a fix(drivers): InitializeAsync releases previous device/session state before overwrite (#630)
FOCAS, AbLegacy, AbCip, TwinCAT, S7 and OpcUaClient orphaned the previous
probe/recycle/fixed-tree loops and clients when re-initialised on a live driver.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 14:28:55 -04:00
Joseph Doherty 52708bff52 fix(address-space): numeric array writes convert (checked) to the node element type; array-to-scalar refusal only for Modbus/S7 (#610 review R6)
MAJOR-1: R4's exact element-type match refused ScadaBridge's CLR-list
writes (its element types are only Boolean/Int32/Float/Double/String/
DateTime) to every Int16/UInt16/Byte/UInt32/Int64/UInt64 array node and a
Double list to a Float array. EvaluateEquipmentWriteStructure now converts
a numeric array of another numeric element type element-wise, CHECKED, into
the node's element type and hands the converted array back through
OnEquipmentTagWrite's ref value, so the node stores and the driver receives
the node's own type. Any out-of-range, fractional-to-integer, NaN/Inf-to-
integer or Float-overflowing element refuses the whole write BadTypeMismatch
(nothing dispatched) - so the UInt32 -> Int32 bit reinterpretation stays
closed. Non-numeric mismatches stay refused. The wrong 'no client relies on
a looser rule' comment is corrected.

MINOR-1: the array-value-to-scalar-node refusal broke writes back to
browse-committed array tags (committed scalar, isArray never set) on
pass-through drivers. It now applies only when the owning driver's scalar
encoder cannot take an array (DriverArrayWriteSupport.
RefusesArrayValueOnScalarTag: Modbus, S7), carried RawTagPlan.
ScalarRefusesArrayValue / UnsReferenceVariable.BackingScalarRefusesArrayValue
-> EnsureVariable(refuseArrayOnScalar) -> a per-node set in the node
manager. OpcUaClient/Galaxy/others keep pre-#610 dispatch. Not on the
classifier whitelist (driver-type edit rebuilds); a mirror whose flag
changes is recreated, not updated in place.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 08:31:46 -04:00
Joseph Doherty a702875d41 Merge review R4 fixes (#610): array writes require an exact element-type match (BadTypeMismatch, nothing sent); Galaxy encoder refuses uint[]/ulong[]; Modbus array writability bounded by maxRegistersPerWrite/coil cap; arrayLength >= 1 required; Galaxy arrays without a declared length stay read-only; arrays to scalar nodes refused
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 08:08:03 -04:00
Joseph Doherty dabd8eb955 fix(address-space): exact element-type check for array writes + array-capability shape edges (#610 review R4)
MAJOR-1: the SDK skips TypeInfo.IsInstanceOfDataType on any node with an
OnWriteValue handler (every writable equipment node), and the node manager's
fail-fast treated numeric<->numeric as compatible, so a UInt32[] to a Galaxy
Int32 array matched MxValueEncoder's int[] arm by CLR covariance and was
written bit-reinterpreted with a Good result. EvaluateEquipmentWriteStructure
now matches an ARRAY write's element type exactly (Byte[]/ByteString the only
equivalence; Variant[]/object[] refused) -> BadTypeMismatch, nothing
dispatched. MxValueEncoder's int[]/long[] arms are exact-type guarded too, and
the Galaxy parity test's false "the SDK refuses" premise is corrected (the
UInt32/UInt64 exclusion is now the encoder's own answer). Scalar writes keep
numeric widening by decision: consumers write CLR-typed values (Double to a
Float node) and every driver's scalar encoder range-checks.

MINOR: Modbus row honours maxRegistersPerWrite (smallest of DriverConfig /
the tag's DeviceConfig, default 123) and FC15's 1968-coil ceiling, and
requires region + numeric address; Modbus/S7/Galaxy require isArray + integer
arrayLength >= 1 (Galaxy: an unbounded node would let a short write resize the
attribute); an array value to a scalar node is refused inline. Docs: Uns.md
array-write rules + upgrade note (ReadWrite array tags become client-writable
on first boot), Modbus.md, Galaxy.md.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 08:07:36 -04:00
Joseph Doherty 7628402893 fix(drivers): R5 minors - MTConnect session publish+generation atomic and superseded pump joined; MQTT birth-typed writes use the birth's own datatype; AbLegacy dead init join removed + Dispose join test; docs (#602)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:52:59 -04:00
Joseph Doherty b29642c5da Merge #602 stability: FOCAS/AbLegacy loop joins on shutdown, Postgres untyped string binds, MTConnect pump generation, MQTT birth-typed writes + QoS1 plain writes + claim backoff
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:33:03 -04:00
Joseph Doherty c120794958 Merge review R2 fixes: Galaxy subscribe-once deadline returns BadTimeout, S7 probe honours backoff, write success clears only write-side Faulted, null array elements rejected, S7_SIM_ALLOW_WRITES opt-in
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:31:36 -04:00
Joseph Doherty e77bca385a fix(galaxy,s7): R2 review minors — subscribe-once deadline, probe vs backoff, write-fault ownership, null array elements, write opt-in
- Galaxy: deadline bounds the wait, not the subscribe RPC; BadTimeout for all tags and late handles unsubscribed; doc comment relocated
- S7: probe connect runs on the connect deadline so a timeout opens the backoff window
- S7: a successful write only retires a Faulted the write side owns
- S7: null array elements rejected (BadTypeMismatch); multi-PDU array writes documented non-atomic
- S7 integration: array write test gated on S7_SIM_ALLOW_WRITES=1

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:31:22 -04:00
Joseph Doherty 275b240ebd fix(mqtt): back off the Sparkplug primary-host claim loop after failures (#602 STAB-27)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:29:08 -04:00
Joseph Doherty e863e9f0e2 fix(ablegacy): refuse native status-bit and PLC-5 writes before creating the runtime (#593)
A first-touch native write to a T/C/R status-bit sub-element (T4:0.DN) created
and initialised the tag's runtime before the runtime's write refusal could fire;
the init is one READ, so against a device lacking the file (the ab_server
fixture has no T/C/R) it surfaced as BadCommunicationError + Degraded health
instead of BadNotSupported. The bit-RMW path was not involved: AbLegacyAddress
rejects sub-element + /N, so a status bit's BitIndex is always null.

NativeAbLegacyTagRuntime.WriteRefusal is now the single predicate (status bit
by address whatever the authored type; any PLC-5 write), asked by the driver via
IAbLegacyTagFactory.WriteRefusal (default null, so libplctag is unchanged)
before EnsureTagRuntimeAsync and ahead of the bit-RMW branch.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:28:52 -04:00
Joseph Doherty 1f4f4fca8f fix(mqtt): birth-typed Sparkplug writes and QoS 1 for Plain writes (#602 STAB-25/26)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:27:42 -04:00
Joseph Doherty e76df0741a fix(mtconnect): stamp the /sample pump with its session generation and supersede a stale one (#602 STAB-20)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:24:43 -04:00
Joseph Doherty 5a5f9b22c9 Merge #622/#621/#606: OpcUaClient driver wire tests against the in-process server; SDK telemetry ctors in driver/browser; TreatWarningsAsErrors on the last 4 src projects
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:10:55 -04:00
Joseph Doherty 71881163df fix(sql): bind string keys untyped on Postgres so integer key columns compare (#602 STAB-24)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:03:00 -04:00
Joseph Doherty e9e35baa70 Merge #600.1/#603: provision changed-to-historized tags; retire dormant VirtualTagEngine; cache ScriptSandbox references; one LocalDb replication predicate for registration and drain gate
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:01:41 -04:00
Joseph Doherty 75c66c16ef Merge #625: deferred code-review findings resolved or ruled (S7.Cli verdict, browser CP release, Cluster member events, Sql Detach hoist, docs)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:01:32 -04:00
Joseph Doherty 80ed056d41 Merge #602(S7)/#610.2: S7 connect backoff, write-path worst-state-wins, Volatile health, factory logging, driver-side array writes
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 07:01:32 -04:00
Joseph Doherty 3fb6549209 fix(review): resolve deferred code-review findings; S7.Cli verdict, browser CP release, MemberLeft/Exited/Downed, Sql Detach hoist (#625)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:59:53 -04:00
Joseph Doherty 5a5c80ee57 fix(drivers): join FOCAS and AbLegacy background loops on shutdown (#602 STAB-23/6)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:59:38 -04:00
Joseph Doherty 82372f1672 fix(s7): connect backoff, worst-state write health, Volatile health, array writes (#602, #610)
STAB-21 ConnectionBackoff in EnsureConnectedAsync; STAB-22 write-pass accumulator with a
write-side Faulted that a clean read does not erase; _health on the Volatile idiom;
S7-016 contiguous array write (Bool arrays stay refused).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:59:29 -04:00
Joseph Doherty e3cb2dd591 fix(s7): thread ILoggerFactory into the S7 driver factory (#602 CONV-3)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:59:29 -04:00
Joseph Doherty b007d450d8 chore(build): OpcUaClient driver/browser on ITelemetryContext ctors, TreatWarningsAsErrors on remaining src projects (#621 #606)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:59:25 -04:00
Joseph Doherty 6a5e804eca Merge #593/#594: Ab.Transport offline/simulator test gaps, libplctag BadParam per-tag path, fixture suites skip when absent
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:58:44 -04:00
Joseph Doherty 0fb194a096 Merge #608/#602/#596: stale WaveC TODOs resolved, FOCAS/TwinCAT factory logging, Modbus health asymmetry documented, AbCip browsePageDelayMs field, Device1 by-design
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:53:59 -04:00
Joseph Doherty 9c39f2b6c4 fix(drivers): ILoggerFactory on FOCAS+TwinCAT factories; replace stale TODO(v3 WaveC); AbCip browsePageDelayMs form field; document Device1 + Modbus health (#608 #602 #596)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:53:38 -04:00
Joseph Doherty ba3d882897 Merge #605: Galaxy gateway seam hardening (advise-failure unmark, derived historian health, duplicate attributeRef surfaced, bounded subscribe-once read)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:52:49 -04:00
Joseph Doherty 6a6ac9a11f fix(abcip): libplctag ErrorBadParam at tag create is a per-tag config error, no device backoff (#570, #593)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:52:47 -04:00
Joseph Doherty 7c4c7f9653 fix(galaxy): seam hardening - advise unmark on throw, dup attributeRef surfaced, bounded subscribe-once read; delete dormant historian connection probe (#605)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:52:21 -04:00
Joseph Doherty b78c7ff9db feat(mtconnect): authoring DataType warning + loud ignored-DeviceName warning (#613 slice)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:51:40 -04:00
Joseph Doherty a8703c6afe refactor(virtualtags): retire dormant VirtualTagEngine/Source/Definition + TimerTriggerScheduler (#603)
TimerTriggerScheduler was welded to the engine and has no production consumer.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:51:22 -04:00
Joseph Doherty 21267750c7 test(ab-transport): blocked-write timeout fault path via a stream seam (#565, #593)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-08 06:50:40 -04:00
Joseph Doherty 3edff3304b fix(ab-transport): per-pool diagnostics + per-device open-attempt limit, CIP-level close recycle, CipConnection/pool test gaps (#566, #581)
- Process-wide CipConnection statics (ForwardOpenCount, Forward Close counts, RequestsSent, exchange
  stats, OpenAttempts, OpenAttemptBudget) move to an instance ConnectionDiagnostics owned by each
  DeviceSessionPool (and by a stand-alone CipConnection). The open-attempt budget becomes a disposable
  per-DeviceKey limit (LimitOpenAttempts); the differential harness scopes one to the press under test.
- LibplctagOrphanRelease.ForceRootingForTest is gone: ReleaseIfOrphaned(tag, canReadHandles) overload.
- A connected reply with general 0x01 / extended 0x0107 or 0x0203 (target no longer holds the class-3
  connection, TCP still up) retires the session at once; the next send reopens through the throttle.
- New tests: dispose during RegisterSession, abandoned-open observer (falsified), scheduler-dispose-throws
  guard (DeviceSessionPool.CloseAsync), release-in-background with a shared open in flight, per-device
  limit scoping, CIP-close recycle (request + keep-alive + negative control).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 23:21:02 -04:00
Joseph Doherty 501f6ea4e5 Merge #570/#579: native AbCip per-tag config errors, no mistype churn, STRING 82, LWORD type checks; non-blocking lease release (AbCip + AbLegacy)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe

# Conflicts:
#	src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Ab.Transport/Cip/CipPath.cs
2026-10-07 23:06:52 -04:00
Joseph Doherty 4e6eed7454 fix(abcip): native runtime STRING capacity, mistype churn, malformed-name config error, LWORD type check; non-blocking lease release (#570, #579)
#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
2026-10-07 23:05:49 -04:00
Joseph Doherty 7105ef3eb5 Merge #574/#576/#577/#584: AB forms preserve unknown keys + write factory ms keys, /raw device-name + prerender fixes, CLI read prints Bad status, docs dot-form
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 23:03:48 -04:00
Joseph Doherty bafbcbee7e fix(adminui): AB forms read/write the factory-bound *Ms duration keys, migrate legacy TimeSpan strings (#576)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 23:03:38 -04:00
Joseph Doherty 4ba6cbab3a Merge #572/#575: native browse paging/lease/pacing, differential equal-refusal + budget isolation
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe

# Conflicts:
#	docs/drivers/AbCip.md
2026-10-07 23:01:26 -04:00
Joseph Doherty be421a4cb0 fix(abcip): native browse paging/pacing/template lease (#572); differential equal-refusal + budget isolation (#575)
#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
2026-10-07 22:59:42 -04:00
Joseph Doherty 29cd25dec8 Merge #568/#569/#580/#585: shared AB status map + orphan release, confinement guard tokeniser, adapter + DefaultTransport tests
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 22:59:30 -04:00
Joseph Doherty 341417f193 fix(ab): share status map + libplctag orphan release, harden confinement guard, pin width-mismatch + DefaultTransport wiring (#568 #569 #580 #585)
- Ab.Transport: AbOpcUaStatusMap is the single AbStatus->OPC UA table; AbCip/AbLegacy mappers delegate.
- Remove dead MapCipGeneralStatus / MapPcccStatus / AbLegacy MapException (+ tests).
- LibplctagOrphanRelease is one source file (Ab.LibplctagShared) compiled into both drivers.
- LibplctagConfinementGuardTests: tokenising comment/literal stripper, block comments, csproj PackageReference guard.
- LibplctagStatusTranslationTests: real LibPlcTagException round trip, WrapSync/Wrap OCE pass-through, adapter routes decode via WrapSync.
- AbCipTypeMismatchTests: libplctag width-mismatch path (BadTypeMismatch, handle kept, no Degraded, warn once).
- Host: AddAbCipDriverDefaults extension + Program.cs source pin for Drivers:AbCip:DefaultTransport.
- docs/drivers/AbCip.md: layout rows + benign Degraded-after-outage note.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 22:59:06 -04:00
Joseph Doherty 13a6727033 Merge #564/#565: CIP codec hardening, EncapsulationClient gate/timeout bounds
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 22:55:02 -04:00
Joseph Doherty 7892eb13a8 fix(abcip): harden Ab.Transport CIP codec and EncapsulationClient (#564, #565)
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
2026-10-07 22:54:32 -04:00
Joseph Doherty 9641cefe2e fix(abcip): budget expected reply size in RequestScheduler.TakeBatch (#582); private lock + struct-handle check across read fragments (#567)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 22:53:10 -04:00
Joseph Doherty e588443ced fix(abcip,adminui): ParseEnum device wording + libplctag probe assertion (#574); AB forms preserve unknown keys + MaxConcurrentReads (#576); probe off for new AbCip driver (#577); docs T4:0.DN (#584)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 22:53:04 -04:00
Joseph Doherty 8e2dbc6721 merge: master (TwinCAT native array subscriptions) into feat/native-ab-transport
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 21:51:12 -04:00
Joseph Doherty f73c2db599 docs(ab): final review r2 m4/m5 — AbCipTransport doc names Native as default; AbLegacy array-write deferral scoped to libplctag
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 21:50:57 -04:00
Joseph Doherty cc3bea8370 fix(ab): final review M2 — a retyped libplctag handle field degrades to keep-alive
HandleOf cast the reflected nativeTagHandle with (int) and was called
inside the runtimes' catch blocks, so a libplctag bump that retyped the
field would replace every create failure with an InvalidCastException.
The field is now accepted only if it is still an int (otherwise
CanReadHandles is false and orphans are rooted), and HandleOf never
throws. Both projects pin the field type explicitly.

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 21:41:03 -04:00
Joseph Doherty 352ea449e9 fix(abcip): final review I4 — a type mismatch is a refusal, not a comms fault
A native TypeMismatch (read status, write gate, or CIP 0xFF/0x2107) used
to evict the runtime and degrade health on every poll; for a sole lease
holder that meant Forward Close + Forward Open per poll, and an all-
mismatched driver tripped the comms-loss watchdog. It now reports
BadTypeMismatch, keeps the handle, leaves health alone and warns once per
tag per instance — reads, whole-UDT reads and writes, plus the libplctag
width check. Tests (red against the pre-fix driver): single create over N
polls, unchanged health, one warning, and one Forward Open / zero Forward
Close over the real native runtime against FakeCipTarget. AbLegacy has no
TypeMismatch path (its native runtime has no type check).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 21:40:04 -04:00
Joseph Doherty 2cea1a0687 fix(ab-transport): final review I2 — wire DeviceSessionPool.Shared to the host logger
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
2026-10-07 21:36:37 -04:00
Joseph Doherty b75e013d0a feat(abcip): final review I1 — node-level Drivers:AbCip:DefaultTransport
The old AdminUI cannot author the per-driver transport key (#576), so the
'pin before upgrading' recipe had no working path. A node-level appsetting
(env Drivers__AbCip__DefaultTransport) now supplies the default for AbCip
drivers with no key: explicit key > node default > compiled Native. Bound
and validated unconditionally (bad value fails host start); the driver
factory and the Test Connect probe both read it so they agree. AbCip.md
upgrade recipe rewritten around the key; Configuration.md documents it.
Also documents first-wins for AllowPacking/probe and host-alias throttles
(final review M6) and moves the status-bit note out of AbCip.md (M5).

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
2026-10-07 21:34:48 -04:00