Files
lmxopcua/docs/Client.CLI.md
T
Joseph Doherty 6e1903b231 docs(r2-12): document 5 alarm operator CLI commands; fix StyleGuide product name; add minimal .editorconfig (07/U-3, 07/C-5)
- docs/Client.CLI.md: added ### ack / confirm / shelve / enable / disable sections
  (synopsis + options + example + expected output), each noting the AlarmAck LDAP-role
  gate and that --event-id comes from a prior `alarms` notification. Doc now covers all
  13 CLI commands (was 8). Overview operations list updated.
- StyleGuide.md: 'ScadaBridge' -> 'OtOpcUa' (header line 3) + the ScadaBridge:Timeout
  config-key example -> OpcUa:Timeout.
- .editorconfig (new, root): minimal code-style anchor; all C# analyzer rules at
  suggestion severity so nothing fails a TreatWarningsAsErrors build (verified: 808
  warnings unchanged from baseline, 0 errors).
2026-07-13 09:57:36 -04:00

377 lines
15 KiB
Markdown

# Client CLI
## Overview
`ZB.MOM.WW.OtOpcUa.Client.CLI` is a cross-platform command-line client for the OtOpcUa OPC UA server. It targets .NET 10 and uses the shared `IOpcUaClientService` from `Client.Shared` for all OPC UA operations. Commands are routed and parsed by [CliFx](https://github.com/Tyrrrz/CliFx).
The CLI is the primary tool for operators and developers to test and interact with the server from a terminal. It supports all core operations: connectivity testing, browsing, reading, writing, subscriptions, alarm monitoring, alarm operator actions (acknowledge / confirm / shelve / enable / disable), history reads, and redundancy queries. Any driver surface exposed by the server (Galaxy, Modbus, S7, AB CIP, AB Legacy, TwinCAT, FOCAS, OPC UA Client) is reachable through these commands — the CLI is driver-agnostic because everything below the OPC UA endpoint is.
## Build and Run
```bash
cd src/Client/ZB.MOM.WW.OtOpcUa.Client.CLI
dotnet build
dotnet run -- <command> [options]
```
The executable name is `otopcua-cli`. Dev boxes carrying a pre-task-#208 install may still have the legacy `{LocalAppData}/LmxOpcUaClient/` folder on disk; on first launch of any post-#208 CLI or UI build, `ClientStoragePaths` (`src/Client/ZB.MOM.WW.OtOpcUa.Client.Shared/ClientStoragePaths.cs`) migrates it to `{LocalAppData}/OtOpcUaClient/` automatically so trusted certificates + saved settings survive the rename.
## Architecture
All commands inherit from `CommandBase`, which provides common connection options and helper methods. Every command follows the same lifecycle:
1. Build `ConnectionSettings` from common options
2. Create an `IOpcUaClientService` through the factory
3. Call `ConnectAsync` to establish a session
4. Perform the command-specific operation
5. Call `DisconnectAsync` in a `finally` block
No command accesses the OPC UA `Session` directly; all operations go through the shared service abstraction. This ensures consistent behavior with the desktop UI client.
## Common Options
All commands accept these options:
| Flag | Description |
|------|-------------|
| `-u` / `--url` | OPC UA server endpoint URL (required) |
| `-U` / `--username` | Username for `UserName` token authentication |
| `-P` / `--password` | Password for `UserName` token authentication |
| `-S` / `--security` | Transport security mode: `none`, `sign`, `encrypt`, `signandencrypt` (default: `none`) |
| `-F` / `--failover-urls` | Comma-separated failover endpoint URLs for redundancy |
| `--verbose` | Enable debug-level Serilog console logging (default: warning) |
### Authentication
When `-U` and `-P` are provided, the shared service passes a `UserIdentity(username, password)` to the OPC UA session. Without credentials, anonymous identity is used.
```bash
otopcua-cli write -u opc.tcp://localhost:4840 -n "ns=2;s=MyNode" -v 42 -U operator -P op123
```
### Failover
When `-F` is provided, the shared service tries the primary URL first, then each failover URL in order. For long-running commands (`subscribe`, `alarms`), the service monitors the session via keep-alive and automatically reconnects to the next available server on failure.
```bash
otopcua-cli connect -u opc.tcp://localhost:4840/OtOpcUa -F opc.tcp://localhost:4841/OtOpcUa
```
### Transport Security
When `sign` or `encrypt` is specified, the shared service:
1. Ensures a client application certificate exists under `{LocalAppData}/OtOpcUaClient/pki/` (auto-created if missing; pre-rename `LmxOpcUaClient/` is migrated in place on first launch)
2. Discovers server endpoints and selects one matching the requested security mode
3. Prefers `Basic256Sha256` when multiple matching endpoints exist
4. Fails with a clear error if no matching endpoint is found
```bash
otopcua-cli browse -u opc.tcp://localhost:4840/OtOpcUa -S encrypt -U admin -P secret -r -d 2
```
### Verbose Logging
The `--verbose` flag switches Serilog output from `Warning` to `Debug` level, showing internal connection lifecycle, endpoint discovery, and OPC UA SDK diagnostics on the console.
## Commands
### connect
Tests connectivity to an OPC UA server. Creates a session, prints connection metadata, and disconnects.
```bash
otopcua-cli connect -u opc.tcp://localhost:4840/OtOpcUa -U admin -P admin123
```
Output:
```text
Connected to: opc.tcp://localhost:4840/OtOpcUa
Server: OtOpcUa Server
Security Mode: None
Security Policy: http://opcfoundation.org/UA/SecurityPolicy#None
Connection successful.
```
### read
Reads the current value of a single node and prints the value, status code, and timestamps.
```bash
otopcua-cli read -u opc.tcp://localhost:4840/OtOpcUa -n "ns=3;s=DEV.ScanState" -U admin -P admin123
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to read (required) |
Output:
```text
Node: ns=3;s=DEV.ScanState
Value: True
Status: 0x00000000
Source Time: 2026-03-30T19:58:38.0961252Z
Server Time: 2026-03-30T19:58:38.0971257Z
```
### write
Writes a value to a node. The shared service reads the current value first to determine the target data type, then converts the supplied string value using `ValueConverter.ConvertValue()`.
```bash
otopcua-cli write -u opc.tcp://localhost:4840 -n "ns=2;s=MyNode" -v 42
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to write to (required) |
| `-v` / `--value` | Value to write (required) |
### browse
Browses the OPC UA address space starting from the Objects folder or a specified node. Supports recursive traversal with a configurable depth limit. Output uses tree-style indentation with `[Object]`, `[Variable]`, and `[Method]` markers.
```bash
# Browse top-level Objects folder
otopcua-cli browse -u opc.tcp://localhost:4840/OtOpcUa -U admin -P admin123
# Browse a specific node recursively to depth 3
otopcua-cli browse -u opc.tcp://localhost:4840/OtOpcUa -U admin -P admin123 -r -d 3 -n "ns=3;s=ZB"
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to browse (default: Objects folder) |
| `-d` / `--depth` | Maximum browse depth (default: 1) |
| `-r` / `--recursive` | Browse recursively using `-d` as max depth |
### subscribe
Monitors a node (or every Variable in its subtree) for value changes using OPC UA subscriptions.
Prints each data-change notification with timestamp, value, and status code, then prints a
summary on exit. Exits on Ctrl+C, or automatically after `--duration` seconds.
```bash
# Subscribe to a single node
otopcua-cli subscribe -u opc.tcp://localhost:4840 -n "ns=2;s=MyNode" -i 500
# Browse a subtree and subscribe to every Variable, run for 60 seconds, write the summary to disk
otopcua-cli subscribe -u opc.tcp://localhost:4840 -n "ns=3;s=ZB" -r --max-depth 4 \
--duration 60 --quiet --summary-file C:\Temp\subscribe-summary.txt
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to monitor (required). When `--recursive` is set, this is the browse root. |
| `-i` / `--interval` | Sampling interval in milliseconds (default: 1000) |
| `-r` / `--recursive` | Browse recursively from `--node` and subscribe to every Variable found |
| `--max-depth` | Maximum recursion depth when `--recursive` is set (default: 10) |
| `-q` / `--quiet` | Suppress per-update output; only print the final summary |
| `--duration` | Auto-exit after N seconds and print the summary (0 = run until Ctrl+C, default: 0) |
| `--summary-file` | Also write the summary to this file path on exit |
#### Summary buckets
The summary prints per-node counts across these buckets:
- **Ever went BAD during window** — node received at least one notification whose status was not Good.
- **NEVER went bad (suspect)** — node received at least one notification and every one was Good.
- **Last status GOOD / NOT-GOOD** — final observed status across nodes that received any update.
- **No update received at all** — node was subscribed but no notification arrived during the window.
### historyread
Reads historical data from a node. Supports raw history reads and aggregate (processed) history reads.
`--start` and `--end` are parsed with `CultureInfo.InvariantCulture` and treated as UTC; supply
them in ISO 8601 UTC form (`YYYY-MM-DDTHH:MM:SSZ`) for unambiguous behaviour across hosts.
```bash
# Raw history
otopcua-cli historyread -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001.TestHistoryValue" \
--start "2026-03-25T00:00:00Z" --end "2026-03-30T00:00:00Z"
# Aggregate: 1-hour average
otopcua-cli historyread -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001.TestHistoryValue" \
--start "2026-03-25T00:00:00Z" --end "2026-03-30T00:00:00Z" \
--aggregate Average --interval 3600000
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to read history for (required) |
| `--start` | Start time in ISO 8601 UTC format, e.g. `2026-01-15T08:00:00Z` (default: 24 hours ago) |
| `--end` | End time in ISO 8601 UTC format, e.g. `2026-01-15T09:00:00Z` (default: now) |
| `--max` | Maximum number of values (default: 1000) |
| `--aggregate` | Aggregate function: Average, Minimum, Maximum, Count, Start, End, StandardDeviation |
| `--interval` | Processing interval in milliseconds for aggregates (default: 3600000) |
#### Aggregate mapping
| Name | Aliases | OPC UA Node ID |
|------|---------|---------------|
| `Average` | `avg` | `AggregateFunction_Average` |
| `Minimum` | `min` | `AggregateFunction_Minimum` |
| `Maximum` | `max` | `AggregateFunction_Maximum` |
| `Count` | | `AggregateFunction_Count` |
| `Start` | `first` | `AggregateFunction_Start` |
| `End` | `last` | `AggregateFunction_End` |
| `StandardDeviation` | `stddev`, `stdev` | `AggregateFunction_StandardDeviationPopulation` |
### alarms
Subscribes to alarm events on a node. Prints structured alarm output including source, condition, severity, active/acknowledged state, and message. Runs until Ctrl+C, then unsubscribes and disconnects cleanly.
```bash
# Subscribe to alarm events on the Server node
otopcua-cli alarms -u opc.tcp://localhost:4840/OtOpcUa
# Subscribe to a specific source node with condition refresh
otopcua-cli alarms -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" --refresh
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Node ID to monitor for events (default: Server node) |
| `-i` / `--interval` | Publishing interval in milliseconds (default: 1000) |
| `--refresh` | Request a `ConditionRefresh` after subscribing to get current retained alarm states |
### ack
Acknowledges an active alarm condition via the OPC UA Part 9 `Acknowledge` method. The server gates this on the **`AlarmAck`** LDAP role (fail-closed — a session without `AlarmAck` group membership gets `BadUserAccessDenied`; see [ScriptedAlarms.md](ScriptedAlarms.md) §"AlarmAck gate"). The `--event-id` is the hex-encoded `EventId` taken from a prior `alarms` subscription notification for the same condition.
```bash
otopcua-cli ack -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" -e 0A1B2C -c "Investigating"
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Condition node ID of the alarm to acknowledge (**required**) |
| `-e` / `--event-id` | `EventId` from the alarm notification, hex-encoded (e.g. `0A1B2C`) (**required**) |
| `-c` / `--comment` | Operator comment for the acknowledgment (optional) |
Example output:
```text
Acknowledge successful: ns=1;s=TestMachine_001
```
### confirm
Confirms an already-acknowledged alarm condition via the OPC UA Part 9 `Confirm` method — the second stage of two-stage acknowledgment. Same `AlarmAck` role gate and same `--event-id` source (a prior `alarms` notification) as `ack`.
```bash
otopcua-cli confirm -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" -e 0A1B2C -c "Root cause cleared"
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Condition node ID of the alarm to confirm (**required**) |
| `-e` / `--event-id` | `EventId` from the alarm notification, hex-encoded (**required**) |
| `-c` / `--comment` | Operator comment for the confirmation (optional) |
Example output:
```text
Confirm successful: ns=1;s=TestMachine_001
```
### shelve
Shelves or unshelves an active alarm condition via the OPC UA Part 9 `ShelvedStateMachine` (`OneShotShelve` / `TimedShelve` / `Unshelve`). Gated on the `AlarmAck` role. `--duration` is given in **seconds** (converted to milliseconds for the OPC UA call) and is required only for `--kind Timed`.
```bash
# One-shot shelve until the next clear
otopcua-cli shelve -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" -k OneShot
# Timed shelve for 300 seconds
otopcua-cli shelve -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" -k Timed -d 300
# Unshelve
otopcua-cli shelve -u opc.tcp://localhost:4840/OtOpcUa \
-n "ns=1;s=TestMachine_001" -k Unshelve
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Condition node ID of the alarm to shelve/unshelve (**required**) |
| `-k` / `--kind` | Shelve operation: `OneShot` \| `Timed` \| `Unshelve` (**required**) |
| `-d` / `--duration` | Shelving duration in **seconds** (must be > 0; **required for `--kind Timed`**) |
Example output:
```text
Timed successful: ns=1;s=TestMachine_001
```
### enable
Enables an alarm condition via the OPC UA Part 9 `ConditionType.Enable` method (resumes evaluation/reporting for a previously disabled condition). Gated on the `AlarmAck` role.
```bash
otopcua-cli enable -u opc.tcp://localhost:4840/OtOpcUa -n "ns=1;s=TestMachine_001"
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Condition node ID of the alarm to enable (**required**) |
Example output:
```text
Enable successful: ns=1;s=TestMachine_001
```
### disable
Disables an alarm condition via the OPC UA Part 9 `ConditionType.Disable` method (stops evaluation/reporting). Gated on the `AlarmAck` role.
```bash
otopcua-cli disable -u opc.tcp://localhost:4840/OtOpcUa -n "ns=1;s=TestMachine_001"
```
| Flag | Description |
|------|-------------|
| `-n` / `--node` | Condition node ID of the alarm to disable (**required**) |
Example output:
```text
Disable successful: ns=1;s=TestMachine_001
```
### redundancy
Reads the OPC UA redundancy state from a server: redundancy mode, service level, server URIs, and application URI.
```bash
otopcua-cli redundancy -u opc.tcp://localhost:4840/OtOpcUa -U admin -P admin123
```
Example output:
```text
Redundancy Mode: Warm
Service Level: 200
Server URIs:
- urn:localhost:OtOpcUa:instance1
- urn:localhost:OtOpcUa:instance2
Application URI: urn:localhost:OtOpcUa:instance1
```
## Testing
The Client CLI has 104 unit tests covering option parsing, service invocation, output formatting, and cleanup behavior:
```bash
dotnet test tests/Client/ZB.MOM.WW.OtOpcUa.Client.CLI.Tests
```