Files
lmxopcua/docs/drivers/OpcUaClient.md
2026-04-26 00:24:24 -04:00

3.2 KiB

OPC UA Client driver

Tier-A in-process driver that opens a Session against a remote OPC UA server and re-exposes its address space through the local OtOpcUa server. The "gateway / aggregation" direction — opposite to the usual "server exposes PLC data" flow.

For the test fixture (opc-plc) see OpcUaClient-Test-Fixture.md. For the configuration surface see OpcUaClientDriverOptions in src/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient/OpcUaClientDriverOptions.cs.

Auto re-import on ModelChangeEvent

The driver subscribes to BaseModelChangeEventType (and its subtype GeneralModelChangeEventType) on the upstream Server node (i=2253) at the end of InitializeAsync. When the upstream server advertises a topology change, the driver coalesces events over a debounce window and runs a single re-import (equivalent to calling ReinitializeAsync — internally ShutdownAsync + InitializeAsync).

Configuration

Option Default Notes
WatchModelChanges true Disable to skip the watch entirely (no extra subscription, no re-import on topology change).
ModelChangeDebounce 5s Coalescing window. The first event starts the timer; further events extend it; when it elapses with no new events, the driver fires one re-import.

Behaviour

  • One model-change subscription per driver instance, separate from the data + alarm subscriptions. Created best-effort: a server that doesn't advertise the event types or rejects the EventFilter falls through to no-watch — InitializeAsync still succeeds.
  • The EventFilter selects only the EventType field (a WhereClause constrains by OfType BaseModelChangeEventType). Payload fields like Changes[] are intentionally ignored: the driver always re-imports the full upstream root, so per-event delta tracking would just add wire overhead.
  • Debounce is implemented via a single-shot Timer; every event calls Timer.Change(window, Infinite) so a burst of N events triggers exactly one re-import after the window elapses with no further events.
  • The re-import path acquires the same _gate semaphore that ReadAsync / WriteAsync / BrowseAsync / SubscribeAsync use. Downstream callers see a brief browse-gap (≈ the upstream DiscoverAsync duration) while the gate is held — but no torn reads or split-batch writes.
  • Failure during the re-import is best-effort: the next ModelChangeEvent triggers another attempt, and the keep-alive watchdog covers permanent upstream loss. Operators see failures through DriverHealth.LastError
    • the diagnostics counters.

When to disable

Flip WatchModelChanges to false when:

  • The upstream topology is known-static (e.g. firmware-pinned PLC) and the driver should never run a re-import unprompted.
  • The brief browse-gap during re-import is unacceptable and a manual ReinitializeAsync call from the operator is preferred.
  • The upstream server fires spurious ModelChangeEvents that don't reflect real topology changes, causing wasted re-imports. Tighten or disable rather than chasing the noise downstream.