学习open62541

编程入门 行业动态 更新时间:2024-10-12 03:16:39

学习open62541

学习open62541

由于很多读者都问我如何实现Client一次读写多个节点,于是本人抽空简单研究了下,发现可以一次读写多个节点,但是同一次只能读或者写,不支持读写混合。


一 用到的API

一次读多个节点,用到UA_Client_Service_read(),其原型如下,

static UA_INLINE UA_ReadResponse
UA_Client_Service_read(UA_Client *client, const UA_ReadRequest request)

一次写多个节点,用到UA_Client_Service_write(),其原型如下,

static UA_INLINE UA_WriteResponse
UA_Client_Service_write(UA_Client *client, const UA_WriteRequest request)

二 例子

本例子只读写节点的value属性

Server代码

我们创建一个server,然后在server这边添加6个节点,3个是LocalizedText类型,3个是UInt32类型,

#include <signal.h>
#include <stdlib.h>#include "open62541.h"UA_Boolean running = true;static void stopHandler(int sign) {UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");running = false;
}static void addLocalizedTextVariable(UA_Server *server, char *name)
{/* Define the attribute of the myInteger variable node */UA_VariableAttributes attr = UA_VariableAttributes_default;UA_LocalizedText orig = UA_LOCALIZEDTEXT((char*)"en-US", "hello");UA_Variant_setScalar(&attr.value, &orig, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);attr.description = UA_LOCALIZEDTEXT("en-US", name);attr.displayName = UA_LOCALIZEDTEXT("en-US", name);attr.dataType = UA_TYPES[UA_TYPES_LOCALIZEDTEXT].typeId;attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;/* Add the variable node to the information model */UA_NodeId myNodeId = UA_NODEID_STRING(1, name);UA_QualifiedName myName = UA_QUALIFIEDNAME(1, name);UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);UA_Server_addVariableNode(server, myNodeId, parentNodeId, parentReferenceNodeId, myName,UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}static void addUInt32Variable(UA_Server *server, char *name)
{/* Define the attribute of the myInteger variable node */UA_VariableAttributes attr = UA_VariableAttributes_default;UA_UInt32 orig = 100;UA_Variant_setScalar(&attr.value, &orig, &UA_TYPES[UA_TYPES_UINT32]);attr.description = UA_LOCALIZEDTEXT("en-US", name);attr.displayName = UA_LOCALIZEDTEXT("en-US", name);attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;/* Add the variable node to the information model */UA_NodeId myNodeId = UA_NODEID_STRING(1, name);UA_QualifiedName myName = UA_QUALIFIEDNAME(1, name);UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);UA_Server_addVariableNode(server, myNodeId, parentNodeId, parentReferenceNodeId, myName,UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}int main(void)
{signal(SIGINT, stopHandler);signal(SIGTERM, stopHandler);UA_Server *server = UA_Server_new();UA_ServerConfig_setDefault(UA_Server_getConfig(server));addLocalizedTextVariable(server, "info1");addLocalizedTextVariable(server, "info2");addLocalizedTextVariable(server, "info3");addUInt32Variable(server, "uint1");addUInt32Variable(server, "uint2");addUInt32Variable(server, "uint3");UA_StatusCode retval = UA_Server_run(server, &running);UA_Server_delete(server);return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

代码比较简单,也就是在Objects目录下添加3个LocalizedText类型的子节点(默认值是“hello”)和3个UInt32类型的子节点(默认值是100)

编译运行后,使用UaExpert观察,

Client代码

代码如下,

#include <stdlib.h>
#include <stdio.h>
#include "open62541.h"static UA_StatusCode multiRead(UA_Client *client)
{const int arraySize = 6;UA_ReadValueId itemArray[arraySize];for (int i = 0; i < arraySize; ++i){UA_ReadValueId_init(&itemArray[i]);itemArray[i].attributeId = UA_ATTRIBUTEID_VALUE;}itemArray[0].nodeId = UA_NODEID_STRING(1, "info1");itemArray[1].nodeId = UA_NODEID_STRING(1, "info2");itemArray[2].nodeId = UA_NODEID_STRING(1, "info3");itemArray[3].nodeId = UA_NODEID_STRING(1, "uint1");itemArray[4].nodeId = UA_NODEID_STRING(1, "uint2");itemArray[5].nodeId = UA_NODEID_STRING(1, "uint3");UA_ReadRequest request;UA_ReadRequest_init(&request);request.nodesToRead = &itemArray[0];request.nodesToReadSize = arraySize;UA_ReadResponse response = UA_Client_Service_read(client, request);UA_StatusCode retStatusArray[arraySize];UA_StatusCode retval = response.responseHeader.serviceResult;if (retval == UA_STATUSCODE_GOOD){if (response.resultsSize == arraySize){for (int i = 0; i < arraySize; ++i){retStatusArray[i] = response.results[i].status;}}else{UA_ReadResponse_clear(&response);return UA_STATUSCODE_BADUNEXPECTEDERROR;}}for (int i = 0; i < arraySize; ++i){if (retStatusArray[i] == UA_STATUSCODE_GOOD){UA_DataValue res = response.results[i];if (!res.hasValue) // no value{UA_ReadResponse_clear(&response);return UA_STATUSCODE_BADUNEXPECTEDERROR;}UA_Variant out;memcpy(&out, &res.value, sizeof(UA_Variant));UA_Variant_init(&res.value);if (out.type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]){UA_LocalizedText * ptr = (UA_LocalizedText *)out.data;printf("Text: %.*s\n", ptr->text.length, ptr->text.data);}else if (out.type == &UA_TYPES[UA_TYPES_UINT32]){UA_UInt32 * ptr = (UA_UInt32 *)out.data;printf("UInt32 Value: %d\n", *ptr);}}}UA_ReadResponse_clear(&response);return UA_STATUSCODE_GOOD;
}UA_StatusCode multiWrite(UA_Client *client)
{const int arraySize = 6;UA_WriteValue wValueArray[arraySize];for (int i = 0; i < arraySize; ++i){UA_WriteValue_init(&wValueArray[i]);wValueArray[i].attributeId = UA_ATTRIBUTEID_VALUE;}wValueArray[0].nodeId = UA_NODEID_STRING(1, "info1");wValueArray[1].nodeId = UA_NODEID_STRING(1, "info2");wValueArray[2].nodeId = UA_NODEID_STRING(1, "info3");wValueArray[3].nodeId = UA_NODEID_STRING(1, "uint1");wValueArray[4].nodeId = UA_NODEID_STRING(1, "uint2");wValueArray[5].nodeId = UA_NODEID_STRING(1, "uint3");UA_Variant infoVar;UA_LocalizedText info1Value = UA_LOCALIZEDTEXT("en-US", "world1");UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &info1Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);wValueArray[0].value.value = infoVar;wValueArray[0].value.hasValue = true;UA_LocalizedText info2Value = UA_LOCALIZEDTEXT("en-US", "world2");UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &info2Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);wValueArray[1].value.value = infoVar;wValueArray[1].value.hasValue = true;UA_LocalizedText info3Value = UA_LOCALIZEDTEXT("en-US", "world3");UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &info3Value, &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);wValueArray[2].value.value = infoVar;wValueArray[2].value.hasValue = true;UA_UInt32 uint1Value = 101;UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &uint1Value, &UA_TYPES[UA_TYPES_UINT32]);wValueArray[3].value.value = infoVar;wValueArray[3].value.hasValue = true;UA_UInt32 uint2Value = 102;UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &uint2Value, &UA_TYPES[UA_TYPES_UINT32]);wValueArray[4].value.value = infoVar;wValueArray[4].value.hasValue = true;UA_UInt32 uint3Value = 103;UA_Variant_init(&infoVar);UA_Variant_setScalar(&infoVar, &uint3Value, &UA_TYPES[UA_TYPES_UINT32]);wValueArray[5].value.value = infoVar;wValueArray[5].value.hasValue = true;UA_WriteRequest wReq;UA_WriteRequest_init(&wReq);wReq.nodesToWrite = &wValueArray[0];wReq.nodesToWriteSize = arraySize;UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);UA_StatusCode retval = wResp.responseHeader.serviceResult;if (retval == UA_STATUSCODE_GOOD) {if (wResp.resultsSize == 1)retval = wResp.results[0];elseretval = UA_STATUSCODE_BADUNEXPECTEDERROR;}UA_WriteResponse_clear(&wResp);return retval;}int main(void)
{UA_Client *client = UA_Client_new();UA_ClientConfig_setDefault(UA_Client_getConfig(client));UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://localhost:4840");if (retval != UA_STATUSCODE_GOOD) {UA_Client_delete(client);return (int)retval;}printf("---- Before write ---- \n");multiRead(client);printf("\n\n");multiWrite(client);printf("---- After write ---- \n");multiRead(client);UA_Client_delete(client); /* Disconnects the client internally */return EXIT_SUCCESS;
}

函数multiRead()用于一次读取多个节点,这里是读取6个节点,即Server端添加的那6个节点;multiWrite()用于一次写多个节点,这里是写6个节点,即Server端添加的那6个节点。

这2个函数是对UA_Client_readValueAttribute()和UA_Client_writeValueAttribute()的扩展,原本这2个函数每次只能读/写一个节点值。

运行后结果如下,

可以看出写之前读出来的值和之前设置的默认值是一样的,写之后,值就变成了我们期望的值,所以运行没问题。

关键参数

比较关键的类型是AttributeId,其类型是UA_AttributeId,因为本文例子只读写节点值,所以AttributeId的值是UA_ATTRIBUTEID_VALUE

还有很多其它的AttributeId,如UA_ATTRIBUTEID_DATATYPE,UA_ATTRIBUTEID_VALUERANK等等,可以根据需要进行选择,支持不同属性的同时读或者写。


三 总结

本文主要讲述如何一次读或者写多个节点值,但是同一次不可读写混合,只能读或者只能写。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

更多推荐

学习open62541

本文发布于:2024-02-26 07:52:05,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1701780.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!