学习open62541 --- [77] 修改String类型变量的注意点

news2024/11/27 18:35:19

对于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(&myVar);
    UA_Variant_setScalar(&myVar, &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,这样就避免了动态分配内存,提高了性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/551160.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一种基于数值的横向相互作用

( A, B )---144*30*2---( 1, 0 )( 0, 1 ) 让网络的输入有144个节点&#xff0c;训练集AB各由12张二值化的图片组成&#xff0c;让A中每行有1个1&#xff0c;B中全是0&#xff0c;排列组合A &#xff0c;统计迭代次数的顺序。 前面实验已经表明对于A中每行只有1个1&#xff0c;…

虚幻or现实?堆区、栈区真实存在吗?是操作系统在骗你罢了...

文章目录 &#x1f490;专栏导读&#x1f490;文章导读&#x1f427;引例 &#x1f426;进程地址空间&#x1f426;虚拟地址与物理内存的联系&#x1f514;回答引例中的问题&#x1f513;写时拷贝 &#x1f426;虚拟地址存在的意义&#x1f513;malloc的本质 &#x1f490;专栏…

Lift, Splat, Shoot 论文学习

1. 解决了什么问题&#xff1f; LSS 在工业界具有非常重要的地位。自从 Tesla AI Day 上提出了 BEV 感知后&#xff0c;不少公司都进行了 BEV 工程化的探索。当前 BEV 下的感知方法大致分为两类&#xff1a; 自下而上&#xff1a;利用 transformer 的 query 机制&#xff0c;…

软考 软件设计师上午题设计模式概念类

设计模式分类 创建型设计模式 简单工厂模式 不符合开闭原则&#xff0c;因此没有列入23类模式里 对扩展开放对修改关闭 工厂方法模式 说穿了&#xff1a;系统开放一个接口&#xff08;拓展开放&#xff09;、不提供修改的接口&#xff08;修改关闭&#xff09;&#xff0c;…

基于html+css的图展示83

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

使用贝壳物联控制led灯

1、完成esp8266 01S的固件刷机 1.1 使用刷机软件刷原生固件 esp8266 01S要使用标准固件1M&#xff08;主要是01S是8M&#xff09; 1.2 刷机接线方式 ttl直接连esp8266 接线&#xff1a; tts esp8266 3v3 ---》面包板高----》3.3 tx--------------…

BEVFormer 论文学习

1. 解决了什么问题&#xff1f; 3D 视觉感知任务&#xff0c;包括基于多相机图像的 3D 目标检测和分割&#xff0c;对于自动驾驶系统非常重要。与基于 LiDAR 的方法相比&#xff0c;基于相机图像的方法能够检测到更远距离的目标&#xff0c;识别交通信号灯、交通标识等信息。有…

[CTF/网络安全] 攻防世界 command_execution 解题详析

[CTF/网络安全] 攻防世界 command_execution 解题详析 ping命令ping命令的应用格式ping命令执行漏洞 ls命令cat命令姿势ping本地回环地址ping目录ping文件夹ping文件 Tips总结 题目描述&#xff1a;小宁写了个ping功能,但没有写waf,X老师告诉她这是非常危险的&#xff0c;你知道…

图【无向图】的创建与遍历

树&#xff1a;无回路 图&#xff1a;有回路 代码在最下面 邻接矩阵&#xff1a;重点&#xff1a;矩阵 &#xff08;一&#xff09;图的创建 存储方式 如图&#xff1a; 代码截图分析&#xff1a;顶点用一维数组存&#xff0c;边用两个点之间的值为0或1来表…

异地研发团队都使用哪些研发协同工具?盘点7类最主流的研发管理协同软件

产品研发场景下好用的协同办公软件有哪些&#xff1f;分享7类研发过程中主流的协同办公软件&#xff0c;比如项目管理协作与问题跟踪工具PingCode、代码托管与版本控制平台github、持续集成与持续部署&#xff08;CI/CD&#xff09;工具jinkens、文档协作与知识管理工具conflue…

【力扣周赛】第346场周赛

【力扣周赛】第346场周赛 6439. 删除子串后的字符串最小长度题目描述解题思路 6454. 字典序最小回文串题目描述解题思路 6441. 求一个整数的惩罚数题目描述解题思路 6439. 删除子串后的字符串最小长度 题目描述 描述&#xff1a;给你一个仅由 大写 英文字符组成的字符串 s 。…

Gradle ——Gradle安装与配置

目录 一、简介 二、功能和特点 三、安装 参考&#xff1a; Gradle_百度百科 Gradle 学习 ----Gradle 入门_你若不离不弃&#xff0c;我必生死相依的博客-CSDN博客 还有硬石科技的开源wifiAPP程序,没找到他们的码仓 一、简介 Gradle是一个基于Apache Ant和Apache Maven概念…

BPMN2.0 -条件序列流和默认序列流

序列流是流程中两个元素或者活动的连接器。在流程执行过程中访问一个元素之后,将继续执行素有的序列流,默认是并行的。传出的序列流将创建独立的并行执行路径。 顺序流需要有流程唯一的id,并引用存在的源与目标元素。 <sequenceFlow id="flow1" sourceRef=&qu…

vxe-table 对合并列的理解

行列对应关系 删除数据&#xff0c;只对一条数据进行分析 合并列代码&#xff08;_rowspan移除不影响&#xff0c;但是函数会eslint报红&#xff0c;正常运行&#xff09; 合并之后&#xff0c;从第一列开始&#xff0c;内容整体右移动&#xff0c;标题内容不对应 解决内容右移…

第三章 openEuler 文件系统

系列文章目录 第一章 openEuler 安装指南 第二章 openEuler 网络配置 第三章 openEuler 文件系统 文章目录 系列文章目录前言一、openEuler中的文件系统1.整体架构2.文件系统层次结构3. FHS定义的一级目录结构 二、常用命令1. pwd 查看当前所在的目录路径2. ls 查看当前目录下…

算法——各排序算法效率对比和总结

1. 算法效率对比 在此我们使用如下代码来测试各个算法的效率 void TestOP() {srand(time(0));const int N 100000;int* a1 (int*)malloc(sizeof(int) * N);int* a2 (int*)malloc(sizeof(int) * N);int* a3 (int*)malloc(sizeof(int) * N);int* a4 (int*)malloc(sizeof(i…

【TOP生物信息】CNS图表复现,单细胞marker基因展示的另一种方式——蜂巢图

扫码关注下方公粽号&#xff0c;回复推文合集&#xff0c;获取400页单细胞学习资源&#xff01; 本文共计1359字&#xff0c;阅读大约需要4分钟。 Sten Linnarsson大神的单细胞绘图堪称极致美学&#xff0c;在这里&#xff0c;小编选择了发表在nature上展示marker基因的绘图进…

找不到msvcp140.dll无法继续执行代码,怎么解决?

MSVCP140.dll是一个Windows操作系统中的动态链接库文件&#xff0c;它是Microsoft Visual C Redistributable for Visual Studio 2015的一部分。这些库提供了一些C程序所需的基本功能&#xff0c;例如内存管理、文件操作、字符串处理等等。丢失或者损坏会导致很多软件跟游戏无法…

由浅入深Netty简易实现RPC框架

目录 1 准备工作2 服务器 handler3 客户端代码第一版4 客户端 handler 第一版5 客户端代码 第二版6 客户端 handler 第二版 1 准备工作 这些代码可以认为是现成的&#xff0c;无需从头编写练习 为了简化起见&#xff0c;在原来聊天项目的基础上新增 Rpc 请求和响应消息 Data …

由浅入深Netty代码调优

目录 1. 优化1.1 扩展序列化算法 2 参数调优2.1 CONNECT_TIMEOUT_MILLIS2.2 SO_BACKLOG2.3 ulimit -n2.4 TCP_NODELAY2.5 SO_SNDBUF & SO_RCVBUF2.6 ALLOCATOR2.7 RCVBUF_ALLOCATOR 1. 优化 1.1 扩展序列化算法 序列化&#xff0c;反序列化主要用在消息正文的转换上 序列…