实现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单次发送的主要内容,如果未能解决你的问题,请参考以下文章

西门子plc之间如何建立opc ua通讯

RS485转OPC UA

opc-ua milo项目的骆驼路线

Springboot 实现操作OPC ua Server 的数据读写 | 代码教程

Springboot 实现操作OPC ua Server 的数据读写 | 代码教程

实现OPC UA publish/subscribe单次发送