[opcuaclient] OpcUaClient — Method node mirroring + Call passthrough #361

Merged
dohertj2 merged 1 commits from auto/opcuaclient/9 into auto/driver-gaps 2026-04-25 20:55:11 -04:00
Owner

Summary

Method node mirroring + Call passthrough for OpcUaClient — new 9th capability interface.

  • Core.Abstractions/IMethodInvoker.cs (new) — capability interface with CallMethodAsync(objectNodeId, methodNodeId, inputs, ct) returning MethodCallResult(StatusCode, Outputs, InputArgumentResults).
  • IAddressSpaceBuilder — extended with default-no-op RegisterMethodNode(MirroredMethodNodeInfo) plus MirroredMethodNodeInfo and MethodArgumentInfo DTOs. Existing builders (Galaxy, Modbus, FOCAS, S7, TwinCAT, AbCip, AbLegacy, server-side DriverNodeManager) keep compiling unchanged.
  • OpcUaClientDriver:
    • Stopped filtering NodeClass.Method from browse pass.
    • For each method node, walks HasProperty references to find InputArguments / OutputArguments properties, reads them, decodes Argument ExtensionObjects into MethodArgumentInfo, projects via RegisterMethodNode.
    • Implements IMethodInvoker.CallMethodAsync — wraps inputs as Variants, dispatches via Session.CallAsync under the existing _gate, returns Bad codes verbatim (cascading quality), surfaces local faults as BadNodeIdInvalid / BadCommunicationError.

Server-side dispatch deferred per issue's explicit allowance. Wiring MethodNode.OnCallMethod inside DriverNodeManager requires materializing local MethodNode + ArgumentDefinition objects from MirroredMethodNodeInfo and routing the OPC UA Call callback to IMethodInvoker.CallMethodAsync. Tracked as follow-up.

Test plan

  • dotnet build — Driver + Core.Abstractions + tests — clean (0 / 0)
  • dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests160 / 160 passed (7 new in OpcUaClientMethodInvokerTests: capability surface, DTOs, lifecycle invariants, default-no-op back-compat for RegisterMethodNode)
  • dotnet test tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests37 / 37 passed
  • Integration tests — skipped (live UA server required)

🤖 Auto-generated by the Mode-B execution loop. Closes #281.

Closes #281

## Summary Method node mirroring + Call passthrough for OpcUaClient — new 9th capability interface. - **`Core.Abstractions/IMethodInvoker.cs`** (new) — capability interface with `CallMethodAsync(objectNodeId, methodNodeId, inputs, ct)` returning `MethodCallResult(StatusCode, Outputs, InputArgumentResults)`. - **`IAddressSpaceBuilder`** — extended with default-no-op `RegisterMethodNode(MirroredMethodNodeInfo)` plus `MirroredMethodNodeInfo` and `MethodArgumentInfo` DTOs. Existing builders (Galaxy, Modbus, FOCAS, S7, TwinCAT, AbCip, AbLegacy, server-side `DriverNodeManager`) keep compiling unchanged. - **`OpcUaClientDriver`**: - Stopped filtering `NodeClass.Method` from browse pass. - For each method node, walks `HasProperty` references to find `InputArguments` / `OutputArguments` properties, reads them, decodes `Argument` ExtensionObjects into `MethodArgumentInfo`, projects via `RegisterMethodNode`. - Implements `IMethodInvoker.CallMethodAsync` — wraps inputs as `Variant`s, dispatches via `Session.CallAsync` under the existing `_gate`, returns Bad codes verbatim (cascading quality), surfaces local faults as `BadNodeIdInvalid` / `BadCommunicationError`. > **Server-side dispatch deferred** per issue's explicit allowance. Wiring `MethodNode.OnCallMethod` inside `DriverNodeManager` requires materializing local `MethodNode` + `ArgumentDefinition` objects from `MirroredMethodNodeInfo` and routing the OPC UA `Call` callback to `IMethodInvoker.CallMethodAsync`. Tracked as follow-up. ## Test plan - [x] `dotnet build` — Driver + Core.Abstractions + tests — clean (0 / 0) - [x] `dotnet test tests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests` — **160 / 160 passed** (7 new in `OpcUaClientMethodInvokerTests`: capability surface, DTOs, lifecycle invariants, default-no-op back-compat for `RegisterMethodNode`) - [x] `dotnet test tests/ZB.MOM.WW.OtOpcUa.Core.Abstractions.Tests` — **37 / 37 passed** - [ ] Integration tests — skipped (live UA server required) 🤖 Auto-generated by the Mode-B execution loop. Closes #281. Closes #281
dohertj2 added 1 commit 2026-04-25 20:55:07 -04:00
Adds the 9th capability interface (IMethodInvoker) so the OPC UA Client
driver can mirror upstream OPC UA Method nodes into the local address
space and forward Call invocations as Session.CallAsync. Method-bearing
servers (e.g. ProgramStateMachine, Acknowledge / Confirm methods, custom
control surfaces) now show up downstream instead of being silently
filtered out.

- Core.Abstractions: IMethodInvoker + MethodCallResult; default no-op
  IAddressSpaceBuilder.RegisterMethodNode + MirroredMethodNodeInfo +
  MethodArgumentInfo. Default impls keep tag-based drivers and existing
  builders compiling without forced overrides.
- OpcUaClientDriver: BrowseRecursiveAsync now lifts the Method node-class
  filter; for each method it walks HasProperty to pick up InputArguments
  + OutputArguments and decodes the Argument extension objects into
  MethodArgumentInfo. Status-codes pass through verbatim (cascading
  quality), local NodeId-parse + lost-session faults surface as
  BadNodeIdInvalid / BadCommunicationError.
- 7 new unit tests cover the capability surface, the DTO shapes, and the
  back-compat default no-op for RegisterMethodNode. Suite green at
  160/160.

Server-side OnCallMethod dispatch (wiring local MethodNode handlers to
IMethodInvoker.CallMethodAsync inside DriverNodeManager) is deferred to
a follow-up — the driver-side surface + browse mirror ship cleanly here.

Closes #281

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 69d9a6fbb5 into auto/driver-gaps 2026-04-25 20:55:11 -04:00
dohertj2 deleted branch auto/opcuaclient/9 2026-04-25 20:55:11 -04:00
Sign in to join this conversation.