Learn open62541 --- [39] Client batch read and write

Since many readers have asked me how to implement the Client to read and write multiple nodes at once, I took the time to do a simple research and found that I can read and write multiple nodes at a time, but I can only read or write at the same time, and does not support mixed reading and writing.


One used API

To read multiple nodes at once, UA_Client_Service_read() is used. The prototype is as follows,

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

To write multiple nodes at once, UA_Client_Service_write() is used. The prototype is as follows,

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

Two examples

This example only reads and writes the value attribute of the node

Server code

We create a server, and then add 6 nodes on the server side, 3 are LocalizedText type, 3 are UInt32 type,

#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;
}

The code is relatively simple, that is, add 3 child nodes of LocalizedText type (the default value is "hello") and 3 child nodes of UInt32 type (the default value is 100) in the Objects directory

After compiling and running, use UaExpert to observe,
Insert picture description here

Client code

code show as below,

#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];
		else
			retval = 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;
}

The function multiRead() is used to read multiple nodes at once, here is to read 6 nodes, that is, the 6 nodes added on the server side; multiWrite() is used to write multiple nodes at once, here is to write 6 nodes, that is The 6 nodes added on the Server side.

These two functions are extensions of UA_Client_readValueAttribute() and UA_Client_writeValueAttribute(). Originally, these two functions can only read/write one node value at a time.

The results after running are as follows. It
Insert picture description here
can be seen that the value read before writing is the same as the default value set before. After writing, the value becomes the value we expect, so the operation is no problem.

key parameter

The key type is AttributeId, and its type is UA_AttributeId. Because the example in this article only reads and writes the node value, the value of AttributeId is UA_ATTRIBUTEID_VALUE

There are many other AttributeIds, such as UA_ATTRIBUTEID_DATATYPE, UA_ATTRIBUTEID_VALUERANK, etc., which can be selected as needed to support simultaneous reading or writing of different attributes.


Three summary

This article mainly describes how to read or write multiple node values ​​at a time, but the same time can not be read or written, only read or write only.

If there is something wrong with the writing, I hope to leave a message to correct it, thank you for reading.

Guess you like

Origin blog.csdn.net/whahu1989/article/details/110009077