对于String类型节点,其值的类型是UA_String,在这篇文章我们解释了UA_String的生成方法

我们修改String类型节点的值时,会事先准备一个UA_String变量,这时就会遇到一个选择是否需要动态分配内存,即是否需要使用UA_STRING_ALLOC,因为动态分配内存比较消耗资源,如果修改比较频繁,那么就会影响系统性能

本文讲解一下这个注意点。


简单例子

首先添加一个String类型的变量

UA_NodeId addTheAnswerVariable(UA_Server *server) 
{
    /* Define the attribute of the myInteger variable node */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_String initStrVal = UA_STRING((char*)"hello 0");
    UA_Variant_setScalar(&attr.value, &initStrVal, &UA_TYPES[UA_TYPES_STRING]);
    attr.description = UA_LOCALIZEDTEXT((char*)"en-US", (char*)"the answer");
    attr.displayName = UA_LOCALIZEDTEXT((char*)"en-US", (char*)"the answer");
    attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Add the variable node to the information model */
    UA_NodeId theAnswerNodeId = UA_NODEID_NUMERIC(1, 62541);
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, (char*)"the answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, theAnswerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
    
    return theAnswerNodeId;
}

注意,这里设置初始值使用的是UA_STRING(),没有二次分配内存内容是“hello 0”

然后修改变量值,
main函数添加一个定时任务,每隔2s去调用一次cycleCallback

UA_UInt64 callbackId = 0;
UA_Server_addRepeatedCallback(server, cycleCallback, &targetNodeId, 2000, &callbackId); // call every 2s

cycleCallback函数内容如下

void cycleCallback(UA_Server *server, void *data)
{
    static UA_Int32 update = 1;

    UA_NodeId *targetNodeId = static_cast<UA_NodeId*>(data);

    char strData[32];
    snprintf(strData, 32, "hello %d", update);

    UA_String tempStr = UA_STRING(strData);

    UA_Variant myVar;
    UA_Variant_init(&amp;myVar);
    UA_Variant_setScalar(&amp;myVar, &amp;tempStr, &UA_TYPES[UA_TYPES_STRING]);
    UA_Server_writeValue(server, *targetNodeId, myVar);

    if (update++ == 1000)
    {
        update = 1;
    }
}

不断累加变量update然后传给strData里,接着使用UA_STRING去构造UA_String变量最后以此修改节点值,可见这样也没有动态分配内存

完整代码如下

// server.cpp
#include <memory>

#include <signal.h>
#include <stdlib.h>
#include <unistd.h>


#include "open62541.h"

UA_Boolean running = true;

void stopHandler(int sign) {
    UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
    running = false;
}

UA_UInt32 monid = 0;


UA_NodeId addTheAnswerVariable(UA_Server *server) 
{
    /* Define the attribute of the myInteger variable node */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_String initStrVal = UA_STRING((char*)"hello 0");
    UA_Variant_setScalar(&attr.value, &initStrVal, &UA_TYPES[UA_TYPES_STRING]);
    attr.description = UA_LOCALIZEDTEXT((char*)"en-US", (char*)"the answer");
    attr.displayName = UA_LOCALIZEDTEXT((char*)"en-US", (char*)"the answer");
    attr.dataType = UA_TYPES[UA_TYPES_STRING].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Add the variable node to the information model */
    UA_NodeId theAnswerNodeId = UA_NODEID_NUMERIC(1, 62541);
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, (char*)"the answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, theAnswerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
    
    return theAnswerNodeId;
}



void cycleCallback(UA_Server *server, void *data)
{
    static UA_Int32 update = 1;

    UA_NodeId *targetNodeId = static_cast<UA_NodeId*>(data);

    char strData[32];
    snprintf(strData, 32, "hello %d", update);

    UA_String tempStr = UA_STRING(strData);

    UA_Variant myVar;
    UA_Variant_init(&myVar);
    UA_Variant_setScalar(&myVar, &tempStr, &UA_TYPES[UA_TYPES_STRING]);
    UA_Server_writeValue(server, *targetNodeId, myVar);

    if (update++ == 1000)
    {
        update = 1;
    }
}


int main(void) 
{    
    signal(SIGINT, stopHandler);
    signal(SIGTERM, stopHandler);

    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

    UA_NodeId targetNodeId = addTheAnswerVariable(server);

    
    UA_UInt64 callbackId = 0;
    UA_Server_addRepeatedCallback(server, cycleCallback, &targetNodeId, 2000, &callbackId); // call every 2s

    UA_StatusCode retval = UA_Server_run(server, &running);

    UA_Server_delete(server);

    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

编译运行发现一切OK。


二 疑问和解答

可能会疑问:不使用动态内存,那么更新节点值之后,就会离开函数作用域,这样内存是否还有效呢?

答案是:出了函数作用域之后内存会失效,但是在写的过程内部会重新拷贝一份内存,所以不会影响节点值

这里可以分析一下UA_Server_writeValue()的调用过程就可以知道答案了。其调用过程如下

UA_Server_writeValue调用__UA_Server_write
在这里插入图片描述
__UA_Server_write调用writeAttribute,
在这里插入图片描述
writeAttribute调用Operation_Write,
在这里插入图片描述
Operation_Write调用UA_Server_editNode,注意这里有个函数指针copyAttributeIntoNode,接下来调用它,
在这里插入图片描述
copyAttributeIntoNode中,会调用writeNodeValueAttribute ,因为我们是修改节点值,
在这里插入图片描述

在writeNodeValueAttribute()里,会调用writeValueAttributeWithoutRange ,
在这里插入图片描述
在writeValueAttributeWithoutRange里就会调用UA_DataValue_copy,会把string的内容拷贝一份过来
在这里插入图片描述
这样最外部的UA_String参数即使失效也不会影响节点值。

简化的调用流程如下
UA_Server_writeValue -> __UA_Server_write -> writeAttribute -> Operation_Write -> UA_Server_editNode -> copyAttributeIntoNode -> writeNodeValueAttribute -> writeValueAttributeWithoutRange -> UA_DataValue_copy


三 结论

通过以上分析可以得知,当我们创建String类型节点并设置初值,以及后续更新节点值时,都不需要使用UA_STRING_ALLOC,这样就避免了动态分配内存,提高性能

原文地址:https://blog.csdn.net/whahu1989/article/details/130791857

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_17839.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注