Phase 3 PR 66 -- OPC UA Client (gateway) driver scaffold #65

Merged
dohertj2 merged 1 commits from phase-3-pr66-opcua-client-scaffold into v2 2026-04-19 01:10:09 -04:00
Owner

Summary

First driver that consumes OPC UA rather than publishes it — opens a Session to a remote OPC UA server via DefaultSessionFactory + re-exposes the remote address space through the local OtOpcUa server. Matches driver-specs.md §8.

  • OpcUaClientDriverOptions — EndpointUrl (default opc.tcp://localhost:4840), SecurityPolicy/SecurityMode/AuthType, Username/Password, SessionTimeout=120s / KeepAliveInterval=5s / ReconnectPeriod=5s, AutoAcceptCertificates=false (production default).
  • OpcUaClientDriver : IDriver — builds its own ApplicationConfiguration (cert stores under %LocalAppData%/OtOpcUa/pki), selects endpoint via CoreClientUtils.SelectEndpointAsync, connects via DefaultSessionFactory.CreateAsync.
  • Session + Gate exposed internally to the test project so PRs 67-69 can stack read/write/discovery/subscribe on the same serialization.

SDK 1.5.378 gotchas navigated

  • All Session.Create* statics are [Obsolete]; must use new DefaultSessionFactory(...) instance.
  • SelectEndpointAsync requires ITelemetryContext (passing null is tolerated — SDK falls back to default sink).
  • UserIdentity(string, string) gone in favour of (string, byte[]) with explicit encoding.
  • ApplicationInstance() default ctor obsolete — wrapped in #pragma warning disable CS0618.

Validation

  • 5/5 scaffold tests pass (8s total — uses opc.tcp://127.0.0.1:1 for the unreachable-endpoint path because port 1 refuses immediately with RST, while RFC 5737 reserved IPs get black-holed and only surface after the SDK's internal retry burns ~60s).
  • dotnet build: 0 errors

Scope

ITagDiscovery / IReadable / IWritable / ISubscribable / IHostConnectivityProbe deliberately NOT here — land in PRs 67-69.

Test plan

  • Default options (4840, None, Anonymous, production-safe cert default)
  • Default timeouts match spec §8
  • DriverType/DriverInstanceId shape + pre-init Unknown health
  • Initialize against unreachable endpoint → Faulted + throws
  • Reinitialize re-throws cleanly
## Summary First driver that **consumes** OPC UA rather than publishes it — opens a `Session` to a remote OPC UA server via `DefaultSessionFactory` + re-exposes the remote address space through the local OtOpcUa server. Matches `driver-specs.md` §8. - `OpcUaClientDriverOptions` — EndpointUrl (default `opc.tcp://localhost:4840`), `SecurityPolicy`/`SecurityMode`/`AuthType`, Username/Password, SessionTimeout=120s / KeepAliveInterval=5s / ReconnectPeriod=5s, `AutoAcceptCertificates=false` (production default). - `OpcUaClientDriver : IDriver` — builds its own `ApplicationConfiguration` (cert stores under `%LocalAppData%/OtOpcUa/pki`), selects endpoint via `CoreClientUtils.SelectEndpointAsync`, connects via `DefaultSessionFactory.CreateAsync`. - Session + Gate exposed internally to the test project so PRs 67-69 can stack read/write/discovery/subscribe on the same serialization. ## SDK 1.5.378 gotchas navigated - All `Session.Create*` statics are `[Obsolete]`; must use `new DefaultSessionFactory(...)` instance. - `SelectEndpointAsync` requires `ITelemetryContext` (passing null is tolerated — SDK falls back to default sink). - `UserIdentity(string, string)` gone in favour of `(string, byte[])` with explicit encoding. - `ApplicationInstance()` default ctor obsolete — wrapped in `#pragma warning disable CS0618`. ## Validation - 5/5 scaffold tests pass (8s total — uses `opc.tcp://127.0.0.1:1` for the unreachable-endpoint path because port 1 refuses immediately with RST, while RFC 5737 reserved IPs get black-holed and only surface after the SDK's internal retry burns ~60s). - `dotnet build`: 0 errors ## Scope ITagDiscovery / IReadable / IWritable / ISubscribable / IHostConnectivityProbe deliberately NOT here — land in PRs 67-69. ## Test plan - [x] Default options (4840, None, Anonymous, production-safe cert default) - [x] Default timeouts match spec §8 - [x] DriverType/DriverInstanceId shape + pre-init Unknown health - [x] Initialize against unreachable endpoint → Faulted + throws - [x] Reinitialize re-throws cleanly
dohertj2 added 1 commit 2026-04-19 01:10:05 -04:00
Phase 3 PR 66 -- OPC UA Client (gateway) driver project scaffold + IDriver session lifecycle. First driver that CONSUMES OPC UA rather than PUBLISHES it -- connects to a remote server and re-exposes its address space through the local OtOpcUa server per driver-specs.md \u00A78. Uses the same OPCFoundation.NetStandard.Opc.Ua.Client package the existing Client.Shared ships (bumped to 1.5.378.106 to match). Builds its own ApplicationConfiguration (cert stores under %LocalAppData%/OtOpcUa/pki so multiple driver instances in one OtOpcUa server process share a trust anchor) rather than reusing Client.Shared -- Client.Shared is oriented at the interactive CLI with different session-lifetime needs (this driver is always-on, needs keep-alive + session transfer on reconnect + multi-year uptime). Navigated the post-refactor 1.5.378 SDK surface: every Session.Create* static is now [Obsolete] in favour of DefaultSessionFactory; CoreClientUtils.SelectEndpoint got the sync overloads deprecated in favour of SelectEndpointAsync with a required ITelemetryContext parameter. Driver passes telemetry: null! to both SelectEndpointAsync + new DefaultSessionFactory(telemetry: null!) -- the SDK's internal default sink handles null gracefully and plumbing a telemetry context through the driver options surface is out of scope (the driver emits its own logs via the DriverHealth surface anyway). ApplicationInstance default ctor is also obsolete; wrapped in #pragma warning disable CS0618 rather than migrate to the ITelemetryContext overload for the same reason. OpcUaClientDriverOptions models driver-specs.md \u00A78 settings: EndpointUrl (default opc.tcp://localhost:4840 IANA-assigned port), SecurityPolicy/SecurityMode/AuthType enums, Username/Password, SessionTimeout=120s + KeepAliveInterval=5s + ReconnectPeriod=5s (defaults from spec), AutoAcceptCertificates=false (production default; dev turns on for self-signed servers), ApplicationUri + SessionName knobs for certificate SAN matching and remote-server session-list identification. OpcUaClientDriver : IDriver: InitializeAsync builds the ApplicationConfiguration, resolves + creates cert if missing via app.CheckApplicationInstanceCertificatesAsync, selects endpoint via CoreClientUtils.SelectEndpointAsync, builds UserIdentity (Anonymous or Username with UTF-8-encoded password bytes -- the legacy string-password ctor went away; Certificate auth deferred), creates session via DefaultSessionFactory.CreateAsync. Health transitions Unknown -> Initializing -> Healthy on success or -> Faulted on failure with best-effort Session.CloseAsync cleanup. ShutdownAsync (async now, not Task.CompletedTask) closes the session + disposes. Internal Session + Gate expose to the test project via InternalsVisibleTo so PRs 67-69 can stack read/write/discovery/subscribe on the same serialization. Scaffold tests (OpcUaClientDriverScaffoldTests, 5 facts): Default_options_target_standard_opcua_port_and_anonymous_auth (4840 + None mode + Anonymous + AutoAccept=false production default), Default_timeouts_match_driver_specs_section_8 (120s/5s/5s), Driver_reports_type_and_id_before_connect (DriverType=OpcUaClient, DriverInstanceId round-trip, pre-init Unknown health), Initialize_against_unreachable_endpoint_transitions_to_Faulted_and_throws, Reinitialize_against_unreachable_endpoint_re_throws. Uses opc.tcp://127.0.0.1:1 as the 'guaranteed-unreachable' target -- RFC 5737 reserved IPs get black-holed and time out only after the SDK's internal retry/backoff fully elapses (~60s), while port 1 on loopback refuses immediately with TCP RST which keeps the test suite snappy (5 tests / 8s). 5/5 pass. dotnet build clean. Scope boundary: ITagDiscovery / IReadable / IWritable / ISubscribable / IHostConnectivityProbe deliberately NOT in this PR -- they need browse + namespace remapping + reference-counted MonitoredItem forwarding + keep-alive probing and land in PRs 67-69. 91eaf534c8
dohertj2 merged commit b21d550836 into v2 2026-04-19 01:10:09 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#65