docs(driver-expansion): resolve all open design concerns from the review pass
Every concern the 7-agent review parked is now decided and integrated: - universal browser: in-flight capture coalescing keyed (driverType, config-hash) + global cap of 4 concurrent captures; CanBrowse catches a throwing TryCreate and defensively shuts down the throwaway instance; cleanup ShutdownAsync bounded at 10s (R2-01); picker Refresh = close + re-capture. Coalesced sessions share an immutable tree, so DisposeAsync drops the session's reference, not the tree. - mtconnect: UNAVAILABLE pinned to BadNoCommunication (fleet's BadCommunicationError stays reserved for the driver's own transport failures); TIME_SERIES sampleCount flows into ArrayDim; Subscribe timeout bounds only the stream-start handshake; library version/TFM folded into the license checklist. - mqtt: browse rebirth is an explicit DriverOperator-gated "Request rebirth" button (RequestRebirthAsync(scope)) - never fired by open/root/expand; hand-rolled reconnect loop committed for P1 (MQTTnet v5 dropped ManagedMqttClient); Wave-2-start library re-verification checkbox; implement from the Sparkplug v3.0 spec text. - bacnet: P1 opens with a first-spike checklist (package TFM, BBMD/ segmented-RPM/COV API smoke, unicast-I-Am fixture behavior); AdminUI-node browse registers a second foreign device - BBMD FD-table sizing note. - sql-poll: split-node browse needs env parity for Sql__ConnectionStrings__ refs on admin nodes (actionable error + session-only pasted literal); one-row-per-key query contract (last-wins + rate-limited warning); absent key -> BadNoData; operationTimeout > commandTimeout authoring rule; Browser->Driver.Sql SqlClient transitive on AdminUI accepted on record. - omron: FINS framer gated on W227 + live golden vectors; CIP string layout is the first P1 live-gate item; AbCip harness static-init anti-pattern note; writable-defaults-true operator warning. - modbus-rtu: first P1 step confirms pymodbus exposes framer=rtu on a TCP server (custom-script fallback otherwise). - program doc: new cross-cutting rule - driver ctors must be connection-free (the universal browser's CanBrowse throwaway instances depend on it).
This commit is contained in:
@@ -74,7 +74,7 @@ src/Drivers/
|
||||
| Path | Library | License | Notes |
|
||||
|---|---|---|---|
|
||||
| **CIP** | **`libplctag.NET`** (`<PackageReference Include="libplctag"/>`), attribute string `plc=omron-njnx` | native core dual MPL-2.0 / LGPL-2.1; .NET wrapper **MPL-2.0** | **Zero new license review** — the identical dependency AbCip already ships. |
|
||||
| **FINS** | **Hand-rolled** (no third-party dep) | our own code | FINS framing is ~200 lines, fully documented (W227 + Wireshark dissector); mirrors `ModbusTcpTransport`. |
|
||||
| **FINS** | **Hand-rolled** (no third-party dep) | our own code | FINS framing is ~200 lines, documented (W227 + Wireshark dissector) but research-sourced — trusted only after the P2 validation gate (§10); mirrors `ModbusTcpTransport`. |
|
||||
|
||||
**Avoid `HslCommunication`.** The maintained builds are **commercial-paid**; the "MIT" claim in
|
||||
casual sources refers to a stale, abandoned community fork
|
||||
@@ -187,6 +187,12 @@ programmer set its **Network Publish** attribute (`Publish Only`) in **Sysmac St
|
||||
it are invisible to any external client — including our reads. Document this loudly in
|
||||
`docs/drivers/Omron.md`: a CIP read of an unpublished variable fails at the wire, not a config bug.
|
||||
|
||||
**Operator documentation task (`docs/drivers/Omron.md`, P1):** alongside the Network-Publish note
|
||||
above, the driver doc must carry an explicit operator-facing warning that **`writable` defaults to
|
||||
`true` when omitted** from a tag's config (the convention inherited from AbCip's parser, §5.4). On
|
||||
a write-capable PLC driver, forgetting the field does not fail safe — it silently authors a
|
||||
writable node. Operators must set `"writable": false` explicitly on every read-only point.
|
||||
|
||||
---
|
||||
|
||||
## 4. Browse story — reconciliation with the universal browser
|
||||
@@ -337,6 +343,11 @@ via `EquipmentTagRefResolver`. For an **equipment tag**, `FullName` **is the raw
|
||||
`AbCipDataTypeExtensions`) maps to `DriverDataType`; `SecurityClass` = `Operate` when `writable` else
|
||||
`ViewOnly`.
|
||||
|
||||
**`writable` defaults to `true` when absent** — the parser inherits AbCip's convention, on both
|
||||
transport shapes (§5.2/§5.3). An Omron tag authored without an explicit `writable` field is
|
||||
therefore a **writable** node (`SecurityClass = Operate`). On a write-capable PLC driver this
|
||||
deserves an explicit operator-facing warning — see the operator-documentation item in §3.1.
|
||||
|
||||
---
|
||||
|
||||
## 6. Typed editor + validator
|
||||
@@ -464,14 +475,26 @@ The honest gap: **there is no free NJ/NX CIP simulator.**
|
||||
- **Primary CI surface — hand-rolled `IOmronCipRuntime` fake** (the same seam pattern AbCip uses via
|
||||
`IAbCipTagRuntime`/factory injection). Drives read/write/decode/array/BOOL-in-word/status-mapping
|
||||
unit tests with **no hardware and no container**. This is where correctness of the driver *body* is
|
||||
proven.
|
||||
proven. When mirroring AbCip's test structure, do **not** copy its integration-harness
|
||||
profile-class pattern that had the textual static-initializer-order bug (`AbServerProfile.cs`,
|
||||
fixed in `326b22a7`) — initialize profile constants free of cross-field static init-order
|
||||
dependence.
|
||||
- **Live gate — env-gated `Category=LiveIntegration`** against real NJ/NX hardware, skipping cleanly
|
||||
when the env var is absent (the pattern the HistorianGateway live suite uses). This is the **only**
|
||||
way to validate the real `plc=omron-njnx` libplctag wire path, Network-Publish behavior, and Omron
|
||||
string/array/UDT quirks. Env vars e.g. `OMRON_CIP_ENDPOINT` / `OMRON_CIP_TEST_TAG` /
|
||||
`OMRON_CIP_WRITE_SANDBOX_TAG`. **Flag as an infra-gated known limitation** in the driver doc + this
|
||||
design until an NJ/NX is on the bench. (Omron's own CX-Simulator is Windows-only + license-gated in
|
||||
CX-One — not a CI fixture.)
|
||||
way to validate the real `plc=omron-njnx` libplctag wire path. **P1 live-hardware gate checklist,
|
||||
in order:**
|
||||
1. **CIP string layout — FIRST.** Omron NJ/NX's string layout differs from Logix, and whether
|
||||
libplctag's `GetString` handles it correctly can only be proven on hardware. This is the
|
||||
likeliest wire surprise, and the `IOmronCipRuntime` fake cannot cover it — run a `String`
|
||||
read/write round-trip before anything else.
|
||||
2. Basic read/write round-trips per `OmronDataType` over the real `plc=omron-njnx` path.
|
||||
3. Network-Publish behavior (unpublished variable → wire failure, per §3.1).
|
||||
4. Array reads/writes + dotted UDT-member access (§3.1's live-gate item).
|
||||
|
||||
Env vars e.g. `OMRON_CIP_ENDPOINT` / `OMRON_CIP_TEST_TAG` /
|
||||
`OMRON_CIP_WRITE_SANDBOX_TAG` / `OMRON_CIP_STRING_SANDBOX_TAG`. **Flag as an infra-gated known
|
||||
limitation** in the driver doc + this design until an NJ/NX is on the bench. (Omron's own
|
||||
CX-Simulator is Windows-only + license-gated in CX-One — not a CI fixture.)
|
||||
|
||||
### FINS
|
||||
- **Primary — hand-rolled byte-level `IFinsTransport` fake** (mirror the Modbus `IModbusTransport`
|
||||
@@ -497,20 +520,34 @@ can't be proven in CI, so it should land when live NJ/NX hardware is available f
|
||||
|
||||
| Phase | Scope | Effort | Gate |
|
||||
|---|---|---|---|
|
||||
| **P1 — Omron CIP r/w (NJ/NX)** | `Driver.Omron` + `.Contracts`, `DriverType="Omron"`, `OmronCipTransport` on libplctag `plc=omron-njnx`; Connect/Read/Write/Subscribe(poll)/Probe; authored + equipment tags; typed editor + validator; factory + registration | Low–Medium (70–80% AbCip reuse) | hand-rolled runtime fake (CI) + env-gated live gate (hardware) |
|
||||
| **P2 — Omron FINS r/w (CJ/CS/CP)** | `IFinsTransport` codec (TCP+UDP, `0x0101`/`0x0102`, area-code table, word-swap) behind the same `IDriver` shell; memory-area TagConfig + editor fields (no browse) | Medium (net-new codec, no dep/license risk) | byte-level fake (CI) + OSS FINS sim container |
|
||||
| **P1 — Omron CIP r/w (NJ/NX)** | `Driver.Omron` + `.Contracts`, `DriverType="Omron"`, `OmronCipTransport` on libplctag `plc=omron-njnx`; Connect/Read/Write/Subscribe(poll)/Probe; authored + equipment tags; typed editor + validator; factory + registration | Low–Medium (70–80% AbCip reuse) | hand-rolled runtime fake (CI) + env-gated live gate (hardware; §9 checklist — string layout first) |
|
||||
| **P2 — Omron FINS r/w (CJ/CS/CP)** | `IFinsTransport` codec (TCP+UDP, `0x0101`/`0x0102`, area-code table, word-swap) behind the same `IDriver` shell; memory-area TagConfig + editor fields (no browse) | Medium (net-new codec, no dep/license risk) | byte-level fake (CI) + OSS FINS sim container + **FINS validation gate** (below) |
|
||||
|
||||
**P2 FINS validation gate (explicit):** every FINS wire specific in this design — the area-code
|
||||
table, the `0x0101`/`0x0102` command framing, the FINS/TCP node-address handshake, port 9600, and
|
||||
the bit-write behavior — is **research-sourced with no in-repo evidence**. The hand-rolled FINS
|
||||
framer is therefore **gated on validation against the Omron W227 FINS reference manual plus golden
|
||||
request/response vectors captured from live CJ/CS/CP hardware before it is trusted**; the captured
|
||||
vectors become the byte-level fake's fixtures (§9). Wire specifics stay parameterised per CPU
|
||||
family (the `FinsAreaCodeTable` keying already hedged in §5.3), so a manual/hardware discrepancy is
|
||||
a table correction, not a codec rewrite.
|
||||
| **P3 — Sysmac tag-export importer (CIP)** | AdminUI offline importer: parse Sysmac Studio variable export → author `OmronTagDefinition`s. **Not** a live browser. | Medium | parser unit tests on real export samples |
|
||||
|
||||
### Top risks
|
||||
1. **No free NJ/NX CIP simulator** (the headline). CIP wire correctness (string/array/UDT/
|
||||
Network-Publish) is provable only on live hardware behind the env-gated `LiveIntegration` suite →
|
||||
**infra-gated known limitation** until an NJ/NX is on the bench. The hand-rolled fake proves the
|
||||
driver *body*, not the *wire*.
|
||||
driver *body*, not the *wire* — and the NJ/NX-vs-Logix **string layout** is the likeliest wire
|
||||
surprise, so it is the *first* item on the P1 live-gate checklist (§9).
|
||||
2. **CIP browse is not free reuse.** libplctag's `@tags` walker returns `ErrorUnsupported` on Omron
|
||||
NJ/NX; `SupportsOnlineDiscovery=false`, the universal browser doesn't apply, and browse ships as
|
||||
an offline Sysmac-export importer (P3), with online browse deferred to a later research phase.
|
||||
3. **FINS per-family variance** in memory-area codes + multi-word ordering — contained by the
|
||||
CPU-family-parameterised `FinsAreaCodeTable` + per-tag `wordSwap`.
|
||||
3. **FINS wire specifics are research-sourced, not evidence-backed.** The area codes,
|
||||
`0x0101`/`0x0102` commands, FINS/TCP handshake, port 9600, and bit-write behavior have no
|
||||
in-repo precedent — the hand-rolled framer is gated on the P2 validation gate (W227 manual +
|
||||
golden vectors from live hardware) before it is trusted. Per-family variance in memory-area
|
||||
codes + multi-word ordering is contained by the CPU-family-parameterised `FinsAreaCodeTable` +
|
||||
per-tag `wordSwap`.
|
||||
4. **Enum-serialization trap** — every config JSON surface must attach `JsonStringEnumConverter`, or
|
||||
AdminUI-authored Omron configs fault the driver. Live-`/run` verify catches it; unit tests don't.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user