# Driver test-client CLIs Five shell-level ad-hoc validation tools, one per native-protocol driver family. Each mirrors the v1 `otopcua-cli` shape (probe / read / write / subscribe) against the **same driver** the OtOpcUa server uses — so "does the CLI see it?" and "does the server see it?" are the same question. | CLI | Protocol | Docs | |---|---|---| | `otopcua-modbus-cli` | Modbus-TCP | [Driver.Modbus.Cli.md](Driver.Modbus.Cli.md) | | `otopcua-abcip-cli` | CIP / EtherNet-IP (Logix symbolic) | [Driver.AbCip.Cli.md](Driver.AbCip.Cli.md) | | `otopcua-ablegacy-cli` | PCCC (SLC / MicroLogix / PLC-5) | [Driver.AbLegacy.Cli.md](Driver.AbLegacy.Cli.md) | | `otopcua-s7-cli` | S7comm / ISO-on-TCP | [Driver.S7.Cli.md](Driver.S7.Cli.md) | | `otopcua-twincat-cli` | Beckhoff ADS | [Driver.TwinCAT.Cli.md](Driver.TwinCAT.Cli.md) | The OPC UA client CLI lives separately and predates this suite — see [Client.CLI.md](Client.CLI.md) for `otopcua-cli`. ## Shared commands Every driver CLI exposes the same four verbs: - **`probe`** — open a session, read one sentinel tag, print driver health. Fastest "is the device talking?" check. - **`read`** — synthesise a one-tag driver config from `--type` / `--address` (or `--tag` / `--symbol`) flags, read once, print the snapshot. No extra config file needed. - **`write`** — same shape plus `--value`. Values parse per `--type` using invariant culture. Booleans accept `true` / `false` / `1` / `0` / `yes` / `no` / `on` / `off`. Writes are **non-idempotent by default** — a timeout after the device already applied the write will not auto-retry (plan decisions #44, #45). - **`subscribe`** — long-running data-change stream until Ctrl+C. Uses native push where available (TwinCAT ADS notifications) and falls back to polling (`PollGroupEngine`) where the protocol has no push (Modbus, AB, S7). ## Shared infrastructure All five CLIs depend on `src/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/`: - `DriverCommandBase` — `--verbose` + Serilog configuration + the abstract `Timeout` surface every protocol-specific base overrides with its own default. - `SnapshotFormatter` — consistent output across every CLI: tag / value / status / source-time / server-time for single reads, a 4-column table for batches, `Write : 0x... (Name)` for writes, and one line per change event for subscriptions. OPC UA status codes render as `0xXXXXXXXX (Name)` with a shortlist for `Good` / `Bad*` / `Uncertain`; unknown codes fall back to hex. Writing a sixth CLI (hypothetical Galaxy / FOCAS) costs roughly 150 lines: a `{Family}CommandBase` + four thin command classes that hand their flag values to the already-shipped driver. ## Typical cross-CLI workflows - **Commissioning a new device** — `probe` first, then `read` a known-good tag. If the device is up + talking the protocol, both pass; if the tag is wrong you'll see the read fail with a protocol-specific error. - **Reproducing a production bug** — `subscribe` to the tag the bug report names, then have the operator run the scenario. You get an HH:mm:ss.fff timeline of exactly when each value changed. - **Validating a recipe write** — `write` + `read` back. If the server's write path would have done anything different, the CLI would have too. - **Byte-order / word-swap debugging** — `read` with one `--byte-order`, then the other. The plausible result identifies the correct setting for that device family. (Modbus, S7.) ## Known gaps - **AB Legacy PCCC wire-level** against the ab_server Docker simulator is upstream-broken — see the "Known limitations" section in [Driver.AbLegacy.Cli.md](Driver.AbLegacy.Cli.md). Pointing the CLI at real SLC / MicroLogix / PLC-5 hardware or a RSEmulate 500 golden-box works as expected. - **S7 PUT/GET communication** must be enabled in TIA Portal for any S7-1200/1500. See [Driver.S7.Cli.md](Driver.S7.Cli.md). - **TwinCAT AMS router** must be reachable (local XAR, standalone Router NuGet, or authorised remote route). See [Driver.TwinCAT.Cli.md](Driver.TwinCAT.Cli.md). - **Structure / UDT writes** are refused by the AB CIP + TwinCAT CLIs — whole-UDT writes need a declared member layout that belongs in a real driver config, not a one-shot flag. ## Tracking Tasks #249 / #250 / #251 shipped the suite. 122 unit tests cumulative (16 shared-lib + 106 across the five CLIs) — run `dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common.Tests` + `tests/ZB.MOM.WW.OtOpcUa.Driver.*.Cli.Tests` to re-verify.