结构体解码
概述
OPC UA 1.04+ 把每个自定义结构体的字段定义放到 DataType 节点的 DataTypeDefinition 属性 (AttributeId=27, Part 5 §6.4.2)。SDK 实现完整协议层。
API
| 函数 | 类别 | 读写 | 说明 |
|---|---|---|---|
| DarraUa_StructDecoder_New(ua, &out_dec) | 构造 | — | 绑定到会话 |
| DarraUa_StructDecoder_Free(dec) | 释放 | — | 析构 |
| DarraUa_StructDecoder_Decode(dec, variant, &out_obj) | 方法 | 读 | 解码 ExtensionObject |
| DarraUa_StructDecoder_DecodeBody(dec, body, n, type_id, &out_obj) | 方法 | 读 | 直接给字节 |
| DarraUa_StructDecoder_GetDefinition(dec, type_id, &out_def) | 方法 | 读 | 获取 / 缓存 |
| DarraUa_StructDecoder_PrefetchDefinitions(dec, ids, n) | 方法 | 读 | 预热 |
| DarraUa_DynamicObject_GetField(obj, name, &out_val) | 索引 | 读 | 取字段 (DarraUa_FieldValue*) |
| DarraUa_DynamicObject_GetFields(obj, &out_kvs, &out_n) | 方法 | 读 | 全字段 KV 列表 |
| DarraUa_DynamicObject_Free(obj) | 释放 | — | 释放 |
DarraUa_FieldValue 是 tagged union: type + value 联合体, type 枚举: BOOL / INT64 / UINT64 / FLOAT / DOUBLE / STRING / BYTES / DATETIME / NESTED / ARRAY / NULL_VALUE。
相关结构:
typedef enum {
SK_STRUCTURE = 0, // 普通
SK_STRUCTURE_WITH_SUBTYPED_VALUES = 1, // 含子类型
SK_STRUCTURE_WITH_OPTIONAL_FIELDS = 2, // 含可选 (前置 EncodingMask)
SK_UNION = 3, // Union (前置 selector)
SK_UNION_WITH_SUBTYPED_VALUES = 4 // Union + 子类型
} DarraUa_StructureKind;
代码示例
#include <opcua.h>
DarraUa_Session* ua;
DarraUa_Session_New("opc.tcp://localhost:4840", &ua);
DarraUa_Session_Connect(ua);
DarraUa_StructDecoder* dec;
DarraUa_StructDecoder_New(ua, &dec);
// 1) 读自定义结构
DarraUa_DataValue* dv = NULL;
DarraUa_Session_Read(ua, "ns=2;s=Robot.CurrentPose", ATTR_VALUE, &dv);
DarraUa_DynamicObject* pose = NULL;
DarraUa_StructDecoder_Decode(dec, &dv->value, &pose);
DarraUa_FieldValue* x_val = NULL;
DarraUa_DynamicObject_GetField(pose, "X", &x_val);
if (x_val && x_val->type == FV_DOUBLE)
printf("x = %f\n", x_val->value.dbl);
// 2) 嵌套
DarraUa_FieldValue* quat_val = NULL;
DarraUa_DynamicObject_GetField(pose, "Quaternion", &quat_val);
if (quat_val && quat_val->type == FV_NESTED) {
DarraUa_FieldValue* w_val = NULL;
DarraUa_DynamicObject_GetField(quat_val->value.nested, "W", &w_val);
if (w_val && w_val->type == FV_DOUBLE)
printf("qw = %f\n", w_val->value.dbl);
}
// 3) 全字段遍历
DarraUa_KvPair* fields = NULL;
size_t n_fields = 0;
DarraUa_DynamicObject_GetFields(pose, &fields, &n_fields);
for (size_t i = 0; i < n_fields; ++i)
printf("%s = ...\n", fields[i].key);
DarraUa_FreeKvPairs(fields, n_fields);
// 4) 预热
const char* ids[] = { "ns=2;i=3001", "ns=2;i=3002" };
DarraUa_StructDecoder_PrefetchDefinitions(dec, ids, 2);
DarraUa_DynamicObject_Free(pose);
DarraUa_FreeDataValue(dv);
DarraUa_StructDecoder_Free(dec);
DarraUa_Session_Free(ua);
实现限制
- Native 层未导出 ExtensionObject 字节提取接口, Decode 在拿不到字节时返回 NULL_VALUE 占位字段
- 协议层全部已实现, native 接通后 DecodeBody 立刻可用
- DiagnosticInfo 跳过
最佳实践
- 共享一个 decoder, 字段表缓存复用
- PrefetchDefinitions 启动时调
- 嵌套结构 type==NESTED 后访问
- Union 看 definition.kind == SK_UNION
跨语言对照
| C# | Python | Java | C++ | Rust | C |
|---|---|---|---|---|---|
| StructDecoder | StructDecoder | StructDecoder | StructDecoder | StructDecoder | DarraUa_StructDecoder |
| Decode | decode | decode | Decode | decode | DarraUa_StructDecoder_Decode |
| DynamicExtensionObject | DynamicExtensionObject | DynamicExtensionObject | DynamicExtensionObject | DynamicExtensionObject | DarraUa_DynamicObject |