Read / Write
前置阅读
- 数据类型 Variant 和 DataValue 是所有读写的基础.
- 高频访问相同节点请用 RegisterNodes 加速.
- 实时变化通知用 Subscription 而不是循环 Read.
Read
public DataValue Read(string nodeId, AttributeId attribute = AttributeId.Value);
读单节点的指定 Attribute.
| 参数 | 类型 | 默认 | 说明 |
|---|---|---|---|
nodeId | string | — | NodeId 字符串 (i=2258 / ns=2;s=...) |
attribute | AttributeId | Value (13) | 要读的属性 |
返回: DataValue (实现 IDisposable, 必须用 using)
异常: OpcUaException (BadNodeIdUnknown / BadNotReadable / BadCommunicationError / ...)
// 读 Value
using var dv = ua.Read("ns=2;s=Temperature");
Console.WriteLine(dv.Value.AsDouble);
// 读 DisplayName
using var dn = ua.Read("ns=2;s=Temperature", AttributeId.DisplayName);
Console.WriteLine(dn.Value.AsString);
ReadMany
同 Attribute 批量
public IReadOnlyList<DataValue> ReadMany(
IReadOnlyList<string> nodeIds,
AttributeId attribute = AttributeId.Value);
一次 RPC 批量读多个节点的同一 Attribute.
var nodes = new[] { "ns=2;s=T1", "ns=2;s=T2", "ns=2;s=T3" };
var values = ua.ReadMany(nodes);
for (int i = 0; i < nodes.Length; i++)
Console.WriteLine($"{nodes[i]} = {values[i].Value}");
任意 NodeId + AttributeId 组合
public IReadOnlyList<DataValue> ReadMany(
IReadOnlyList<(string nodeId, AttributeId attr)> items);
var items = new (string, AttributeId)[]
{
("ns=2;s=T1", AttributeId.Value),
("ns=2;s=T1", AttributeId.DataType),
("ns=2;s=T2", AttributeId.Value),
};
var results = ua.ReadMany(items);
性能: 批量比循环单读快 ~N 倍 (省 N-1 次 RPC 往返).
Write
public StatusCode Write(string nodeId, Variant value, AttributeId attribute = AttributeId.Value);
写单节点.
StatusCode st = ua.Write("ns=2;s=Setpoint", new Variant(42.5));
if (st != StatusCode.Good) Console.WriteLine($"Write failed: {st}");
返回 StatusCode (Good / BadNotWritable / BadTypeMismatch / BadUserAccessDenied / ...).
业务级失败不抛异常
Write 不抛异常 (除非 transport 级错误), 业务级失败通过返回值判断. 必须检查返回值, 不要假定 Good.
Variant 构造速查
new Variant(true) // Boolean
new Variant((short)42) // Int16
new Variant(42) // Int32
new Variant(42L) // Int64
new Variant(3.14) // Double
new Variant(3.14f) // Float
new Variant("hello") // String
new Variant(DateTime.UtcNow) // DateTime
new Variant(Guid.NewGuid()) // Guid
new Variant(new byte[]{...}) // ByteString
// 数组
new Variant(new[] { 1.0, 2.0, 3.0 })
new Variant(new[] { "a", "b", "c" })
详见 Variant 数据类型.
DataValue 字段速查
| 字段 | 类型 | 说明 |
|---|---|---|
Value | Variant | 实际值 (可能为 null) |
Status | StatusCode | Good / Bad* / Uncertain* |
SourceTimestamp | DateTime? | 数据源时间戳 |
ServerTimestamp | DateTime? | 服务端打的时间戳 |
using var dv = ua.Read("ns=2;s=T1");
if (dv.Status != StatusCode.Good)
{
Console.WriteLine($"Bad value: {dv.Status}");
return;
}
Console.WriteLine($"{dv.Value.AsDouble} @ {dv.SourceTimestamp}");
AttributeId 常用值
| Id | 名称 | 说明 |
|---|---|---|
| 1 | NodeId | 节点 ID |
| 2 | NodeClass | 节点类别 |
| 3 | BrowseName | 浏览名 |
| 4 | DisplayName | 显示名 |
| 5 | Description | 描述 |
| 13 | Value | 变量值 (默认) |
| 14 | DataType | 数据类型 NodeId |
| 17 | AccessLevel | 访问权限位 |
| 20 | Historizing | 是否在记录历史 |
| 21 | Executable | Method 是否可执行 |
最佳实践
- 批量优先: 监控 100+ Tag 用
ReadMany, 不要循环Read(省 N-1 次 RPC) - 多 Attribute 也用批量: 大量读同一节点的不同 Attribute, 用
ReadMany((nodeId, attr))形式 - 必须 Dispose:
DataValue实现 IDisposable, 漏 Dispose 会泄漏 native 内存 - Write 检查返回值: 不要假定一定 Good
- 实时变化用订阅: 不要 1 秒一次轮询 Read, 改用
SubscriptionDataChanged 事件 (服务端推送)