• 与想象不同的OPC UA publish/subscribe


         最近一直在写OPC UA publish/subscribe 模式的程序,发现程序设置好之后,数据就源源不断地发出来了。但是令人困惑的是变量值明明没有改变,数据会连续不断地重复发送,根本停不下来 。网络上也有人在抓狂,我只要一个数据就够了,不要没完没了。

          其实,OPC UA publish/subscribe 的机制与传统IT 领域的MQTT 发布/订阅机制不完全相同。MQTT 发布的信息是非周期性的消息。比如开/关灯,·某个数据的当前值等等。程序每次发送消息只能发送一个消息包。如果你需要周期性地发送数据,需要使用定时器来重复发送。

        OPC UA publish/subscribe是为了发送周期性数据而建立的通信机制。传统OT为实现如温度闭环、运动控制、机器人等控制,需要周期性的数据采集,OPC UA publish/subscribe用来解决“周期性”数据的传输问题。

           在OPC UA 协议栈中维护了一个周期性时钟,一旦这个时钟产生中断,服务器将会根据DatasetWrite 的配置,读取相关变量和事件的值,准备好Publish 的消息,从网络上发布出去。而周期是在PublishGroup 中配置的。它们以毫秒为单位。所有这些都不需要应用程序干预。这种异步周期发送的机制非常适合需要连续性采样数据的场合,比如过程控制,机器人动作控制等。

    只发送一个消息的方法

            对于许多控制数据而言,多次连续发送会带来麻烦,比如开关灯的操作。虽然我们可以在订阅端做判断,过滤掉重复的数据,但是那样会增加网络的带宽。

           是否能够实现数据不连续发送呢?网络上的例子几乎都是连续发送的。我不信邪,尝试了很长一段时间,终于成功了,在此处分享给大家。

        这里的关键是Publish端要将变量值的状态改为“UA_STATUSCODE_BADNOTCONNECTED“。下面是写变量程序。

    1. void writeVariableNode( UA_Server * server ,const UA_NodeId nodeId,UA_Int32 value)
    2. {
    3. //Write a different integer value
    4. UA_Int32 val=value;
    5. UA_Variant Var;
    6. UA_Variant_init(&Var);
    7. UA_Variant_setScalar(&Var, &val, &UA_TYPES[UA_TYPES_INT32]);
    8. UA_Server_writeValue(server, nodeId, Var);
    9. // Set the status code of the value to an error code. The function
    10. //UA_Server_write provides access to the raw service. The above
    11. //UA_Server_writeValue is syntactic sugar for writing a specific node
    12. //attribute with the write service.
    13. UA_WriteValue wv;
    14. UA_WriteValue_init(&wv);
    15. wv.nodeId = nodeId;
    16. wv.attributeId = UA_ATTRIBUTEID_VALUE;
    17. wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
    18. wv.value.value = Var;
    19. wv.value.value.type=&UA_TYPES[UA_TYPES_INT32];
    20. wv.value.hasValue = true;
    21. wv.value.hasStatus = true;
    22. UA_Server_write(server, &wv);
    23. }

     另外,addDataSetWriter程序中,keyFrameCounter 改为0。否则会出现message 丢弃, 只接受keyFrame  消息 。

    1. static void
    2. addDataSetWriter(UA_Server *server) {
    3. /* We need now a DataSetWriter within the WriterGroup. This means we must
    4. * create a new DataSetWriterConfig and add call the addWriterGroup function. */
    5. UA_NodeId dataSetWriterIdent;
    6. UA_DataSetWriterConfig dataSetWriterConfig;
    7. memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
    8. dataSetWriterConfig.name = UA_STRING("Demo DataSetWriter");
    9. dataSetWriterConfig.dataSetWriterId = 62542;
    10. dataSetWriterConfig.keyFrameCount = 0;//fixed by yao
    11. UA_Server_addDataSetWriter(server, writerGroupIdent, publishedDataSetIdent,
    12. &dataSetWriterConfig, &dataSetWriterIdent);
    13. }

    在Publisher 端,我添加了一个线程定时发送两个变量的值。

    1. void* threadFunction(void* server)
    2. { UA_Int32 variable1=0;
    3. UA_Int32 variable2=0;
    4. UA_Variant value;
    5. UA_Variant_init(&value);
    6. while(1)
    7. {
    8. variable2=100-variable1;
    9. writeVariableNode(server,UA_NODEID_NUMERIC(1,52525),variable1);
    10. writeVariableNode(server,UA_NODEID_NUMERIC(1,52510),variable2);
    11. printf("variable1 value%d\n",variable1);
    12. variable1++;
    13. if(variable1>100) variable1=0;
    14. UA_sleep_ms(100);
    15. }
    16. }

       OPC UA 现场总线

                在这里,我们也联想到了最近提出来的OPC UA FX 现场交换总线。目前它是基于了OPC UA pub/sub 和tsn 构建的。pub/sub 模式简单,而且能够通过时间窗口实现定时发送。与tsn 完美结合在一起。

          笔者认为,只有同时实现了连续发送和单独发送两种模式,才有可能成为现场总线协议。毕竟许多的场合需要确定的单个消息传递。本次测试,为以后进一步实现OPC UA Over tsn 打下了基础。

    OPC UA 的心比较大,希望未来一统天下!

        

  • 相关阅读:
    Qt之分类导航主界面
    工业楼控暖通组态恒温检测控制大屏前端UI案例
    vue3+vuex4
    docker仓库的搭建以及使用
    Web前端:开发人员如何为React Native App选择合适的数据库?
    2022.10.29每日一题
    SBT安装与配置
    【Python抢票神器】火车票枪票软件到底靠谱吗?实测—终极攻略。
    鸿鹄电子招投标系统:基于Spring Boot、Mybatis、Redis和Layui的企业电子招采平台源码与立项流程
    密码学基础知识
  • 原文地址:https://blog.csdn.net/yaojiawan/article/details/125904193