MethodBuilder — Method 调用流式构造器
概述
OPC UA Call 服务 (Part 4 §5.11) 要求把入参打包成 Variant[], 顺序与服务端 InputArguments 描述一一对应。手写 Variant[] 容易踩:
- 顺序错位 (
speed和accel颠倒) - 类型错配 (服务端要
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# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| MethodBuilder | MethodBuilder | MethodBuilder | MethodBuilder | MethodBuilder | DarraUa_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 |