前言
这几天看了看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
open62541
mkdir build
cd build
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
", (int)endpointArraySize);
for (size_t i = 0; i
连接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:
");
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
", "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
", 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
", 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
",
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
",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
", 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
", 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
", 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
", 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
", 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
", var_id.identifier.numeric);
这个功能我暂时还没有测试,等有时间再深入研究。
结束
---------------------
作者:liwen9016
来源:CSDN
原文:https://blog.csdn.net/liwen9016/article/details/81982662