跳到主要内容

MethodBuilder — Method 调用流式构造器

概述

OPC UA Call 服务 (Part 4 §5.11) 要求把入参打包成 Variant[], 顺序与服务端 InputArguments 描述一一对应。手写 Variant[] 容易踩:

  • 顺序错位 (speedaccel 颠倒)
  • 类型错配 (服务端要 Int32, 客户端写了 Double)
  • 漏参 / 多参

MethodBuilder 提供链式 API, 一行串起 LoadInputArgs / Arg(name, value) / Invoke, 并自动按 .NET 类型选 Variant.Set*

对应规范段: Part 4 §5.11.2 (Call) / Part 5 §6.4 (Argument)。

API

方法类别读写说明
new MethodBuilder(ua, objectId, methodId)构造绑定到方法
.LoadInputArgs()链式从服务端读取 InputArguments 描述 (容错, 失败不抛)
.Arg(string name, object value)链式按参数名设值
.Arg(int index, object value)链式按索引设值
.Invoke()终端调用方法, 返回 (StatusCode, Variant[] outputs)
.InputArgs属性LoadInputArgs 后可见的参数清单

支持的 .NET 类型: bool / sbyte / byte / short / int / uint / long / ulong / float / double / string / DateTime

代码示例

using DarraOpcUa_Client;

using var ua = new DarraOpcUa("opc.tcp://localhost:4840");
ua.Connect();

// 1) 流式调用 - 按名设参 (推荐, 易读)
var (st, outs) = new MethodBuilder(ua, "ns=2;s=Robot", "ns=2;s=Robot.MoveTo")
.LoadInputArgs()
.Arg("x", 100.0)
.Arg("y", 200.0)
.Arg("speed", 50)
.Invoke();

if (st != StatusCode.Good)
Console.WriteLine($"MoveTo 失败: {st}");

// 2) 不读 InputArguments 描述, 按索引设
var (st2, outs2) = new MethodBuilder(ua, "ns=2;s=Calculator", "ns=2;s=Calculator.Add")
.Arg(0, 3.14)
.Arg(1, 2.71)
.Invoke();
Console.WriteLine($"Add → {outs2[0].AsDouble}"); // 5.85

// 3) 复用 builder 多次调用
var b = new MethodBuilder(ua, "ns=2;s=Pump", "ns=2;s=Pump.SetSpeed")
.LoadInputArgs();

foreach (var rpm in new[] { 100, 500, 1000, 1500 })
{
var (s, _) = b.Arg("rpm", rpm).Invoke();
if (s != StatusCode.Good) break;
Thread.Sleep(500);
}

与 Call 的对比

// 旧写法: 手撸 Variant[]
var inputs = new[] {
new Variant(100.0),
new Variant(200.0),
new Variant(50) // 整型? 长整型? 浮点?
};
var outs = ua.Call("ns=2;s=Robot", "ns=2;s=Robot.MoveTo", inputs);

// 新写法: MethodBuilder
var (st, outs) = new MethodBuilder(ua, "ns=2;s=Robot", "ns=2;s=Robot.MoveTo")
.LoadInputArgs()
.Arg("x", 100.0)
.Arg("y", 200.0)
.Arg("speed", 50)
.Invoke();

实现限制

  • LoadInputArgs() 当前依赖 SDK Stack 的 ExtensionObject 解码, 部分 Server 上 InputArgs 列表可能为空 (但 Arg(int, value) 仍可工作)
  • 不支持复杂结构入参 (StructureDefinition), 只支持基本类型 + 数组
  • Invoke 内部走 Call 而不是 CallSafe, 失败抛 OpcUaException. 想要 Safe 版本, 自己包 try/catch

最佳实践

  • 方法名按名设参 — 比按索引可读
  • Pre-flight LoadInputArgs — 调用前先 LoadInputArgs 验证方法存在 + 参数类型匹配
  • 复用 builder — 同一方法多次调用, 创建一个 builder 即可, 仅改 Arg
  • 不要在 UI 线程同步调Invoke 是 RPC, 用 await Task.Run(() => builder.Invoke())

跨语言对照

C#PythonJavaC++RustC
MethodBuilderMethodBuilderMethodBuilderMethodBuilderMethodBuilderDarraUa_MethodBuilder_*
.Arg(name, value).arg(name, value).arg(name, value).Arg(name, value).arg(name, value)DarraUa_MethodBuilder_ArgByName
.Invoke().invoke().invoke().Invoke().invoke()DarraUa_MethodBuilder_Invoke

下一步