OPC UA - Open62541学习

1 篇文章 0 订阅

前言

这几天看了看Open62541,这是用C语言遵循C99标准实现的一个开源跨平台OPC UA库。学习过程中发现好多不明白的运行结果,可能是自己没搞太明白,也可能是源码有些潜在bug,希望大神看到指导下小弟。这里简单记录下自己的学习过程和测试结果,算是个小笔记?

一:编译

按着文档编译下

Linux:

sudo apt-get install git build-essential gcc pkg-config cmake python python-six
# enable additional features
sudo apt-get install cmake-curses-gui # for the ccmake graphical interface
sudo apt-get install libmbedtls-dev # for encryption support
sudo apt-get install check # for unit tests
sudo apt-get install python-sphinx graphviz # for documentation generation
sudo apt-get install python-sphinx-rtd-theme # documentation style

这些都是一些环境,联网后敲上去安装就好了。

cd open62541
mkdir build
cd build

到文件夹下,创建build文件件,然后到build文件夹下执行编译指令。

这里注意下编译参数,文档上写的很清楚:

CMAKE_BUILD_TYPE
• RelWithDebInfo -O2 optimization with debug symbols
• Release -O2 optimization without debug symbols
• Debug -O0 optimization with debug symbols
• MinSizeRel -Os optimization without debug symbols

最好把参数写上,我不写参数就出错。

 

很快就编译完成了,就可以拿去开发了。

Windows:

2.2.2 Building with CMake on Windows
Here we explain the build process for Visual Studio (2013 or newer). To build with MinGW, just replace the
compiler selection in the call to CMake.
• Download and install
– Python 2.7.x (Python 3.x works as well): https://python.org/downloads
– Install python-six with the pip package manager (pip install six)
– CMake: http://www.cmake.org/cmake/resources/software.html
– Microsoft Visual Studio: https://www.visualstudio.com/products/visual-studio-community-vs
• Download the open62541 sources (using git or as a zipfile from github)
• Open a command shell (cmd) and run
cd <path-to>\open62541
mkdir build
cd build
<path-to>\cmake.exe .. -G "Visual Studio 14 2015"
:: You can use use cmake-gui for a graphical user-interface to select features
• Then open buildopen62541.sln in Visual Studio 2015 and build as usual

这些文档写的都很清楚,按着一步一步来就好了,注意配置的时候可以选动态库、可以选动态库、也可以直接用open62541.c和open62541.h 。

二:实用方法

1.先把头文件和源文件添加到项目中

2.功能简单测试

枚举服务节点

	char *uri = "opc.tcp://127.0.0.1:49320";
	/* Listing endpoints */
	UA_EndpointDescription* endpointArray = NULL;
	size_t endpointArraySize = 0;
	UA_StatusCode retval = UA_Client_getEndpoints(client, uri,
		&endpointArraySize, &endpointArray);
	if (retval != UA_STATUSCODE_GOOD) {
		UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
		UA_Client_delete(client);
		return (int)retval;
	}
	printf("%i endpoints found\n", (int)endpointArraySize);
	for (size_t i = 0; i<endpointArraySize; i++) {
		printf("URL of endpoint %i is %.*s\n", (int)i,
			(int)endpointArray[i].endpointUrl.length,
			endpointArray[i].endpointUrl.data);
	}
	UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);

连接OPC UA服务

UA_Client *client = UA_Client_new(UA_ClientConfig_default);
	char *uri = "opc.tcp://127.0.0.1:49320";
retval = UA_Client_connect(client, uri);
	if (retval != UA_STATUSCODE_GOOD) {
		UA_Client_delete(client);
		return (int)retval;
	}

这里还可以用指定用户名和密码去连接,连接函数原型为:但是我没有测试

UA_StatusCode
UA_Client_connect_username(UA_Client *client, const char *endpointUrl,
                           const char *username, const char *password) {
    client->authenticationMethod = UA_CLIENTAUTHENTICATION_USERNAME;
    client->username = UA_STRING_ALLOC(username);
    client->password = UA_STRING_ALLOC(password);
    return UA_Client_connect(client, endpointUrl);
}

浏览指定节点下NODE

static void BrowseNode(UA_Client *client,char* nodeIdStr)
{
	/* Browse some objects */
	if (nodeIdStr == NULL)
	{
		printf("nodeIdStr == NULL");
		return;
	}
	printf("Browsing nodes in objects folder:\n");
	 UA_NodeId nodeId = UA_NODEID_STRING(2, nodeIdStr);
	//UA_NodeId nodeId = UA_NODEID_NUMERIC(0, 8);
	UA_BrowseRequest bReq ;
	UA_BrowseRequest_init(&bReq);
	bReq.requestedMaxReferencesPerNode = 0;
	bReq.nodesToBrowse = UA_BrowseDescription_new();
	bReq.nodesToBrowseSize = 1;
	bReq.nodesToBrowse[0].nodeId = nodeId;
	bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */
	UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);
	printf("%-9s %-16s %-16s %-16s\n", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");
	for (size_t i = 0; i < bResp.resultsSize; ++i) {
		for (size_t j = 0; j < bResp.results[i].referencesSize; ++j) {
			UA_ReferenceDescription *ref = &(bResp.results[i].references[j]);
			if (ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_NUMERIC) {
				printf("%-9d %-16d %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
					ref->nodeId.nodeId.identifier.numeric, (int)ref->browseName.name.length,
					ref->browseName.name.data, (int)ref->displayName.text.length,
					ref->displayName.text.data);
			}
			else if (ref->nodeId.nodeId.identifierType == UA_NODEIDTYPE_STRING) {
				printf("%-9d %-16.*s %-16.*s %-16.*s\n", ref->nodeId.nodeId.namespaceIndex,
					(int)ref->nodeId.nodeId.identifier.string.length,
					ref->nodeId.nodeId.identifier.string.data,
					(int)ref->browseName.name.length, ref->browseName.name.data,
					(int)ref->displayName.text.length, ref->displayName.text.data);
			}
		}
	}
	UA_BrowseDescription_deleteMembers(&bReq);
	//UA_BrowseRequest_deleteMembers(&bReq);
	UA_BrowseResponse_deleteMembers(&bResp);
}

这里遇到个小问题,其实应该用UA_BrowseRequest_deleteMembers(&bReq);来释放资源,浏览根节点释放资源时没问题,指定Node去浏览后 释放就报错,这样写同样可以起到释放的效果。

UA_BrowseDescription_deleteMembers(&bReq);

这里还有一种枚举指定节点下NODE的方法

	UA_NodeId *parent = UA_NodeId_new();
	*parent = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
	UA_Client_forEachChildNodeCall(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
		nodeIter, (void *)parent);
	UA_NodeId_delete(parent);

static UA_StatusCode
nodeIter(UA_NodeId childId, UA_Boolean isInverse, UA_NodeId referenceTypeId, void *handle) {
	if (isInverse)
		return UA_STATUSCODE_GOOD;
	UA_NodeId *parent = (UA_NodeId *)handle;
	printf("%d, %d --- %d ---> NodeId %d, %d\n",
		parent->namespaceIndex, parent->identifier.numeric,
		referenceTypeId.identifier.numeric, childId.namespaceIndex,
		childId.identifier.numeric);
	return UA_STATUSCODE_GOOD;
}

每个子节点会以回调的方式反馈给我们。

读指定Node

UA_Variant *val = UA_Variant_new();
	UA_LocalizedText outDisplayName;
	UA_NodeId nodeId = UA_NODEID_STRING(2, nodeIdStr);
	//CX62KX63.ER.Queue.CarVin
	//CX62KX63.ER.Queue.Request
	UA_StatusCode retval = UA_Client_readValueAttribute(client, nodeId, val);

主动读的时候需要注意,根据指定node的数据类型指定类型来接收,不然可能拿不到或者拿到错误的数据,因为C语言嘛,比较基于内存。

读的时候很灵活可以用底层的

__UA_Client_readAttribute(UA_Client *client, const UA_NodeId *nodeId,
                          UA_AttributeId attributeId, void *out,
                          const UA_DataType *outDataType);

指定UA_AttributeId 去读。但是推荐使用包装后的读取方式,官方文档上有说明

static UA_INLINE UA_StatusCode
UA_Client_readNodeIdAttribute(UA_Client *client, const UA_NodeId nodeId,
                              UA_NodeId *outNodeId) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
                                     outNodeId, &UA_TYPES[UA_TYPES_NODEID]);
}

static UA_INLINE UA_StatusCode
UA_Client_readNodeClassAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 UA_NodeClass *outNodeClass) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
                                     outNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
}

static UA_INLINE UA_StatusCode
UA_Client_readBrowseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  UA_QualifiedName *outBrowseName) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                     outBrowseName,
                                     &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
}

static UA_INLINE UA_StatusCode
UA_Client_readDisplayNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   UA_LocalizedText *outDisplayName) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                     outDisplayName,
                                     &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_readDescriptionAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   UA_LocalizedText *outDescription) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                     outDescription,
                                     &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_readWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 UA_UInt32 *outWriteMask) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                     outWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
}

static UA_INLINE UA_StatusCode
UA_Client_readUserWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_UInt32 *outUserWriteMask) {
    return __UA_Client_readAttribute(client, &nodeId,
                                     UA_ATTRIBUTEID_USERWRITEMASK,
                                     outUserWriteMask,
                                     &UA_TYPES[UA_TYPES_UINT32]);
}

static UA_INLINE UA_StatusCode
UA_Client_readIsAbstractAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  UA_Boolean *outIsAbstract) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                     outIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_readSymmetricAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 UA_Boolean *outSymmetric) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
                                     outSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_readInverseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   UA_LocalizedText *outInverseName) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                     outInverseName,
                                     &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_readContainsNoLoopsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_Boolean *outContainsNoLoops) {
    return __UA_Client_readAttribute(client, &nodeId,
                                     UA_ATTRIBUTEID_CONTAINSNOLOOPS,
                                     outContainsNoLoops,
                                     &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_readEventNotifierAttribute(UA_Client *client, const UA_NodeId nodeId,
                                     UA_Byte *outEventNotifier) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EVENTNOTIFIER,
                                     outEventNotifier, &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_readValueAttribute(UA_Client *client, const UA_NodeId nodeId,
                             UA_Variant *outValue) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
                                     outValue, &UA_TYPES[UA_TYPES_VARIANT]);
}

static UA_INLINE UA_StatusCode
UA_Client_readDataTypeAttribute(UA_Client *client, const UA_NodeId nodeId,
                                UA_NodeId *outDataType) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                     outDataType, &UA_TYPES[UA_TYPES_NODEID]);
}

static UA_INLINE UA_StatusCode
UA_Client_readValueRankAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 UA_Int32 *outValueRank) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                     outValueRank, &UA_TYPES[UA_TYPES_INT32]);
}

UA_StatusCode UA_EXPORT
UA_Client_readArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       size_t *outArrayDimensionsSize,
                                       UA_UInt32 **outArrayDimensions);

static UA_INLINE UA_StatusCode
UA_Client_readAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   UA_Byte *outAccessLevel) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                     outAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_readUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       UA_Byte *outUserAccessLevel) {
    return __UA_Client_readAttribute(client, &nodeId,
                                     UA_ATTRIBUTEID_USERACCESSLEVEL,
                                     outUserAccessLevel,
                                     &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_readMinimumSamplingIntervalAttribute(UA_Client *client,
                                               const UA_NodeId nodeId,
                                               UA_Double *outMinSamplingInterval) {
    return __UA_Client_readAttribute(client, &nodeId,
                                     UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                     outMinSamplingInterval,
                                     &UA_TYPES[UA_TYPES_DOUBLE]);
}

static UA_INLINE UA_StatusCode
UA_Client_readHistorizingAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   UA_Boolean *outHistorizing) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
                                     outHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_readExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  UA_Boolean *outExecutable) {
    return __UA_Client_readAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                     outExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_readUserExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      UA_Boolean *outUserExecutable) {
    return __UA_Client_readAttribute(client, &nodeId,
                                     UA_ATTRIBUTEID_USEREXECUTABLE,
                                     outUserExecutable,
                                     &UA_TYPES[UA_TYPES_BOOLEAN]);
}

看这些函数命名,几乎不用注释就可以猜到每个函数读的是什么内容。

写指定Node

static bool WriteValue(UA_Client *client,char* nodeIdStr,void *value,UINT type)
{
	boolean result = true;
	
	UA_Variant *myVariant = UA_Variant_new();
	UA_NodeId nodeId = UA_NODEID_STRING(2, nodeIdStr);
	UA_StatusCode code = UA_Variant_setScalarCopy(myVariant, value, &UA_TYPES[type]);
	if(code != UA_STATUSCODE_GOOD)
	{
		result = false;
		UA_Variant_delete(myVariant);
		return result;
	}
	code=UA_Client_writeValueAttribute(client, nodeId, myVariant);
	if (code != UA_STATUSCODE_GOOD)
	{
		result = false;
		UA_Variant_delete(myVariant);
		return result;
	}
	UA_Variant_delete(myVariant);
	return result;
}

我是测试写了个字符串,因为相对字符串要比int、bool这些基本类型字符串读写的时候更需要注意。

和读一样,写也有一个比较低级的函数

UA_StatusCode UA_EXPORT
__UA_Client_writeAttribute(UA_Client *client, const UA_NodeId *nodeId,
                           UA_AttributeId attributeId, const void *in,
                           const UA_DataType *inDataType);

指定UA_AttributeId 去写。但是推荐比较高级的包装函数。

static UA_INLINE UA_StatusCode
UA_Client_writeNodeIdAttribute(UA_Client *client, const UA_NodeId nodeId,
                               const UA_NodeId *newNodeId) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODEID,
                                      newNodeId, &UA_TYPES[UA_TYPES_NODEID]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeNodeClassAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  const UA_NodeClass *newNodeClass) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_NODECLASS,
                                      newNodeClass, &UA_TYPES[UA_TYPES_NODECLASS]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeBrowseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   const UA_QualifiedName *newBrowseName) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_BROWSENAME,
                                      newBrowseName,
                                      &UA_TYPES[UA_TYPES_QUALIFIEDNAME]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeDisplayNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    const UA_LocalizedText *newDisplayName) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DISPLAYNAME,
                                      newDisplayName,
                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeDescriptionAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    const UA_LocalizedText *newDescription) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DESCRIPTION,
                                      newDescription,
                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  const UA_UInt32 *newWriteMask) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_WRITEMASK,
                                      newWriteMask, &UA_TYPES[UA_TYPES_UINT32]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeUserWriteMaskAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_UInt32 *newUserWriteMask) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_USERWRITEMASK,
                                      newUserWriteMask,
                                      &UA_TYPES[UA_TYPES_UINT32]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeIsAbstractAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   const UA_Boolean *newIsAbstract) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ISABSTRACT,
                                      newIsAbstract, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeSymmetricAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  const UA_Boolean *newSymmetric) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_SYMMETRIC,
                                      newSymmetric, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeInverseNameAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    const UA_LocalizedText *newInverseName) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_INVERSENAME,
                                      newInverseName,
                                      &UA_TYPES[UA_TYPES_LOCALIZEDTEXT]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeContainsNoLoopsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_Boolean *newContainsNoLoops) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_CONTAINSNOLOOPS,
                                      newContainsNoLoops,
                                      &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeEventNotifierAttribute(UA_Client *client, const UA_NodeId nodeId,
                                      const UA_Byte *newEventNotifier) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_EVENTNOTIFIER,
                                      newEventNotifier,
                                      &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeValueAttribute(UA_Client *client, const UA_NodeId nodeId,
                              const UA_Variant *newValue) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUE,
                                      newValue, &UA_TYPES[UA_TYPES_VARIANT]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeDataTypeAttribute(UA_Client *client, const UA_NodeId nodeId,
                                 const UA_NodeId *newDataType) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_DATATYPE,
                                      newDataType, &UA_TYPES[UA_TYPES_NODEID]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeValueRankAttribute(UA_Client *client, const UA_NodeId nodeId,
                                  const UA_Int32 *newValueRank) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_VALUERANK,
                                      newValueRank, &UA_TYPES[UA_TYPES_INT32]);
}

UA_StatusCode UA_EXPORT
UA_Client_writeArrayDimensionsAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        size_t newArrayDimensionsSize,
                                        const UA_UInt32 *newArrayDimensions);

static UA_INLINE UA_StatusCode
UA_Client_writeAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    const UA_Byte *newAccessLevel) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_ACCESSLEVEL,
                                      newAccessLevel, &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeUserAccessLevelAttribute(UA_Client *client, const UA_NodeId nodeId,
                                        const UA_Byte *newUserAccessLevel) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_USERACCESSLEVEL,
                                      newUserAccessLevel,
                                      &UA_TYPES[UA_TYPES_BYTE]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeMinimumSamplingIntervalAttribute(UA_Client *client,
                                                const UA_NodeId nodeId,
                                                const UA_Double *newMinInterval) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_MINIMUMSAMPLINGINTERVAL,
                                      newMinInterval, &UA_TYPES[UA_TYPES_DOUBLE]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeHistorizingAttribute(UA_Client *client, const UA_NodeId nodeId,
                                    const UA_Boolean *newHistorizing) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_HISTORIZING,
                                      newHistorizing, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                   const UA_Boolean *newExecutable) {
    return __UA_Client_writeAttribute(client, &nodeId, UA_ATTRIBUTEID_EXECUTABLE,
                                      newExecutable, &UA_TYPES[UA_TYPES_BOOLEAN]);
}

static UA_INLINE UA_StatusCode
UA_Client_writeUserExecutableAttribute(UA_Client *client, const UA_NodeId nodeId,
                                       const UA_Boolean *newUserExecutable) {
    return __UA_Client_writeAttribute(client, &nodeId,
                                      UA_ATTRIBUTEID_USEREXECUTABLE,
                                      newUserExecutable,
                                      &UA_TYPES[UA_TYPES_BOOLEAN]);
}

函数命名几乎和读一样,根据函数名称一目了然。

订阅模式

static void
handler_TheAnswerChanged(UA_Client *client, UA_UInt32 subId, void *subContext,
UA_UInt32 monId, void *monContext, UA_DataValue *value) {
	UA_String valueStr = *(UA_String*)value->value.data;
	printf("The Answer has changed! %s\n",valueStr.data);
}

UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
	UA_CreateSubscriptionResponse response = UA_Client_Subscriptions_create(client, request,
		NULL, NULL, NULL);

	UA_UInt32 subId = response.subscriptionId;
	if (response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
		printf("Create subscription succeeded, id %u\n", subId);

	UA_MonitoredItemCreateRequest monRequest =
		UA_MonitoredItemCreateRequest_default(UA_NODEID_STRING(2, "CX62KX63.ER.Queue.CarVin"));

	UA_MonitoredItemCreateResult monResponse =
		UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,
		UA_TIMESTAMPSTORETURN_BOTH,
		monRequest, NULL, handler_TheAnswerChanged, NULL);
	if (monResponse.statusCode == UA_STATUSCODE_GOOD)
		printf("Monitoring 'the.answer', id %u\n", monResponse.monitoredItemId);

	//UA_Client_Subscriptions_manuallySendPublishRequest(client);

	UA_StatusCode code = UA_Client_run_iterate(client, 10);  //lw这里报错,所以先注释掉

对于订阅模式,open62541.c 和 open62541.h和源代码不一致,找不到UA_StatusCode code = UA_Client_run_iterate(client, 10);这个函数(其实用了这个函数程序就编译不过去,我这里是因为我把源代码的一些东西拿过来了,所以能编译过)。也可以不用这个函数,用UA_Client_Subscriptions_manuallySendPublishRequest(client);这个函数,但是这个函数被标记废弃了

不过我测试了下,还可以用。

未测试的功能

添加NODE

	/* Add new nodes*/
	/* New ReferenceType */
	UA_NodeId ref_id;
	UA_ReferenceTypeAttributes ref_attr = UA_ReferenceTypeAttributes_default;
	ref_attr.displayName = UA_LOCALIZEDTEXT("en-US", "NewReference");
	ref_attr.description = UA_LOCALIZEDTEXT("en-US", "References something that might or might not exist");
	ref_attr.inverseName = UA_LOCALIZEDTEXT("en-US", "IsNewlyReferencedBy");
	retval = UA_Client_addReferenceTypeNode(client,
		UA_NODEID_NUMERIC(1, 12133),
		UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
		UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
		UA_QUALIFIEDNAME(1, "NewReference"),
		ref_attr, &ref_id);
	if (retval == UA_STATUSCODE_GOOD)
		printf("Created 'NewReference' with numeric NodeID %u\n", ref_id.identifier.numeric);

	/* New ObjectType */
	UA_NodeId objt_id;
	UA_ObjectTypeAttributes objt_attr = UA_ObjectTypeAttributes_default;
	objt_attr.displayName = UA_LOCALIZEDTEXT("en-US", "TheNewObjectType");
	objt_attr.description = UA_LOCALIZEDTEXT("en-US", "Put innovative description here");
	retval = UA_Client_addObjectTypeNode(client,
		UA_NODEID_NUMERIC(1, 12134),
		UA_NODEID_NUMERIC(0, UA_NS0ID_BASEOBJECTTYPE),
		UA_NODEID_NUMERIC(0, UA_NS0ID_HASSUBTYPE),
		UA_QUALIFIEDNAME(1, "NewObjectType"),
		objt_attr, &objt_id);
	if (retval == UA_STATUSCODE_GOOD)
		printf("Created 'NewObjectType' with numeric NodeID %u\n", objt_id.identifier.numeric);

	/* New Object */
	UA_NodeId obj_id;
	UA_ObjectAttributes obj_attr = UA_ObjectAttributes_default;
	obj_attr.displayName = UA_LOCALIZEDTEXT("en-US", "TheNewGreatNode");
	obj_attr.description = UA_LOCALIZEDTEXT("de-DE", "Hier koennte Ihre Webung stehen!");
	retval = UA_Client_addObjectNode(client,
		UA_NODEID_NUMERIC(1, 0),
		UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
		UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
		UA_QUALIFIEDNAME(1, "TheGreatNode"),
		UA_NODEID_NUMERIC(1, 12134),
		obj_attr, &obj_id);
	if (retval == UA_STATUSCODE_GOOD)
		printf("Created 'NewObject' with numeric NodeID %u\n", obj_id.identifier.numeric);

	/* New Integer Variable */
	UA_NodeId var_id;
	UA_VariableAttributes var_attr = UA_VariableAttributes_default;
	var_attr.displayName = UA_LOCALIZEDTEXT("en-US", "TheNewVariableNode");
	var_attr.description =
		UA_LOCALIZEDTEXT("en-US", "This integer is just amazing - it has digits and everything.");
	UA_Int32 int_value = 1234;
	/* This does not copy the value */
	UA_Variant_setScalar(&var_attr.value, &int_value, &UA_TYPES[UA_TYPES_INT32]);
	var_attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
	retval = UA_Client_addVariableNode(client,
		UA_NODEID_NUMERIC(1, 0), // Assign new/random NodeID
		UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),
		UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),
		UA_QUALIFIEDNAME(0, "VariableNode"),
		UA_NODEID_NULL, // no variable type
		var_attr, &var_id);
	if (retval == UA_STATUSCODE_GOOD)
		printf("Created 'NewVariable' with numeric NodeID %u\n", var_id.identifier.numeric);

这个功能我暂时还没有测试,等有时间再深入研究。

结束

很佩服这些做开源软件的个人和机构,无私的他们,使我们开发更加经济高效,这种精神值得我们学习。也欢迎加入我的QQ群:633204942  ,我们一起学习。

windows 下编译的动态库和头文件

Linux 下编译的静态库文件

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
OPCUA (OPC Unified Architecture)是一种开放的通信协议,用于实现工业自动化系统中不同设备之间的数据交换和通信。Open62541是一个开源的OPCUA实现,根据Mozilla Public License v2.0授权。在使用Open62541进行具体产品的开发过程中,可能会遇到一些困难。一方面,OPCUA信息模型非常复杂,另一方面,Open62541的文档相对较少,网络上的例子也大多是项目中的示例。因此,需要编写大量的自定义代码来实现所需功能。例如,如果在嵌入式设备上使用Open62541,需要修改Open62541的代码以支持IPv4协议,并将"hints.ai_family = AF_UNSPEC;"修改为"hints.ai_family = AF_INET;"。通过结合Open62541和modbus协议,可以实现OPCUA服务器代码与modbus协议的结合,从而进一步扩展OPCUA的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Windows下,OPC UA移植,open62541移植](https://blog.csdn.net/weixin_43782998/article/details/128461191)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [Open62541 为硬件设备(PLC)构建OPC UA模型](https://blog.csdn.net/yaojiawan/article/details/125466471)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [opcua开源代码OPEN62541的交叉编译](https://blog.csdn.net/u010835747/article/details/127637761)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值