Call
签名
pub fn Session::call(
&self,
object_node_id: &str,
method_node_id: &str,
inputs: &[Variant],
) -> Result<Vec<Variant>, OpcUaError>;
| 参数 | 说明 |
|---|---|
object_node_id | 方法所在 Object 节点 |
method_node_id | 方法本身 NodeId |
inputs | 入参 Variant 切片 (可空 &[]) |
返回 Vec<Variant> 输出参数列表.
内存所有权
返回列表里每个 Variant 都持有 native handle, 由 Rust Drop trait 自动释放.
不需要手动 dispose, 也不会泄漏 native Variant 内存:
{
let outputs = s.call("ns=2;s=Obj", "ns=2;s=Obj.Method", &[])?;
for v in &outputs {
println!("{}", v.try_get_i32().unwrap_or(0));
}
} // <- outputs Vec drop, 每个 Variant native 内存全释放
例子
调 Calculator.Add(3, 4)
use darra_opcua::Variant;
let mut a = Variant::new(); a.set_i32(3);
let mut b = Variant::new(); b.set_i32(4);
let outputs = s.call(
"ns=2;s=Calculator",
"ns=2;s=Calculator.Add",
&[a, b],
)?;
for v in &outputs {
println!("sum = {}", v.try_get_i32().unwrap_or(0));
}
调无入参方法
let outputs = s.call(
"ns=2;s=Boiler1",
"ns=2;s=Boiler1.Reset",
&[], // 空切片
)?;
调多个不同类型入参
let mut name = Variant::new(); name.set_string("Boiler1");
let mut sp = Variant::new(); sp.set_f64(85.5);
let outputs = s.call(
"ns=2;s=Plant",
"ns=2;s=Plant.SetTemperature",
&[name, sp],
)?;
错误
| StatusCode | 含义 |
|---|---|
BAD_NODE_ID_INVALID | NodeId 解析失败 |
BAD_COMMUNICATION_ERROR | Transport 错 |
BAD_METHOD_INVALID | 该 NodeId 不是 Method |
BAD_INVALID_ARGUMENT | 入参类型 / 个数不符 |
BAD_USER_ACCESS_DENIED | 无权限调 |
BAD_* | 方法返回的业务级错误 |
业务级失败 (方法返回非 Good) 也作为 Err 返回, e.status 即业务码:
match s.call("ns=2;s=O", "ns=2;s=O.M", &[]) {
Ok(outs) => println!("ok, {} outputs", outs.len()),
Err(e) if e.status == darra_opcua::StatusCode::BAD_USER_ACCESS_DENIED
=> eprintln!("无权限"),
Err(e) => eprintln!("call failed: {}", e),
}
找方法的 NodeId
use darra_opcua::NodeClass;
// 1. Browse Object 子节点过滤 Method
let methods = s.browse("ns=2;s=Calculator", NodeClass::Method)?;
for m in &methods {
println!(" {} -> {}", m.browse_name, m.node_id);
}
// 2. 看入参 / 出参签名 (Browse 该方法节点的 Property)
let props = s.browse(&methods[0].node_id, NodeClass::Variable)?;
// 含 InputArguments / OutputArguments
输入参数构造
按方法签名构造 Variant:
use darra_opcua::Variant;
use std::time::SystemTime;
let mut a = Variant::new(); a.set_i32(3); // Int32
let mut b = Variant::new(); b.set_i32(4); // Int32
let mut name = Variant::new(); name.set_string("Boiler1");
let mut sp = Variant::new(); sp.set_f64(85.5);
let mut t = Variant::new(); t.set_datetime(SystemTime::now());
let mut flag = Variant::new(); flag.set_bool(true);
输出 Variant 解码
use darra_opcua::BuiltinType;
let outputs = s.call(/* ... */, /* ... */, &[])?;
for v in &outputs {
match v.data_type() {
BuiltinType::Int32 => println!("i32: {}", v.try_get_i32().unwrap_or(0)),
BuiltinType::Double => println!("f64: {}", v.try_get_f64().unwrap_or(0.0)),
BuiltinType::String => println!("str: {:?}", v.try_get_string()),
t => println!("other type: {:?}", t),
}
}
异步
当前 Call 是同步阻塞 (内部走 RPC + 等响应). Session::call 借用 &self, 内部 Stack 加锁,
跨线程并发调用安全; tokio 用户用 tokio::task::spawn_blocking 包装即可.