学习open62541 --- [73] 数据源造成无法监测变量的问题解决

2023-05-16

本人最近遇到一个问题:给一个变量添加数据源后,使用监测项去监测变量变化,如果采样时间为0,会发现无法监测到变量的变化。

本文讲述这种情况的发生原因以及解决办法。


一 Server例子

首先准备server例子,如下,

// server.c

#include <signal.h>
#include <stdlib.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;
}


int32_t gData = 0;

UA_NodeId gTargetNodeId = UA_NODEID_STRING(1, (char*)"the.answer");


void addVariable(UA_Server * server)
{
   /* Define the attribute of the myInteger variable node */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_Int32 myInteger = 0;
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    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_INT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Add the variable node to the information model */
    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, gTargetNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}


UA_StatusCode onReadCallback(UA_Server *server, const UA_NodeId *sessionId,
                          void *sessionContext, const UA_NodeId *nodeId,
                          void *nodeContext, UA_Boolean includeSourceTimeStamp,
                          const UA_NumericRange *range, UA_DataValue *value) 
{
	UA_Variant_setScalarCopy(&(value->value), &gData, &UA_TYPES[UA_TYPES_INT32]);
    value->hasValue = UA_TRUE;

    return UA_STATUSCODE_GOOD;

}

UA_StatusCode onWriteCallback(UA_Server *server, const UA_NodeId *sessionId,
                           void *sessionContext, const UA_NodeId *nodeId,
                           void *nodeContext, const UA_NumericRange *range,
                           const UA_DataValue *value) 
{
    return UA_STATUSCODE_GOOD;
}

void addDataSourceToVariable(UA_Server *server) 
{
    UA_DataSource varDataSource;
    varDataSource.read = onReadCallback;
    varDataSource.write = onWriteCallback;
    UA_Server_setVariableNode_dataSource(server, gTargetNodeId, varDataSource);
}

void cycleCallback(UA_Server *server, void *data) 
{
    if ((++gData) == 10000)
    {
        gData = 0;
    }
}


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

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

    addVariable(server);
    addDataSourceToVariable(server);

    UA_UInt64 callbackId = 0;
	UA_Server_addRepeatedCallback(server, cycleCallback, NULL, 1000, &callbackId); // call every 1s

    UA_StatusCode retval = UA_Server_run(server, &running);
    
    UA_Server_delete(server);
    
    return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}


代码里添加了一个变量叫“the answer”,并添加了数据源用于读写操作,代码里有个全局变量gData,是“the answer”的源,每隔2秒会加1,当用户读取变量值时,会调用读回调onReadCallback,然后把gData的值传给用户。


二 测试

a)使用UaExpert的默认配置进行监测

编译server后运行,然后使用UaExpert连接server,
在这里插入图片描述
然后把变量“the answer”拖到Data Access View里,
在这里插入图片描述
可以看到值每隔1s就会加1,和预期一样,在server这边,会打印监测项的配置,如下,
在这里插入图片描述
可以看到采样时间是250ms,这个是UaExpert的默认配置

b)使用有变化才通知的监测设置

有时用户希望有变化才通知,不想按照一定时间间隔去采样,这样就需要对UaExpert进行配置,点击Setting,然后点击Configure UaExpert,如下,
在这里插入图片描述
在弹出的设置框里把采样时间的默认值改为0,
在这里插入图片描述
然后点击右下角的OK进行保存。

最后重启一下server,然后再使用UaExpert进行连接,连接成功后再把变量"the answer"拖拽到Data Access View窗口里,此时会发现数据不在变化了。
在这里插入图片描述
在Server这端可以看到这个监测项的设置,如下,采样时间是0
在这里插入图片描述


三 原因分析

由上可以看出,设置250ms采样,就可以监测到数据变化,而设置为有变化再通知(采样时间为0),就无法监测到数据的变化,其根本原因是:

  • 250ms采样,server内部会每隔250ms去读一下变量的值,这就会调用onReadCallback,这样就能拿到变化的值
  • 0ms采样,server内部不会去调用读接口去读取变量值,而是在对变量进行写的时候才去检查一下变量值是否发生变化,这样才能做到有变化才通知

server源码中创建监测项的的函数如下,

UA_StatusCode
UA_MonitoredItem_registerSampling(UA_Server *server, UA_MonitoredItem *mon) {
    UA_LOCK_ASSERT(&server->serviceMutex, 1);
    if(mon->sampleCallbackIsRegistered)
        return UA_STATUSCODE_GOOD;

    UA_assert(mon->next == (UA_MonitoredItem*)~0); /* Not registered in a node */

    /* Only DataChange MonitoredItems with a positive sampling interval have a
     * repeated callback. Other MonitoredItems are attached to the Node in a
     * linked list of backpointers. */
    UA_StatusCode res;
    if(mon->itemToMonitor.attributeId == UA_ATTRIBUTEID_EVENTNOTIFIER ||
       mon->parameters.samplingInterval == 0.0) {
        UA_Subscription *sub = mon->subscription;
        UA_Session *session = &server->adminSession;
        if(sub)
            session = sub->session;
        res = UA_Server_editNode(server, session, &mon->itemToMonitor.nodeId,
                                 addMonitoredItemBackpointer, mon);
    } else {
        res = addRepeatedCallback(server,
                                  (UA_ServerCallback)UA_MonitoredItem_sampleCallback,
                                  mon, mon->parameters.samplingInterval,
                                  &mon->sampleCallbackId);
    }

    if(res == UA_STATUSCODE_GOOD)
        mon->sampleCallbackIsRegistered = true;
    return res;
}

可以看到采样时间不为0,就会创建一个循环执行的任务,每隔一段时间就去读一下值。

如果采样时间为0,就会把这个监测项添加到node里的一个单链表里,该链表存放所有对该node创建的检测项(因为代码里可能会对同一个变量添加多个监测项),当对变量进行写时就会去检查是否发生变化并逐个进行通知,此时所使用的函数如下,

/* Trigger sampling if a MonitoredItem surveils the attribute with no sampling
 * interval */
#ifdef UA_ENABLE_SUBSCRIPTIONS
static void
triggerImmediateDataChange(UA_Server *server, UA_Session *session,
                           UA_Node *node, const UA_WriteValue *wvalue) {
    for(UA_MonitoredItem *mon = node->head.monitoredItems; mon != NULL; mon = mon->next) {
        if(mon->itemToMonitor.attributeId != wvalue->attributeId)
            continue;
        UA_DataValue value;
        UA_DataValue_init(&value);
        ReadWithNode(node, server, session, mon->timestampsToReturn,
                     &mon->itemToMonitor, &value);
        UA_Subscription *sub = mon->subscription;
        UA_StatusCode res = sampleCallbackWithValue(server, sub, mon, &value);
        if(res != UA_STATUSCODE_GOOD) {
            UA_DataValue_clear(&value);
            UA_LOG_WARNING_SUBSCRIPTION(&server->config.logger, sub,
                                        "MonitoredItem %" PRIi32 " | "
                                        "Sampling returned the statuscode %s",
                                        mon->monitoredItemId,
                                        UA_StatusCode_name(res));
        }
    }
}
#endif


四 解决办法

知道了原因,解决办法也比较简单,就是当源变化时,要更新一下对应变量的值,对应到代码里,就是在cycleCallback()里把gData的当前值写入到变量里,如下,

void cycleCallback(UA_Server *server, void *data) 
{
    if ((++gData) == 10000)
    {
        gData = 0;
    }

    UA_Variant value;
    UA_Variant_setScalarCopy(&(value), &gData, &UA_TYPES[UA_TYPES_INT32]);
    UA_Server_writeValue(server, gTargetNodeId, value);
}

这样就可以触发通知了,重新编译运行,就会发现可以监测到变量的变化了。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

学习open62541 --- [73] 数据源造成无法监测变量的问题解决 的相关文章

  • 通过gitlab远程统计git代码量

    git的代码量大多数都是根据命令行统计 xff0c 或者根据第三方插件统计 但是都不满足我的需求 xff0c 因为我们代码都由gitlab管理 xff0c 于是想到了通过gitlab暴露出来的接口获取数据 第一步 xff0c 生成私钥 登录
  • Qt第二十二章:将控件放到另一个控件的后面或前面

    话不多说 xff1a 看图
  • 缓存行填充与@sun.misc.Contended注解

    1 缓存模型 CPU和主内存之间有好几层缓存 xff0c 因为与cpu的速度相比 xff0c 访问主内存的速度是非常慢的 如果频繁对同一个数据做运算 xff0c 每次都从内存中加载 xff0c 运算完之后再写回到主内存中 xff0c 将会严
  • ThreadLocal那点事

    目录 1 ThreadLocal原理 2 ThreadLocal内存泄漏 3 ThreadLocal最佳实践 4 FastThreadLocal原理 5 FastThreadLocal最佳实践 6 ThreadLocal与FastThrea
  • 关于雪花算法的设计与思考

    2017年的时候项目组在开发一款大区游戏 xff0c 由于之前demo阶段的玩家id都是单服生成的 xff0c 只能保证单进程中的唯一 xff0c 而无法保证在分布式服务器端的唯一性 随着项目的开发进展 xff0c 需要设计能保证在分布式的
  • java反射之Method的invoke方法实现

    在框架中经常会会用到method invoke 方法 xff0c 用来执行某个的对象的目标方法 以前写代码用到反射时 xff0c 总是获取先获取Method xff0c 然后传入对应的Class实例对象执行方法 然而前段时间研究invoke
  • A*寻路算法之解决路径多拐点问题

    1 问题描述 最近公司正在开发的游戏涉及到了寻路算法 xff0c 然后我从网上找了一份A 算法代码 xff0c 整理了一下写了一个A 算法基础实现 然而 xff0c 在真正实用时A 寻路时 xff0c 却发现了几个问题 xff1a 基础实现
  • 代理模式与委托模式的异同点

    在 设计模式之禅 xff08 第二版 xff09 中 xff0c 作者说 代理模式也叫做委托模式 xff0c 显然是认为代理模式和委托模式是毫无差别的 然而在实际开发中 xff0c 我们通常可以很明确的知道一个模式究竟是代理模式还是委托模式
  • TCP/IP编程之select函数详解

    前述 xff1a linux下的I O复用模型目前很多都已经不用select函数了 xff0c 而是用epoll xff0c 但是为什么还需要了解select编程呢 xff0c 其实是从两个方面考虑的 xff1a 一是为了通过select去
  • 堆栈的详细解释

    一 在c中分为这几个存储区 1 栈 由编译器自动分配释放 2 堆 一般由程序员分配释放 xff0c 若程序员不释放 xff0c 程序结束时可能由OS回收 3 全局区 xff08 静态区 xff09 xff0c 全局变量和静态变量的存储是放在
  • Gmapping、hector、Cartographer三种激光SLAM算法简单对比

    文章目录 一 Gmapping是基于粒子滤波的算法 二 Hector SLAM三 Cartographer 一 Gmapping是基于粒子滤波的算法 缺点 xff1a 严重依赖里程计 xff0c 无法适应无人机及地面不平坦的区域 xff0c
  • TCP连接建立过程

    TCP连接建立过程 浏览器访问网站 xff0c 通过域名解析找到ip地址后会与服务器端建立连接 其中TCP xff08 Transmission Control Protocol xff0c 传输控制协议 xff09 是一种面向连接的 可靠
  • 海康威视错误代码文档大全【完整版】

    简介 本文收录了海康各大设备错误码 xff0c 按ctrl 43 f查询 xff1b 包含网络通讯库错误码 阵列错误码 安全激活相关错误码 智能设备错误码 RTSP通讯库错误码 软解码库错误码 转封装库错误码 语音对讲库错误码 Qos流控库

随机推荐

  • lighttpd http响应报文(Response)增加安全头Referrer-Policy和X-Permitted-Cross-Domain-Policies方法

    X Permitted Cross Domain Policies和Referrer Policy说明 X Permitted Cross Domain Policies X Permitted Cross Domain Policies
  • ROS使用ARUCO识别二维码获取位置信息做定位使用

    使用ARUCO识别二维码获取位置信息 1 安装软件 cd catkin ws src git clone b kinetic devel https github com pal robotics aruco ros cd catkin m
  • Keil 编译时无法找到头文件解决

    Keil 编译时无法找到头文件解决方法 1 背景 Keil 编译的时候无法找到头文件 xff0c 搜了下相关问题及解决方法 xff0c 有介绍说是因为文件夹中有数字 xff0c 无法搜到头文件 xff0c 进行了更改 xff0c 还是找不到
  • 学习open62541 --- [71] Alarm and Condition

    本文讲述Alarm and Condition的用法 xff0c 主要以源码里提供的例子为基础进行讲解和演示 xff0c 即tutorial server alarms conditions c xff0c 该例子写的有点乱 xff0c 本
  • 学习open62541 ---[68] 使用Wireshark观察通信消息

    Wireshark是强大的网络协议分析工具 xff0c 而open62541也是基于socket的 xff0c 所以也可以用其来观察OPCUA通信消息 一 安装Wireshark 去https www wireshark org 去下载并安
  • UART、IIC、SPI、CAN通信的区别与应用

    文章目录 1 通信的基本知识1 1 数据通信的种类1 1 1 串行通信1 1 2 并行通信1 1 3 总结 1 2 数据通信的传输方向1 2 1 单工1 2 2 半双工1 2 3 全双工1 2 4 总结 1 3 数据通信的方式1 3 1 同
  • 学习open62541 --- [69] Client监测多个变量值

    有读者问Client如何监测多个变量值 xff0c 这篇文章给了提示 xff0c 但是没给例子 xff0c 本文给出详细例子 xff0c 使用的open62541版本是1 3 3 xff0c 运行环境debian10 5 一 准备Serve
  • 学习CANopen --- [6] 自定义对象字典

    在前面几篇文章中 xff0c 我们运行例子时都需要一个eds文件 xff0c 比较麻烦 xff0c 本文讲述如何在代码中自定义对象字典 xff0c 只添加自己需要的OD项 如果是作为master来使用 xff0c 还是比较方便的 自定义对象
  • 学习CANopen --- [7] 使用块(Block)下载

    对于一次传输超过4字节的情形 xff0c SDO可以使用Segment传输或者Block传输 xff0c Segment传输在第6篇文章中已经介绍 xff0c 本文讲解Block传输中的下载情况 一 与Segment传输的比较 相比于Seg
  • 学习open62541 --- [70] 深入理解变量监测

    本文深入探讨一下变量监测的用法和原理 一 累积传输 先前写的关于变量监测的文章 xff0c 都是设置一个采样时间 xff0c 然后有变化了就通知一下 xff0c 有时我们希望变化累积一段时间再一起传给client xff0c 这时如何设置呢
  • 学习CANopen --- [8] 多主机同时运行时的问题

    本文记录一下实际使用CANopen时开启多个主机遇到的问题 一 问题描述 本人在嵌入式设备上用Python CANopen库写一个SDO xff0c 由于数据比较多 xff0c 就使用了Segment download xff0c 大概需要
  • 学习CANopen --- [9] CAN总线的状态检查

    本文讲述如何判断CAN总线是否存在以及是否bus off xff0c 以vcan0进行讲解 xff0c vcan0是虚拟的CAN接口 xff0c 可以把它看做一个软件CAN适配器 xff08 区别于硬件CAN适配器 xff0c 如PeakC
  • 学习CANopen --- [10] 汽车外接OBD模块原理

    在某宝上搜索汽车OBD xff0c 可以发现很多卖OBD模块的 xff0c 通过接入OBD模块可以增加车子本身没有的功能 xff0c 如锁车升窗 xff0c 行车自动落锁和后视镜折叠等 xff0c 那么其实现原理是什么呢 xff1f 使用时
  • 学习open62541 --- [72] client删除subscription时的warning

    本文记录一个问题的理解过程 xff0c 试验条件 xff1a 使用open62541运行server使用asyncua运行clientclient会向server添加subscription xff0c 然后在断开连接前删除subscrip
  • 启明欣欣STM32开发板闪烁LED实验

    最近在咸鱼上买了一块启明欣欣的STM32板子 xff0c 准备在上面测试open62541和CANopen xff0c 到货后如下图 xff0c 找商家要了资料 xff0c 然后运行一个LED灯的实验来简单测试下板子 xff0c 本文记录一
  • Python fpdf2生成表格

    fpdf2库可以用来生成pdf文档 xff0c 该库是从fpdf库fork来的 xff0c 老库自2018年就不再更新维护了 xff0c fpdf2对用户更友好 xff0c api也更方便使用 一 fpdf2介绍 其网址是https pyf
  • Android动态分析工具-Inspeckage

    1 Inspeckage简介 Inspeckage是一个用来动态分析安卓app的xposed模块 Inspeckage对动态分析很多常用的功能进行了汇总并且内建一个webserver 整个分析操作可以在友好的界面环境中进行 2 下载地址 I
  • 行为树 --- [7] BehaviorTree.CPP 4.x版本的编译及使用

    根据BehaviorTree CPP的官方介绍 xff0c 3 x版本已经不再维护了 xff0c 建议使用4 x版本 xff0c 4 x版本和3 x版本的区别可以看这里 https www behaviortree dev migratio
  • OPC UA性能评估

    本文是对这篇论文的总结 xff0c 该文章从性能和资源使用方面比较了工业4 0的4个主要协议 xff1a OPC UA xff0c DDS xff0c ROS和MQTT 这4个协议都是基于以太网 xff08 Ethernet based x
  • 学习open62541 --- [73] 数据源造成无法监测变量的问题解决

    本人最近遇到一个问题 xff1a 给一个变量添加数据源后 xff0c 使用监测项去监测变量变化 xff0c 如果采样时间为0 xff0c 会发现无法监测到变量的变化 本文讲述这种情况的发生原因以及解决办法 一 Server例子 首先准备se