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
Next-driver research — index & consolidated roadmap
Overall program design:
docs/plans/2026-07-15-driver-expansion-program-design.mdis 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) | S–M (~1–1.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 | M–L (no free sim) |
| BACnet/IP | System.IO.BACnet / ela-compil (MIT, net10 TFM) | Read + COV subscribe | Yes — Who-Is + object-list | No (write P3) | M–L |
| SQL poll | Microsoft.Data.SqlClient (already in-repo) | Read-only | Yes — INFORMATION_SCHEMA | No (write later) | S–M |
| 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:
HslCommunicationis a hard commercial-license blocker; the clean-license alternatives (McpX/MIT, McProtocol/LGPL) are too immature → hand-roll, patterned on S7. Repo already hasMelsecAddress/MelsecFamily(from the Modbus-path work) to port. - Omron CIP reuses ~70–80% of AbCip's libplctag body, but libplctag's
@tagswalker returnsErrorUnsupportedon 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 applyJsonStringEnumConverteron 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 (~2–3 d) |
| TwinCAT (Beckhoff ADS) | Yes | ADS symbol upload (SymbolLoaderFactory) — enumerator already ships |
Add browser (~3–4 d) |
| FOCAS (Fanuc CNC) | Borderline | Curated, not free-form (axes/spindles + macro/param/PMC ranges) | Curated browser, deferred (~3–5 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).
Recommended build order (value × effort)
Wave 0 — universal Discover-backed browser (the foundational browse piece):
- Universal
DiscoveryDriverBrowser— ONE genericIDriverBrowserthat runs any discovery-capable driver'sITagDiscovery.DiscoverAsyncagainst a capturingIAddressSpaceBuilderand 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. - AbCip / TwinCAT — covered immediately by the universal browser (their enumeration
already lives in
DiscoverAsync). Graduate each to a bespoke lazyIBrowseSessiononly 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.