实现OPC UA publish/subscribe单次发送
Posted 姚家湾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实现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“。下面是写变量程序。
void writeVariableNode( UA_Server * server ,const UA_NodeId nodeId,UA_Int32 value)
//Write a different integer value
UA_Int32 val=value;
UA_Variant Var;
UA_Variant_init(&Var);
UA_Variant_setScalar(&Var, &val, &UA_TYPES[UA_TYPES_INT32]);
UA_Server_writeValue(server, nodeId, Var);
// Set the status code of the value to an error code. The function
//UA_Server_write provides access to the raw service. The above
//UA_Server_writeValue is syntactic sugar for writing a specific node
//attribute with the write service.
UA_WriteValue wv;
UA_WriteValue_init(&wv);
wv.nodeId = nodeId;
wv.attributeId = UA_ATTRIBUTEID_VALUE;
wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
wv.value.value = Var;
wv.value.value.type=&UA_TYPES[UA_TYPES_INT32];
wv.value.hasValue = true;
wv.value.hasStatus = true;
UA_Server_write(server, &wv);
另外,addDataSetWriter程序中,keyFrameCounter 改为0。否则会出现message 丢弃, 只接受keyFrame 消息 。
static void
addDataSetWriter(UA_Server *server)
/* We need now a DataSetWriter within the WriterGroup. This means we must
* create a new DataSetWriterConfig and add call the addWriterGroup function. */
UA_NodeId dataSetWriterIdent;
UA_DataSetWriterConfig dataSetWriterConfig;
memset(&dataSetWriterConfig, 0, sizeof(UA_DataSetWriterConfig));
dataSetWriterConfig.name = UA_STRING("Demo DataSetWriter");
dataSetWriterConfig.dataSetWriterId = 62542;
dataSetWriterConfig.keyFrameCount = 0;//fixed by yao
UA_Server_addDataSetWriter(server, writerGroupIdent, publishedDataSetIdent,
&dataSetWriterConfig, &dataSetWriterIdent);
在Publisher 端,我添加了一个线程定时发送两个变量的值。
void* threadFunction(void* server)
UA_Int32 variable1=0;
UA_Int32 variable2=0;
UA_Variant value;
UA_Variant_init(&value);
while(1)
variable2=100-variable1;
writeVariableNode(server,UA_NODEID_NUMERIC(1,52525),variable1);
writeVariableNode(server,UA_NODEID_NUMERIC(1,52510),variable2);
printf("variable1 value%d\\n",variable1);
variable1++;
if(variable1>100) variable1=0;
UA_sleep_ms(100);
OPC UA 现场总线
在这里,我们也联想到了最近提出来的OPC UA FX 现场交换总线。目前它是基于了OPC UA pub/sub 和tsn 构建的。pub/sub 模式简单,而且能够通过时间窗口实现定时发送。与tsn 完美结合在一起。
笔者认为,只有同时实现了连续发送和单独发送两种模式,才有可能成为现场总线协议。毕竟许多的场合需要确定的单个消息传递。本次测试,为以后进一步实现OPC UA Over tsn 打下了基础。
OPC UA 的心比较大,希望未来一统天下!
以上是关于实现OPC UA publish/subscribe单次发送的主要内容,如果未能解决你的问题,请参考以下文章
Springboot 实现操作OPC ua Server 的数据读写 | 代码教程