Per-file summary: - docs/reqs/OpcUaServerReqs.md — rewritten driver-agnostic. OPC-001..OPC-013 re-scoped to multi-driver address-space composition + capability dispatch; OPC-014 AuthorizationGate + permission trie; OPC-015 dynamic ServiceLevel via RedundancyCoordinator; OPC-017 surgical generation-apply rebuild; OPC-012 capability dispatch via CapabilityInvoker (decision #143 idempotence-aware retry); OPC-013 per-host Polly isolation (decision #144); OPC-019 OpenTelemetry metrics. Transport-security profile matrix (OPC-010) + UserName/LDAP (OPC-011) preserved. - docs/reqs/GalaxyRepositoryReqs.md — scope clarified as Galaxy-driver-only (not platform). GR-001..GR-004 tied to ITagDiscovery.DiscoverAsync + IRediscoverable; all SQL runs inside OtOpcUa.Galaxy.Host and streams to Proxy via named pipe. GR-008 capability wrapping via CapabilityInvoker added. Cross-links to docs/v2/driver-specs.md + docs/GalaxyRepository.md. - docs/reqs/MxAccessClientReqs.md — scope clarified as Galaxy-Host-only. MXA-001..MXA-009 preserved (STA pump, register/unregister, subscription refcount, auto-reconnect, probe, COM cleanup, operation metrics, error translation). MXA-010 Proxy-side capability wrapping + MXA-011 pipe ACL + per-process shared secret (OTOPCUA_ALLOWED_SID / OTOPCUA_GALAXY_SECRET) added. - docs/reqs/ServiceHostReqs.md — rewritten for three-process deployment. Shared section (SVC-SHARED-001/002) for Serilog + bootstrap-only appsettings. SRV-* for OtOpcUa.Server (net10 x64, Microsoft.Extensions.Hosting + AddWindowsService, in-process driver hosting, redundancy-node bootstrap). ADM-* for OtOpcUa.Admin (Blazor Server, cookie+LDAP auth, CanEdit/CanPublish policies, sole DB writer, Prometheus /metrics, audit logging). GHX-* for OtOpcUa.Galaxy.Host (TopShelf, net48 x86, named-pipe IPC bootstrap, STA backend lifecycle, crash handling tied to supervisor). - docs/reqs/ClientRequirements.md — restructured as numbered, verifiable requirements. SHR-* for Client.Shared (single IOpcUaClientService, ConnectionSettings, failover, cross-platform certs, type-coercing write, UI-thread neutrality). CLI-001..CLI-011 cover connect/read/write/browse/subscribe/historyread/alarms/redundancy. UI-001..UI-008 cover connection panel, tree browser, each tab, connection-state reflection, cross-platform build. Reference design content (IOpcUaClientService shape, models, view-model map, mock layout) preserved. - docs/reqs/StatusDashboardReqs.md — retired cleanly. Replaced with a pointer to docs/v2/admin-ui.md + HLR-015 / HLR-016 / HLR-017 / ADM-*. Mapping table shows each retired DASH-001..DASH-009 requirement's replacement (live cluster-node view via SignalR, Prometheus metrics, driver-instance detail views, etc.). Note that a formal AdminUiReqs.md can be written later if needed for cert compliance. HighLevelReqs.md was already at the target shape (HLR-001..HLR-018 with Revision header noting retired HLR-009) as of commit f217636; verified identical and no additional edit required. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
12 KiB
OPC UA Client Requirements
Revision — Refreshed 2026-04-19 for the OtOpcUa v2 multi-driver platform (task #205). The Client surface (shared library + CLI + UI) shipped for v2 is preserved; this refresh restructures the document into numbered, directly-verifiable requirements (CLI-* and UI-* prefixes) layered on top of the existing detailed design content. Requirement coverage added for the
redundancycommand, alarm subscribe/ack round-trip, history-read, and UI tree-browser drag-to-subscribe behaviors. Original design-spec material forConnectionSettings,IOpcUaClientService, models, and view-models is retained as reference-level details below the numbered requirements.
Parent: HLR-001, HLR-009, HLR-013
See also: docs/Client.CLI.md, docs/Client.UI.md.
Projects
| Project | Type | Purpose |
|---|---|---|
ZB.MOM.WW.OtOpcUa.Client.Shared |
Class library | Core OPC UA client, models, interfaces |
ZB.MOM.WW.OtOpcUa.Client.CLI |
Console app | Command-line interface using CliFx |
ZB.MOM.WW.OtOpcUa.Client.UI |
Avalonia app | Desktop UI |
ZB.MOM.WW.OtOpcUa.Client.Shared.Tests |
Test project | Shared-library unit tests |
ZB.MOM.WW.OtOpcUa.Client.CLI.Tests |
Test project | CLI command tests |
ZB.MOM.WW.OtOpcUa.Client.UI.Tests |
Test project | ViewModel unit tests |
Shared Requirements (Client.Shared)
SHR-001: Single Service Interface
The Client.Shared library shall expose a single service interface IOpcUaClientService covering connect, disconnect, read, write, browse, subscribe, alarm-subscribe, alarm-ack, history-read-raw, history-read-aggregate, and get-redundancy-info operations.
SHR-002: ConnectionSettings Model
The library shall expose a ConnectionSettings record with the fields: EndpointUrl (required), FailoverUrls[], Username, Password, SecurityMode (None/Sign/SignAndEncrypt; default None), SessionTimeoutSeconds (default 60), AutoAcceptCertificates (default true), CertificateStorePath.
SHR-003: Automatic Failover
The library shall monitor session keep-alive and automatically fail over across FailoverUrls when the primary endpoint is unreachable, emitting a ConnectionStateChanged event on each transition (Disconnected / Connecting / Connected / Reconnecting).
SHR-004: Cross-Platform Certificate Store
The library shall auto-generate a client certificate on first use and store it in a cross-platform path (default {AppData}/OtOpcUaClient/pki/). Server certificates are auto-accepted when AutoAcceptCertificates = true.
SHR-005: Type-Coercing Write
The library's WriteValueAsync(NodeId, object) shall read the node's current value to determine target type and coerce the input value before sending.
SHR-006: UI-Thread Dispatch Neutrality
The library shall not assume any specific synchronization context. Events (DataChanged, AlarmEvent, ConnectionStateChanged) are raised on the OPC UA stack thread; the consuming CLI / UI is responsible for dispatching to its UI thread.
CLI Requirements (Client.CLI)
CLI-001: Command Surface
The CLI shall expose the following commands: connect, read, write, browse, subscribe, historyread, alarms, redundancy.
CLI-002: Common Options
All CLI commands shall accept the options -u, --url (required), -U, --username, -P, --password, -S, --security none|sign|encrypt, -F, --failover-urls (comma-separated), --verbose.
CLI-003: Connect Command
The connect command shall attempt to establish a session using the supplied options and print Connected plus the resolved endpoint's ServerUriArray and ApplicationUri on success, or a diagnostic error message on failure.
CLI-004: Read Command
The read -n <NodeId> command shall print NodeId, Value, StatusCode, SourceTimestamp, ServerTimestamp one per line.
CLI-005: Write Command
The write -n <NodeId> -v <value> command shall coerce the value to the node's current type (per SHR-005) and print the resulting StatusCode. A Bad_UserAccessDenied result is printed verbatim so operators see the authorization outcome.
CLI-006: Browse Command
The browse [-n <parent>] [-r] [-d <depth>] command shall list child nodes under parent (or the Objects folder if omitted). -r enables recursion up to -d depth (default 1).
CLI-007: Subscribe Command
The subscribe -n <NodeId> -i <intervalMs> command shall create a monitored item at intervalMs publishing interval, print each DataChanged event as <timestamp> <nodeId> <value> <status> until Ctrl-C, then cleanly unsubscribe.
CLI-008: Historyread Command
The historyread -n <NodeId> --start <utc> --end <utc> [--max <n>] [--aggregate <type> --interval <ms>] command shall print raw values or aggregate buckets. Supported aggregate types: Average, Minimum, Maximum, Count, Start, End.
CLI-009: Alarms Command
The alarms [-n <source>] [-i <intervalMs>] command shall subscribe to alarm events, print each event as <time> <source> <condition> <severity> <state> <acked> <message>, accept ack <conditionId> commands interactively, and support refresh to trigger RequestConditionRefreshAsync.
CLI-010: Redundancy Command
The redundancy command shall call GetRedundancyInfoAsync and print Mode, ServiceLevel, ApplicationUri, and ServerUris (one per line). Suitable for redundancy-failover smoke tests.
CLI-011: Logging
The CLI shall use Serilog console sink at Warning minimum by default; --verbose raises to Debug.
UI Requirements (Client.UI)
UI-001: Connection Panel
The UI shall present a top-bar connection panel with fields for Endpoint URL, Username, Password, Security mode, and a Connect / Disconnect button. The resolved RedundancyInfo is displayed next to the bar on successful connect.
UI-002: Tree Browser
The UI shall present a left-pane tree browser backed by IOpcUaClientService.BrowseAsync, lazy-loading children on node expansion (one level per BrowseAsync call).
UI-003: Read/Write Tab
The UI shall provide a Read/Write tab that auto-reads the selected tree node's current value, displays Value + StatusCode + SourceTimestamp, and accepts a write value with a Send button.
UI-004: Subscriptions Tab
The UI shall provide a Subscriptions tab that lists active monitored items (columns: NodeId, Value, Status, Timestamp), supports Add and Remove, and dispatches DataChanged events to the Avalonia UI thread via Dispatcher.UIThread.Post.
UI-005: Alarms Tab
The UI shall provide an Alarms tab that supports SubscribeAlarms / UnsubscribeAlarms / RefreshConditions commands, displays live alarm events, and supports Acknowledge on selected events. Acknowledgment failure (including Bad_UserAccessDenied) is surfaced to the user.
UI-006: History Tab
The UI shall provide a History tab with inputs for StartTime, EndTime, MaxValues, AggregateType, Interval, a Read command, and a results table with columns (Timestamp, Value, Status).
UI-007: Connection State Reflects in UI
All tabs shall reflect the connection state — when disconnected, all action commands are disabled; the status bar shows Disconnected / Connecting / Connected / Reconnecting tied to the ConnectionStateChanged event.
UI-008: Cross-Platform
The UI shall build and run on Windows (win-x64) and macOS (osx-arm64 / osx-x64). No platform-specific OPC UA stack APIs are used.
Technology Stack
- .NET 10, C#
- OPC UA:
OPCFoundation.NetStandard.Opc.Ua.Client - Logging: Serilog
- CLI: CliFx
- UI: Avalonia 11.x with CommunityToolkit.Mvvm
- Tests: xUnit 3, Shouldly, Microsoft.Testing.Platform runner
Client.Shared — Design Detail
IOpcUaClientService Interface (reference)
Lifecycle: ConnectAsync(ConnectionSettings), DisconnectAsync(), IsConnected.
Read/Write: ReadValueAsync(NodeId), WriteValueAsync(NodeId, object).
Browse: BrowseAsync(NodeId? parent) → BrowseResult[] (NodeId, DisplayName, NodeClass, HasChildren); lazy-load compatible.
Subscribe: SubscribeAsync(NodeId, int intervalMs), UnsubscribeAsync(NodeId), event DataChanged(NodeId, DataValue).
Alarms: SubscribeAlarmsAsync(NodeId? source, int intervalMs), UnsubscribeAlarmsAsync(), AcknowledgeAsync(conditionId, comment), RequestConditionRefreshAsync(), event AlarmEvent(AlarmEventArgs).
History: HistoryReadRawAsync(NodeId, start, end, maxValues), HistoryReadAggregateAsync(NodeId, start, end, AggregateType, intervalMs).
Redundancy: GetRedundancyInfoAsync() → RedundancyInfo (Mode, ServiceLevel, ServerUris, ApplicationUri).
Models
BrowseResult— NodeId, DisplayName, NodeClass, HasChildrenAlarmEventArgs— SourceName, ConditionName, Severity, Message, Retain, ActiveState, AckedState, TimeRedundancyInfo— Mode, ServiceLevel, ServerUris, ApplicationUriConnectionState— enum (Disconnected, Connecting, Connected, Reconnecting)AggregateType— enum (Average, Minimum, Maximum, Count, Start, End)
Client.UI — View Layout (reference)
Single-window Avalonia application:
┌─────────────────────────────────────────────────────────┐
│ [Endpoint URL] [User] [Pass] [Security▼] [Connect] │
│ Redundancy: Mode=Warm ServiceLevel=200 AppUri=... │
├──────────────┬──────────────────────────────────────────┤
│ │ ┌Read/Write┬Subscriptions┬Alarms┬History┐│
│ Address │ │ Node: ns=3;s=Tag.Attr ││
│ Space │ │ Value: 42.5 Status: Good ││
│ Tree │ │ [Write: ____] [Send] ││
│ Browser │ └───────────────────────────────────────┘│
├──────────────┴──────────────────────────────────────────┤
│ Status: Connected | Session: abc123 | 3 subscriptions │
└─────────────────────────────────────────────────────────┘
ViewModels (CommunityToolkit.Mvvm)
MainWindowViewModel— connection fields, connect/disconnect commands,ConnectionState,RedundancyInfo,SelectedTreeNode,StatusMessage.BrowseTreeViewModel— root collection (ObservableCollection<TreeNodeViewModel>), lazy-load on expand.ReadWriteViewModel— auto-read on selection,WriteValue+WriteCommand.SubscriptionsViewModel—ActiveSubscriptions,AddSubscription,RemoveSubscription, liveDataChangeddispatch to UI thread.AlarmsViewModel—AlarmEvents, Subscribe / Unsubscribe / Refresh / Acknowledge commands.HistoryViewModel—StartTime,EndTime,MaxValues,AggregateType,Interval,ReadCommand,Results.
Test Projects
Client.Shared.Tests
ConnectionSettingsvalidation- Type conversion
BrowseResult/AlarmEventArgs/RedundancyInfomodel construction- FailoverUrl parsing
Client.CLI.Tests
- Command option parsing (via CliFx test infrastructure)
- Output formatting for each command
Client.UI.Tests
- ViewModel property-change notifications
- Command
CanExecutelogic - Tree lazy-load behavior (with mocked
IOpcUaClientService)
Test Framework
- xUnit 3 with Microsoft.Testing.Platform runner
- Shouldly
- No live OPC UA server required — mock
IOpcUaClientServicefor unit tests