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.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);
這個功能我暫時還沒有測試,等有時間再深入研究。
結束
---------------------
作者:liwen9016
來源:CSDN
原文:https://blog.csdn.net/liwen9016/article/details/81982662