Files
lmxopcua/docs/research/drivers/README.md
T
Joseph Doherty 8fc147d8d4
v2-ci / build (push) Successful in 3m14s
v2-ci / unit-tests (push) Failing after 9m13s
docs: driver-expansion program — 8 research reports + 7 design docs, parallel-reviewed
Adds the driver-expansion program design (umbrella: universal Discover-backed
browser + MTConnect, MQTT/Sparkplug B, BACnet/IP, SQL poll, Omron, Modbus RTU;
MELSEC deferred) plus the per-driver research reports.

All docs went through a 7-agent parallel review against the codebase before
this commit. Highlights fixed in review:

- universal browser: FOCAS FixedTree fills post-connect -> UntilStable settle
  + FixedTree.Enabled patch; MQTT reconciled to bespoke (was contradicting the
  program doc's SupportsOnlineDiscovery=false verdict)
- modbus-rtu: SerialPort.ReadTimeout doesn't bound async BaseStream reads ->
  linked-CTS per-op deadline (R2-01 class); BCL enum reuse would leak
  System.IO.Ports into Contracts
- bacnet: DiscoveryRediscoverPolicy enum name; UDP 47808 contention; live
  suite rewritten around unicast Who-Is + BBMD (broadcast doesn't cross VMs)
- sql-poll: real tier registration via DriverFactoryRegistry.Register;
  blackhole gate must not docker-pause the shared central SQL Server
- mqtt: Sparkplug v3.0 STATE topic form; first-in-repo proto codegen noted
- omron: host hardcodes isIdempotent:false today (retry seam unshipped);
  v1 scopes UDTs to dotted-leaf access
- mtconnect: SecurityClassification.ViewOnly; factory ParseEnum<T> pattern
- program doc: both valid enum-serialization patterns; IRediscoverable is
  change-signal-gated; RTU P2 adds System.IO.Ports; label is host-side
2026-07-15 16:40:36 -04:00

11 KiB
Raw Blame History

Next-driver research — index & consolidated roadmap

Overall program design: docs/plans/2026-07-15-driver-expansion-program-design.md is the authoritative umbrella — shared architecture, cross-cutting rules, the browse-tier decision, the dependency graph, and the wave build order. Start there; this file is the research/design index it points back to.

Research sweep run 2026-07-15 (8 parallel agents). Each per-driver report covers protocol + .NET library options (with license analysis), capability mapping to the Equipment-kind driver seams, TagConfig JSON shape, a browseability verdict + IDriverBrowser/IBrowseSession design where browsable, test-fixture strategy, and effort/phasing. This index synthesizes them.

Scope decided with the user (2026-07-15): 7 new drivers + a browse-support audit of the existing driver fleet. See [[project_next_driver_roadmap]] memory.

Reports

Report Driver
mtconnect-agent.md MTConnect (Agent-first)
mqtt-sparkplug.md MQTT / Sparkplug B
melsec-slmp.md Mitsubishi MELSEC SLMP / MC Protocol
omron.md Omron (CIP + FINS)
bacnet-ip.md BACnet/IP
sql-poll.md SQL poll
modbus-rtu.md Modbus RTU (extend existing Modbus)
00-existing-driver-browse-audit.md Browse audit of the 6 shipped browser-less drivers

Design docs (executable — built from the reports 2026-07-15)

Each non-deferred driver now has a build-ready design under docs/plans/:

Driver Design doc Browse integration
Universal browser (Wave 0) 2026-07-15-universal-discovery-browser-design.md — (the mechanism itself)
MTConnect Agent 2026-07-15-mtconnect-driver-design.md Universal (SupportsOnlineDiscovery)
BACnet/IP 2026-07-15-bacnet-ip-driver-design.md Universal (small sites); bespoke-lazy P-later
MQTT/Sparkplug B 2026-07-15-mqtt-sparkplug-driver-design.md Bespoke observation browser
SQL poll 2026-07-15-sql-poll-driver-design.md Bespoke schema browser
Omron 2026-07-15-omron-driver-design.md None — offline Sysmac tag-export import
Modbus RTU 2026-07-15-modbus-rtu-driver-design.md None (flat registers) — extends existing Modbus

Browse split confirmed by the designs: universal covers MTConnect + BACnet (from DiscoverAsync); bespoke required for MQTT (passive observation window) and SQL (schema walk ≠ discovery); Omron/Modbus-RTU don't browse. All designs consume the universal browser's P1 addition of ITagDiscovery.SupportsOnlineDiscovery — build the universal browser first.

New drivers — at a glance

Driver Library (license) Read/Write Browsable Write in v1 Effort
MTConnect Agent TrakHound MTConnect.NET (MIT) Read-only Yes/probe device model No (telemetry std) SM (~11.5 wk)
MQTT / Sparkplug B MQTTnet v5 (MIT) + SparkplugNet (MIT, net10 risk) or hand-rolled Tahu Read + subscribe Yes — birth certs / topic tree (passive, time-bounded) No M
MELSEC SLMP (deferred) Hand-roll 3E-binary framer (HslCommunication is a commercial-license blocker) Read/Write No — flat device memory Yes M (addressing)
Omron libplctag.NET (MPL-2.0) for CIP + hand-rolled FINS Read/Write CIP conditional (net-new) / FINS no Yes ML (no free sim)
BACnet/IP System.IO.BACnet / ela-compil (MIT, net10 TFM) Read + COV subscribe Yes — Who-Is + object-list No (write P3) ML
SQL poll Microsoft.Data.SqlClient (already in-repo) Read-only Yes — INFORMATION_SCHEMA No (write later) SM
Modbus RTU Extend existing Modbus (new IModbusTransport) Read/Write No — flat registers Yes S (lowest)

Notes:

  • All fit the existing all-.NET-10 Equipment-kind model — no COM gateway.
  • MELSEC: HslCommunication is a hard commercial-license blocker; the clean-license alternatives (McpX/MIT, McProtocol/LGPL) are too immature → hand-roll, patterned on S7. Repo already has MelsecAddress/MelsecFamily (from the Modbus-path work) to port.
  • Omron CIP reuses ~7080% of AbCip's libplctag body, but libplctag's @tags walker returns ErrorUnsupported on Omron NJ/NX — CIP browse is net-new Omron work gated on the Sysmac "Network Publish" attribute, not free reuse.
  • MQTT/Sparkplug & BACnet discovery is passive/time-bounded (listen/broadcast over a window), unlike a synchronous OPC UA browse — the browse session handles this with an observation window (+ Sparkplug rebirth NCMD to force near-synchronous enum).
  • Every new driver needs a typed tag editor (TagConfigEditorMap+TagConfigValidator) and must apply JsonStringEnumConverter on enum config fields (the known [[project_driver_enum_serialization_bug]] trap).

Existing-driver browse audit — verdicts

Driver Browsable? Mechanism Action
AbCip (ControlLogix EtherNet/IP) Yes CIP Symbol Object 0x6B + Template 0x6C — decode stack already ships Add browser (~23 d)
TwinCAT (Beckhoff ADS) Yes ADS symbol upload (SymbolLoaderFactory) — enumerator already ships Add browser (~34 d)
FOCAS (Fanuc CNC) Borderline Curated, not free-form (axes/spindles + macro/param/PMC ranges) Curated browser, deferred (~35 d)
AbLegacy (PCCC) No Numeric addresses (N7:0), no symbolic catalog Skip (documented)
S7 (S7comm) No Symbols live in the TIA project, not the CPU Skip (use OpcUaClient for optimized DBs)
Modbus (TCP/RTU) No Flat register space, no discovery Skip (documented)

Key finding: AbCip & TwinCAT already contain complete, unit-tested symbol-enumeration logic buried in the runtime driver's DiscoverAsync (gated by EnableControllerBrowse / Flat-mode) and simply not surfaced as the interactive IDriverBrowser the AdminUI picker consumes. Their browsers are a wrapping job over existing code, not new protocol work — the razor picker bodies already exist as static manual-entry stubs.

Shareable code: the CIP browse core (0x6B/0x6C decoders + system-tag filter + UDT layout) is protocol-CIP, not Rockwell-specific — extract a shared Cip.Core lib only if/when Omron CIP browse lands (don't pre-abstract). Both AbCip & TwinCAT already expose their enumerator behind an interface = the ready-made reuse seam.

Unified browse-support picture

Browsers to build (net + existing), by protocol richness:

  • Synchronous, model-rich: AbCip, TwinCAT, MTConnect (/probe), SQL (schema), BACnet (object-list), Omron-CIP.
  • Passive/time-bounded: MQTT/Sparkplug (observation window / rebirth).
  • Curated/fixed surface: FOCAS (deferred).

Not browsable — documented, decision recorded so it isn't revisited: MELSEC SLMP · Modbus TCP · Modbus RTU · S7 · AbLegacy · Omron-FINS (all flat address spaces / no on-wire symbol table).

Wave 0 — universal Discover-backed browser (the foundational browse piece):

  1. Universal DiscoveryDriverBrowser — ONE generic IDriverBrowser that runs any discovery-capable driver's ITagDiscovery.DiscoverAsync against a capturing IAddressSpaceBuilder and serves the captured tree back through the picker. Lights up AbCip, TwinCAT, FOCAS, MTConnect, BACnet (small sites), authored-MQTT, and every future discovery driver's picker with zero per-driver browser code. Gated on a new online-discovery capability flag so flat-address drivers stay manual-entry. Design: docs/plans/2026-07-15-universal-discovery-browser-design.md.
  2. AbCip / TwinCAT — covered immediately by the universal browser (their enumeration already lives in DiscoverAsync). Graduate each to a bespoke lazy IBrowseSession only if a real controller's symbol set is too large for the universal browser's eager one-shot discovery (UDT drill-down / per-click expand). Not up-front work anymore.

Wave 1 — low-effort, high-leverage new drivers: 3. Modbus RTU — lowest-effort new capability; extends the existing driver via one new IModbusTransport (lead with RTU-over-TCP for the containerized server). 4. SQL poll — zero new NuGet deps (SqlClient already in-repo), broad MES/ERP glue value, browsable.

Wave 2 — strategic new drivers: 5. MTConnect Agent — MIT library, clean browse, complements FOCAS with vendor-neutral machine-tool telemetry. 6. MQTT / Sparkplug B — UNS-strategic; plain-MQTT first to stand up the skeleton + validate MQTTnet/net10, then Sparkplug birth-driven discovery.

Wave 3 — heavier / test-gated: 7. BACnet/IP — opens facilities/HVAC/energy; COV + discovery + BBMD are the meaty parts (live-gate the broadcast reachability). 8. Omron — CIP-first (libplctag) then FINS; last of the active set because there's no free NJ/NX CIP simulator, so real-wire correctness is a live-hardware LiveIntegration gate.

Deferred (research complete, not scheduled):

  • MELSEC SLMP — deferred (2026-07-15). The mature library is a commercial-license blocker so it's a full hand-rolled 3E-binary framer with the hardest addressing model on the roadmap (device-code × bit/word × hex/octal/decimal × word order); revisit when a Mitsubishi-shop customer need makes it worth the build. Report stays valid as the starting point.

FOCAS browser slots opportunistically whenever the CNC surface is being touched.

Per-driver add pattern (reference)

*.Contracts project + driver impl (Connect/Discover/Read/Subscribe[/Write]) + factory + DriverType string + typed tag editor (TagConfigEditorMap+TagConfigValidator) + optional *.Browser project (register one line at EndpointRouteBuilderExtensions AddAdminUI DI) + docker fixture (project=lmxopcua label). Watch the enum-serialization trap and add a per-query/-op timeout deadline (the R2-01 frozen-peer lesson) on every network call.