DataValue
DataValue 包装一次 Read 返回的全部信息: 值 + 状态 + 时间戳.
配套
- 内部值容器请看 Variant.
- 标准节点常量请看 well_known_nodes.
公共方法
| 类别 | 属性 | 类型 | 访问 | 说明 |
|---|---|---|---|---|
| 值 | variant() | Variant | R | 实际值 (owns=false 借用) |
value() | Variant | R | 兼容旧名, 同 variant() | |
| 状态 | status() | StatusCode | R | 数据质量 Good / Bad* / Uncertain* |
is_good() | bool | R | == status().is_good() | |
| 时间戳 | source_timestamp() | Option<SystemTime> | R | 数据源打的时间戳 |
server_timestamp() | Option<SystemTime> | R | 服务端打的时间戳 | |
source_timestamp_ft() | i64 | R | FILETIME 100ns since 1601 (兼容旧 API) | |
server_timestamp_ft() | i64 | R | 同上 |
用法
use darra_opcua::StatusCode;
let dv = s.read("ns=2;s=Temperature")?;
// 检查质量
if !dv.is_good() {
eprintln!("Bad value: {}", dv.status());
return Ok(());
}
// 检查值
let v = dv.variant();
if matches!(v.data_type(), darra_opcua::BuiltinType::Null) {
eprintln!("Null value");
return Ok(());
}
// 取值
let temp = v.try_get_f64().unwrap_or(0.0);
let t = dv.source_timestamp();
println!("{:?}: {} °C", t, temp);
StatusCode 重点值
| 值 | 含义 |
|---|---|
GOOD (0x00000000) | 一切正常 |
UNCERTAIN (0x40000000) | 不确定但可用 (例如传感器超量程) |
BAD (0x80000000) | 数据无效 |
BAD_COMMUNICATION_ERROR | 通讯失败 |
BAD_NODE_ID_UNKNOWN | 节点不存在 |
BAD_NOT_READABLE | 该 Attribute 不可读 |
业务代码必须检查 status(), 不能假设 Good.
let dv = s.read("ns=2;s=X")?;
if dv.status() != StatusCode::GOOD {
eprintln!("uncertain data: {}", dv.status());
}
时间戳含义
source_timestamp()— 数据真实产生时间 (传感器采样时刻 / 计算时刻). 历史 / 趋势 / 同步用这个.server_timestamp()— 服务端收到 / 处理时间. 用于诊断网络延迟. 一般不用于业务.
如果服务端没设 SourceTimestamp, 它就是 ServerTimestamp 的副本.
SystemTime vs FILETIME
source_timestamp() / server_timestamp() 返回 Option<SystemTime> —
None 表示服务端未设置 (FILETIME=0).
如果要原始 100ns ticks (不做 1601→1970 转换), 用 *_ft() 版本:
let ft: i64 = dv.source_timestamp_ft(); // 0 if not set
内存所有权 (Drop)
DataValue 持有 native handle, 实现 Drop. 从 s.read / s.read_history /
s.read_many 返回的都自动释放, 不会泄漏.
订阅回调里通过 wrap_borrowed 看到的 DataValue 是 owns=false, 不释放底层 (由 C 层
Publish 线程管生命周期, 回调返回后失效).
{
let dv = s.read("ns=2;s=T1")?;
let v = dv.variant(); // borrowed Variant, dv 在生命周期内有效
println!("{}", v);
} // <- dv Drop, native 释放
// 批量
let dvs = s.read_many(&nodes, AttributeId::Value)?;
for dv in &dvs {
if let Some(d) = dv {
println!("{:?}", d.variant());
}
}
// dvs 离开作用域, 每个 DataValue Drop, 全部 native 内存释放
跨线程
DataValue 是 Send + Sync (内部 native 加锁), 可以在线程间传 owned DataValue.
不要把 borrowed DataValue (从订阅回调拿的) 跨线程使用, 因为底层指针生命周期在 C 层.
与订阅回调的关系
订阅回调里 SDK 已把 DataValue 字段抽取为扁平结构 DataChangeEventArgs (含
value_string, data_type_name, status, source_timestamp, server_timestamp),
所以订阅回调里不需要 DataValue 对象. 详见 DataChange 回调.