如何使用 open62541 一次使用 OPC-UA 编写多个节点?
Posted
技术标签:
【中文标题】如何使用 open62541 一次使用 OPC-UA 编写多个节点?【英文标题】:How to write multiple nodes with OPC-UA at once using open62541? 【发布时间】:2020-08-23 10:23:56 【问题描述】:我正在尝试在单个请求中编写多个节点,但是我没有找到任何有关如何执行此操作的文档或示例,每次我发现有关该问题的任何内容时,都会写入一个节点。根据我对 open62541 库(不多)的理解,我尝试这样做:
void Write_from_3_to_5_piece_queue()
char NodeID[128];
char NodeID_backup[128];
char aux[3];
bool bool_to_write = false;
strcpy(NodeID_backup, _BaseNodeID);
strcat(NodeID_backup, "POU.AT2.piece_queue["); // this is where I want to write, I need only to append the array index in which to write
UA_WriteRequest wReq;
UA_WriteValue my_nodes[3]; // this is where I start to make things up, I'm not sure this is the correct way to do it
my_nodes[0] = *UA_WriteValue_new();
my_nodes[1] = *UA_WriteValue_new();
my_nodes[2] = *UA_WriteValue_new();
strcpy(NodeID, NodeID_backup);
strcat(NodeID, "3]"); //append third index of array (will write to piece_queue[3])
my_nodes[0].nodeId = UA_NODEID_STRING_ALLOC(_nodeIndex, NodeID);
my_nodes[0].attributeId = UA_ATTRIBUTEID_VALUE;
my_nodes[0].value.hasValue = true;
my_nodes[0].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
my_nodes[0].value.value.storageType = UA_VARIANT_DATA_NODELETE;
my_nodes[0].value.value.data = &bool_to_write;
strcpy(NodeID, NodeID_backup);
strcat(NodeID, "4]");
my_nodes[1].nodeId = UA_NODEID_STRING_ALLOC(_nodeIndex, NodeID);
my_nodes[1].attributeId = UA_ATTRIBUTEID_VALUE;
my_nodes[1].value.hasValue = true;
my_nodes[1].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
my_nodes[1].value.value.storageType = UA_VARIANT_DATA_NODELETE;
my_nodes[1].value.value.data = &bool_to_write;
strcpy(NodeID, NodeID_backup);
strcat(NodeID, "5]");
my_nodes[2].nodeId = UA_NODEID_STRING_ALLOC(_nodeIndex, NodeID);
my_nodes[2].attributeId = UA_ATTRIBUTEID_VALUE;
my_nodes[2].value.hasValue = true;
my_nodes[2].value.value.type = &UA_TYPES[UA_TYPES_BOOLEAN];
my_nodes[2].value.value.storageType = UA_VARIANT_DATA_NODELETE;
my_nodes[2].value.value.data = &bool_to_write;
UA_WriteRequest_init(&wReq);
wReq.nodesToWrite = my_nodes;
wReq.nodesToWriteSize = 3;
UA_WriteResponse wResp = UA_Client_Service_write(_client, wReq);
UA_WriteResponse_clear(&wResp);
UA_WriteRequest_clear(&wReq);
return;
起初我并没有太大希望这会起作用,但事实证明这实际上写入了我希望的值。问题是在UA_WriteRequest_clear(&wReq);
上我触发了open62541 库中的异常:
另外,我知道我可以专门将多个值写入数组,即使在这个可以解决我的问题的特定示例中,这不是我的意思,这个示例只是为了简化我的问题。假设我有一个多类型的结构,我想写入它,所有这些都在一个请求中。感谢您的帮助!
【问题讨论】:
【参考方案1】:首先,这很糟糕:
UA_WriteValue my_nodes[3];
my_nodes[0] = *UA_WriteValue_new();
my_nodes[1] = *UA_WriteValue_new();
my_nodes[2] = *UA_WriteValue_new();
my_nodes 已经在堆栈上创建,然后您通过取消引用将新对象的内容复制到其中。这肯定会导致内存泄漏。您可能想改用UA_WriteValue_init()
。
永远不要取消引用 new()
函数的返回值。
让我们自下而上:
UA_WriteRequest_clear(&wReq)
递归释放wReq
结构的所有内容。
这意味着它还将调用:
UA_Array_delete(wReq.nodesToWrite, wReq.nodesToWriteSize, ...)
进而调用UA_free(wReq.nodesToWrite)
你有:
wReq.nodesToWrite = my_nodes;
和
UA_WriteValue my_nodes[3];
这意味着您正在将一个位于堆栈中的变量分配给一个指针,然后该指针被释放。 free
只能删除堆而不是堆栈上的东西,因此它失败了。
你现在有两个选择:
-
如果您仍想使用堆栈技巧,则 UA_clear 认为变量为空:
wReq.nodesToWrite = NULL;
wReq.nodesToWriteSize = 0;
UA_clear(&wReq);
-
将节点放在堆上:
代替
UA_WriteValue my_nodes[3];
使用 UA_WriteValue *my_nodes = (UA_WriteValue*)UA_malloc(sizeof(UA_WriteValue)*3);
之类的东西
此外,我强烈建议您使用 valgrind 或 clang 内存清理程序来避免所有这些内存问题。
【讨论】:
感谢您的快速回答!我不完全清楚如何实施选项二。你是说 UA_WriteValue_new() 只像你一样分配内存吗?我试图将 3 个 WriteValues 大小的内存分配给 UA_WriteValue 指针(如您所述),但我仍然得到一个明确的错误。我已经成功实现了第一个选项,并通过指向 NULL 来欺骗清除。我担心的是,通过使用堆栈然后欺骗 UA_clear 认为没有值可能会导致 UA_WriteValue_init() 导致一些泄漏,你知道是不是这样吗? 简单地说,_clear
试图以递归方式释放你传入的所有内容。如果任何孩子在堆栈上,这将失败。我强烈建议您首先确保您了解堆和堆栈,包括 C 指针、堆分配和堆栈分配。这对于 open62541 堆栈非常重要。并且还尝试使用clang记忆消毒剂。它会准确地告诉你错误在哪里。
确保在指向wReq.nodesToWrite
的每个初始化索引的指针上调用UA_WriteValue_init()
。否则你会得到一个明确的错误。例如wReq.nodesToWrite = (UA_WriteValue*) UA_malloc(2*sizeof(UA_WriteValue)); UA_WriteValue_init(&wReq.nodesToWrite[0]; UA_WriteValue_init(&wReq.nodesToWrite[1];
以上是关于如何使用 open62541 一次使用 OPC-UA 编写多个节点?的主要内容,如果未能解决你的问题,请参考以下文章
如何在open62541中将UA_String数组添加到服务器