赞
踩
最近一直在写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 FX 现场交换总线。目前它是基于了OPC UA pub/sub 和tsn 构建的。pub/sub 模式简单,而且能够通过时间窗口实现定时发送。与tsn 完美结合在一起。
笔者认为,只有同时实现了连续发送和单独发送两种模式,才有可能成为现场总线协议。毕竟许多的场合需要确定的单个消息传递。本次测试,为以后进一步实现OPC UA Over tsn 打下了基础。
OPC UA 的心比较大,希望未来一统天下!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。