OPC UA - Open62541学习
前言
這幾天看了看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.RequestUA_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 NodeIDUA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER),UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),UA_QUALIFIEDNAME(0, "VariableNode"),UA_NODEID_NULL, // no variable typevar_attr, &var_id);if (retval == UA_STATUSCODE_GOOD)printf("Created 'NewVariable' with numeric NodeID %u\n", var_id.identifier.numeric);這個功能我暫時還沒有測試,等有時間再深入研究。
結束
很佩服這些做開源軟件的個人和機構,無私的他們,使我們開發更加經濟高效,這種精神值得我們學習。也歡迎加入我的QQ群:633204942? ,我們一起學習。
windows 下編譯的動態庫和頭文件
Linux 下編譯的靜態庫文件
總結
以上是生活随笔為你收集整理的OPC UA - Open62541学习的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: k8s进阶篇-云原生存储ceph
- 下一篇: 频谱分析仪二次开发|频谱分析仪程控软件N
