使用open62541读取OPC UA复杂节点(自定义类型)的用法咨询
关于open62541读取OPC UA复杂节点的问题
我有一个关于OPC UA“复杂”节点的问题:我的OPC UA服务器包含一些复杂节点,这里的“复杂”指节点拥有多个简单类型的子节点(例如某个节点包含三个整数子节点),我希望通过一次读取操作获取该复杂节点。我尝试编写了如下C++代码实现该功能:
int main(void) { typedef struct { UA_Int32 firstInteger; UA_Int32 secondInteger; UA_Double someDouble; } MyComplexType; UA_Client *client = UA_Client_new(); UA_ClientConfig_setDefault(UA_Client_getConfig(client)); UA_StatusCode status = UA_Client_connect(client, "opc.tcp://localhost:4840"); if (status != UA_STATUSCODE_GOOD) { printf("Failed to connect\n"); UA_Client_delete(client); return status; } // Define the NodeId of the complex type variable UA_NodeId nodeId = UA_NODEID_STRING(1, "ComplexVariable"); // Read the value UA_Variant value; UA_Variant_init(&value); status = UA_Client_readValueAttribute(client, nodeId, &value); if (status == UA_STATUSCODE_GOOD && UA_Variant_hasType(&value, &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])) { UA_ExtensionObject *extObj = (UA_ExtensionObject *)value.data; if (extObj->encoding == UA_EXTENSIONOBJECT_DECODED && extObj->content.decoded.type == &MyComplexType_desc) { MyComplexType *complexData = (MyComplexType *)extObj->content.decoded.data; printf("Read Complex Data: %d, %d, %f\n", complexData->firstInteger, complexData->secondInteger, complexData->someDouble); } else { printf("Failed to decode complex data\n"); } } else { printf("Failed to read complex node\n"); } UA_Variant_clear(&value); UA_Client_disconnect(client); UA_Client_delete(client); return 0; }
请问这是否是open62541库的正确用法?另外,我假设extObj本质上是UA_Variant数组,这个假设是否正确?
问题解答
1. 代码的正确性:核心思路方向对,但存在关键缺失
这段代码仅适用于服务器端是自定义结构化类型(Struct)的Variable节点,但有几个必须补充的关键步骤:
- 缺失
MyComplexType_desc的定义与注册:open62541需要明确的UA_DataType类型描述符识别自定义类型,你必须通过open62541的代码生成工具(如ua_generator)基于XML类型定义生成,或手动构造UA_DataType结构体,并调用UA_Client_addDataType将其注册到客户端的类型字典中。否则extObj->content.decoded.type == &MyComplexType_desc的判断永远无法成立。 - 服务器端节点类型必须匹配:如果你的“复杂节点”是包含子节点的Object节点(仅父节点下挂三个变量子节点),这段代码完全无效——因为Object节点的Value属性是空的,无法通过读取父节点Value获取子节点数据。这种场景下需要用批量读取接口获取子节点。
- 可补充严谨性检查:比如在转换
value.data为UA_ExtensionObject*前,确认value.data非空;对extObj的有效性做额外校验。
2. 关于extObj本质是UA_Variant数组的假设:完全错误
UA_ExtensionObject是OPC UA标准中专门用于封装自定义类型、非标类型或扩展类型的容器,和UA_Variant数组没有任何关系:
- 它有三种编码状态:
UA_EXTENSIONOBJECT_ENCODED:存储编码后的二进制数据+类型ID,需解码后才能使用;UA_EXTENSIONOBJECT_DECODED:存储解码后的具体数据(如你的MyComplexType实例)+对应的UA_DataType描述符;UA_EXTENSIONOBJECT_ENCODED_NO_BODY:仅包含类型ID,无实际数据。
UA_Variant是用于存储任意类型值的通用容器,而UA_ExtensionObject是针对扩展类型的专用封装结构,二者设计目的完全不同。
补充:两种场景的正确实现方式
场景A:服务器端是自定义Struct类型的Variable节点
- 生成并注册自定义类型的
UA_DataType到客户端; - 保持你代码的核心逻辑,确保服务器端的
ComplexVariable确实是该Struct类型的变量; - 解码时通过匹配
UA_DataType获取结构化数据。
场景B:服务器端是包含子节点的Object节点
使用批量读取接口一次性获取所有子节点的值,示例代码片段:
// 定义三个子节点的NodeId UA_NodeId nodeIds[3] = { UA_NODEID_STRING(1, "ComplexVariable/FirstInteger"), UA_NODEID_STRING(1, "ComplexVariable/SecondInteger"), UA_NODEID_STRING(1, "ComplexVariable/SomeDouble") }; UA_Variant values[3]; for(size_t i=0; i<3; i++) UA_Variant_init(&values[i]); // 批量读取多个节点的Value属性 UA_StatusCode status = UA_Client_readValues(client, nodeIds, 3, values); if(status == UA_STATUSCODE_GOOD) { // 解析每个子节点的值 UA_Int32 first = *(UA_Int32*)values[0].data; UA_Int32 second = *(UA_Int32*)values[1].data; UA_Double dbl = *(UA_Double*)values[2].data; printf("Read Data: %d, %d, %f\n", first, second, dbl); } // 清理资源 for(size_t i=0; i<3; i++) UA_Variant_clear(&values[i]);
内容的提问来源于stack exchange,提问作者pepsilon
相关产品推荐
相关产品推荐

