跳到主要内容

Read / Write

前置阅读

read

public OpcUaDataValue read(String nodeId);
public OpcUaDataValue read(String nodeId, Enums.AttributeId attribute);

读单节点的指定 Attribute.

参数类型默认说明
nodeIdStringNodeId 字符串 (i=2258 / ns=2;s=...)
attributeEnums.AttributeIdValue (13)要读的属性

返回: OpcUaDataValue (实现 AutoCloseable, 必须 close)

异常: OpcUaException (BadNodeIdUnknown / BadNotReadable / BadCommunicationError / ...)

// 读 Value
try (OpcUaDataValue dv = ua.read("ns=2;s=Temperature")) {
System.out.println(dv.variant().asDouble());
}

// 读 DisplayName
try (OpcUaDataValue dn = ua.read("ns=2;s=Temperature", Enums.AttributeId.DisplayName)) {
System.out.println(dn.variant().asString());
}

readMany

一次 RPC 批量读多个节点的同一 Attribute.

public List<OpcUaDataValue> readMany(List<String> nodeIds, Enums.AttributeId attribute);
List<String> nodes = Arrays.asList("ns=2;s=T1", "ns=2;s=T2", "ns=2;s=T3");
List<OpcUaDataValue> values = ua.readMany(nodes, Enums.AttributeId.Value);
try {
for (int i = 0; i < nodes.size(); i++) {
OpcUaDataValue dv = values.get(i);
if (dv != null) {
System.out.println(nodes.get(i) + " = " + dv.variant());
}
}
} finally {
for (OpcUaDataValue dv : values) if (dv != null) dv.close();
}

性能: 批量比循环单读快 ~N 倍 (省 N-1 次 RPC 往返).

必须逐项 close

readMany 返回的列表里每个 OpcUaDataValue 都持有 native handle. try-finally 逐项 close, 否则泄漏.


write

public Enums.StatusCode write(String nodeId, OpcUaVariant value);
public Enums.StatusCode write(String nodeId, OpcUaVariant value, Enums.AttributeId attribute);

写单节点.

try (OpcUaVariant v = new OpcUaVariant().setDouble(42.5)) {
Enums.StatusCode st = ua.write("ns=2;s=Setpoint", v);
if (st != Enums.StatusCode.Good) {
System.err.println("Write failed: " + st);
}
}

返回 Enums.StatusCode (Good / BadNotWritable / BadTypeMismatch / BadUserAccessDenied / ...).

业务级失败不抛异常

write 不抛异常 (除非 transport 级错误), 业务级失败通过返回值判断. 必须检查返回值, 不要假定 Good.


OpcUaVariant 构造速查

new OpcUaVariant().setBoolean(true)          // Boolean
new OpcUaVariant().setSByte((byte)-1) // SByte
new OpcUaVariant().setByte(255) // Byte
new OpcUaVariant().setInt16((short)42) // Int16
new OpcUaVariant().setUInt16(42) // UInt16
new OpcUaVariant().setInt32(42) // Int32
new OpcUaVariant().setUInt32(42) // UInt32
new OpcUaVariant().setInt64(42L) // Int64
new OpcUaVariant().setFloat(3.14f) // Float
new OpcUaVariant().setDouble(3.14) // Double
new OpcUaVariant().setString("hello") // String
new OpcUaVariant().setDateTime(fileTimeLong) // DateTime (Windows FileTime)

详见 OpcUaVariant 数据类型.


OpcUaDataValue 字段速查

方法返回说明
variant()OpcUaVariant实际值 (不持有, 不要 close)
getStatusEnum()Enums.StatusCodeGood / Bad* / Uncertain*
isGood()boolean等价 getStatusEnum().isGood()
sourceTimestamp()long数据源 FileTime, 0 = null
serverTimestamp()long服务端 FileTime, 0 = null
try (OpcUaDataValue dv = ua.read("ns=2;s=T1")) {
if (!dv.isGood()) {
System.err.println("Bad value: " + dv.getStatusEnum());
return;
}
Double v = dv.variant().asDouble();
long ft = dv.sourceTimestamp();
Instant t = (ft > 0)
? Instant.ofEpochMilli((ft - 116444736000000000L) / 10000L)
: Instant.now();
System.out.println(v + " @ " + t);
}

AttributeId 常用值

Id名称说明
1NodeId节点 ID
2NodeClass节点类别
3BrowseName浏览名
4DisplayName显示名
5Description描述
13Value变量值 (默认)
14DataType数据类型 NodeId
17AccessLevel访问权限位
20Historizing是否在记录历史
21ExecutableMethod 是否可执行

最佳实践

  • 批量优先: 监控 100+ Tag 用 readMany, 不要循环 read (省 N-1 次 RPC)
  • 必须 close: OpcUaDataValue 实现 AutoCloseable, 漏 close 会泄漏 native 内存
  • write 检查返回值: 不要假定一定 Good
  • 实时变化用订阅: 不要 1 秒一次轮询 read, 改用 Subscription onDataChanged (服务端推送)
  • OpcUaVariant 也要 close: 自己 new OpcUaVariant() 构造的, 用 try-with-resources 包起来

下一步