# Next-driver research — index & consolidated roadmap

> **Overall program design:** [`docs/plans/2026-07-15-driver-expansion-program-design.md`](../../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.md) | MTConnect (Agent-first) |
| [`mqtt-sparkplug.md`](mqtt-sparkplug.md) | MQTT / Sparkplug B |
| [`melsec-slmp.md`](melsec-slmp.md) | Mitsubishi MELSEC SLMP / MC Protocol |
| [`omron.md`](omron.md) | Omron (CIP + FINS) |
| [`bacnet-ip.md`](bacnet-ip.md) | BACnet/IP |
| [`sql-poll.md`](sql-poll.md) | SQL poll |
| [`modbus-rtu.md`](modbus-rtu.md) | Modbus RTU (extend existing Modbus) |
| [`00-existing-driver-browse-audit.md`](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`](../../plans/2026-07-15-universal-discovery-browser-design.md) | — (the mechanism itself) |
| MTConnect Agent | [`2026-07-15-mtconnect-driver-design.md`](../../plans/2026-07-15-mtconnect-driver-design.md) | **Universal** (`SupportsOnlineDiscovery`) |
| BACnet/IP | [`2026-07-15-bacnet-ip-driver-design.md`](../../plans/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`](../../plans/2026-07-15-mqtt-sparkplug-driver-design.md) | **Bespoke** observation browser |
| SQL poll | [`2026-07-15-sql-poll-driver-design.md`](../../plans/2026-07-15-sql-poll-driver-design.md) | **Bespoke** schema browser |
| Omron | [`2026-07-15-omron-driver-design.md`](../../plans/2026-07-15-omron-driver-design.md) | None — offline Sysmac tag-export import |
| Modbus RTU | [`2026-07-15-modbus-rtu-driver-design.md`](../../plans/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**: `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 ~70–80% 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** (~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):**
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`](../../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.
