AB CIP PR 5 — ITagDiscovery (pre-declared + controller-enumeration scaffolding) #112

Merged
dohertj2 merged 1 commits from abcip-pr5-discovery into v2 2026-04-19 17:07:05 -04:00
Owner

Summary

PR 5 of the AB CIP sequence.

DiscoverAsync:

  • Pre-declared tags emit under AbCip/<device-host>/<tag> matching Modbus shape
  • Writable→Operate, non-writable→ViewOnly
  • AbCipSystemTagFilter rejects __DEFVAL_*, Routine:, Task:, Local:N:X, Map:, Axis:, Cam:, MotionGroup:
  • Controller-discovered tags (via IAbCipTagEnumerator) land under Discovered/

Abstraction:

  • IAbCipTagEnumerator + IAbCipTagEnumeratorFactory — swappable; default is empty
  • The real @tags walker is a follow-up (libplctag 1.5.2 doesn't expose TagInfoPlcMapper)

AbCipTemplateCache:

  • ConcurrentDictionary<(device, templateInstanceId), AbCipUdtShape> + Put/TryGet/Clear
  • FlushOptionalCachesAsync clears it
  • PR 6 (UDT reader) writes into it

Test plan

  • 25 new discovery tests — emission, folder hierarchy, system-tag filter, device params, cache roundtrip
  • 123/123 AbCip unit tests pass (+25 from PR 4's 98)
  • Full solution builds 0 errors
  • Modbus + other drivers untouched

Deferred

  • Real @tags / Symbol Object decoder — follow-up PR (needs custom IPlcMapper or raw-buffer decode)
  • UDT Template Object reader — PR 6

Merges to v2.

## Summary PR 5 of the AB CIP sequence. **DiscoverAsync:** - Pre-declared tags emit under `AbCip/<device-host>/<tag>` matching Modbus shape - Writable→Operate, non-writable→ViewOnly - `AbCipSystemTagFilter` rejects `__DEFVAL_*`, `Routine:`, `Task:`, `Local:N:X`, `Map:`, `Axis:`, `Cam:`, `MotionGroup:` - Controller-discovered tags (via `IAbCipTagEnumerator`) land under `Discovered/` **Abstraction:** - `IAbCipTagEnumerator` + `IAbCipTagEnumeratorFactory` — swappable; default is empty - The real `@tags` walker is a follow-up (libplctag 1.5.2 doesn't expose `TagInfoPlcMapper`) **`AbCipTemplateCache`:** - `ConcurrentDictionary<(device, templateInstanceId), AbCipUdtShape>` + Put/TryGet/Clear - `FlushOptionalCachesAsync` clears it - PR 6 (UDT reader) writes into it ## Test plan - [x] 25 new discovery tests — emission, folder hierarchy, system-tag filter, device params, cache roundtrip - [x] **123/123 AbCip unit tests pass** (+25 from PR 4's 98) - [x] Full solution builds 0 errors - [x] Modbus + other drivers untouched ## Deferred - Real `@tags` / Symbol Object decoder — follow-up PR (needs custom `IPlcMapper` or raw-buffer decode) - UDT Template Object reader — PR 6 Merges to `v2`.
dohertj2 added 1 commit 2026-04-19 17:06:54 -04:00
AB CIP PR 5 — ITagDiscovery (pre-declared emission + controller-enumeration scaffolding). DiscoverAsync streams tags to IAddressSpaceBuilder with the same shape the Modbus driver uses, keyed by device host address so one driver instance exposing N PLCs produces N device folders under a shared "AbCip" root. Pre-declared tags from AbCipDriverOptions.Tags emit first, filtered through AbCipSystemTagFilter so __DEFVAL_* / __DEFAULT_* / Routine: / Task: / Local:N:X / Map: / Axis: / Cam: / MotionGroup: infrastructure names never reach the address space. Writable tags map to SecurityClassification.Operate, non-writable to ViewOnly. Controller enumeration (walking the Logix Symbol Object via @tags) is wired up through a new IAbCipTagEnumerator + IAbCipTagEnumeratorFactory abstraction — default EmptyAbCipTagEnumeratorFactory returns an empty sequence so the driver stays production-safe without a real decoder. Tests inject FakeEnumeratorFactory to exercise the discovered-tag path: discovered tags land under a Discovered/ sub-folder, program-scope produces Program:P.Name full references, the IsSystemTag hint + the AbCipSystemTagFilter both act as gates, ReadOnly surfaces SecurityClassification.ViewOnly. The real @tags walker is a follow-up because libplctag 1.5.2 (latest stable on NuGet) does not expose TagInfoPlcMapper / UdtInfoMapper — the DataTypes namespace only ships IPlcMapper<T>, so enumerating the Symbol Object requires either implementing a custom IPlcMapper for the CIP byte layout or raw-buffer decoding via plc_tag_get_raw — both non-trivial enough to warrant their own PR. Code comment on EmptyAbCipTagEnumerator documents the gap + points to the follow-up. AbCipTemplateCache placeholder ships with a ConcurrentDictionary<(device, templateInstanceId), AbCipUdtShape> + Put / TryGet / Clear / Count — the Template Object reader (CIP class 0x6C) populates it in PR 6 and FlushOptionalCachesAsync now clears it. AbCipUdtShape + AbCipUdtMember records describe UDT layout — type name + total size + ordered members with offset / type / array length. AbCipDriver ctor gains optional enumeratorFactory parameter matching the tagFactory pattern from PR 3. TemplateCache exposed internally for PR 6's reader to write into. 25 new unit tests in AbCipDriverDiscoveryTests covering — pre-declared emission under device folder, DeviceName fallback to host address, system-tag filter rejecting pre-declared infrastructure names, cross-device tag filtering (tags for a device this driver does not own are ignored), controller enumeration adds tags under Discovered/, system-tag hint + filter both enforced, ReadOnly → ViewOnly, AbCipTagCreateParams composition (gateway / port / CIP path / libplctag attribute / tag name "@tags" / timeout), default enumerator factory used when not injected, 13 Theory cases covering every AbCipSystemTagFilter pattern, template cache roundtrip + clear, FlushOptionalCachesAsync clears the cache. Total AbCip unit tests now 123/123 passing (+25 from PR 4's 98). Modbus + other existing tests untouched; full solution builds 0 errors. Unblocks PR 6 (UDT structured read/write) + PR 7 (subscriptions consuming PollGroupEngine from PR 1). 447086892e
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit dd1389a8e7 into v2 2026-04-19 17:07:05 -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#112