OPC UA(开放式产品通信统一架构,Open Platform Communications Unified Architecture)是一种通信协议和通信架构,用于实现工业自动化系统中设备之间的数据交换和通信。
硬核技术分析OPC UA
Kepserver EX6配置opc ua服务端 以及客户端
opc uap官方参考文档
opc-ua技术资料网站汇总
OPC UA开源库介绍
UAExpert:
C++ UA Server SDK Document
C++库:
open62541的C++库:
open62541(C库)
官网:/
文档:.html
下载:
Building open62541
open62541专栏
open62541中文文档
Qt_OpcuaClient
OPCUAClient_Qt
QT实现OPC_UA客户端程序以及与OPC_UA服务器通信
使用KEPServerv6进行OPC_UA的服务器搭建
对象可包含:对象、变量和方法。引用类型是节点的基类
OPC UA学习心得 — 1 OPC基础
OPC UA学习心得 — 3 对象、变量和方法
工具:
KepServer的下载安装与使用说明
为了快速、完整地体验从 PLC( Programmable Logic Controller,编程逻辑控制器)采集数据、传输到上位机进行展示的完整流程,可基于 SIMATIC_PLCSIM_Advanced_V3 、 TIA Portal V16 、 KEPServerEX6 以及 UAExpert 搭建西门子 PLC 的仿真环境。
环境准备
1、安装SIMATIC_PLCSIM_Advanced_V3
先安装WinPcap_4_
再安装SIMATIC_PLCSIM_
SimEKBInstall2022. 选中需要的密钥,勾选你的软件,然后选择安装长密钥。
重启S7-PLCSIM Advanced V3.0
2、安装TIA Portal V16
双击安装
SimEKBInstall2022.在左侧列表双击TIA Portal,选择TIA Portal V16,勾选右侧的STEP 7 Professional V16,然后选择安装长密钥。
3、安装KepServer
4、安装UAExpert
OPC UA/DA协议库open62541的源码编译及案例测试
1、github下载:注意选择的是1.2版本,1.3.6编译pollfa有问题
2、git工具下载
git clone --recursive .git
git clone --recursive .git #国内gitee下载
#如果某些模块无法加载,重新调整网络后,继续加载确保无fail输出
cd open62541
git submodule update --remote --recursive
确保系统安装了cmake、MinGW、VC和python3工具,将工作路径加入环境变量path中。
MinGW直接使用的是Qt的minGW目录(D:QtQt5.12.3Toolsmingw730_64bin),将其加入到path中。
有两种编译方式:命令行编译、图形化编译。
#minGW编译
mkdir build_mingw #创建编译目录
cd build_mingw
cmake -G "MinGW Makefiles" .. -DUA_ENABLE_AMALGAMATION=ON
#如果提示无法查找到python3包,直接显式指定
cmake -G "MinGW Makefiles" .. -DUA_ENABLE_AMALGAMATION=ON -DPython3_EXECUTABLE="D:\workForSoftware\python36\"
#编译
mingw32-make -j4 #或cmake --build . --config release#vc编译
md build_vc
cd build_vc
#本文采用vs2017版本
cmake -G "Visual Studio 15 2017" .. -DUA_ENABLE_AMALGAMATION=ON -DPython3_EXECUTABLE="D:\workForSoftware\python36\"
cmake --build . --config release
#可能会出现 error C2220: 警告被视为错误 - 没有生成“object”文件,
#可以通过文本编辑工具将生成的open62541.c文件改为ANSI格式,然后再次编译
在open62541源码目录下新建build目录,将拖入cmake-gui,where to build the binary选择刚才新建的build目录,选择msvc64位,并勾选UA_ENABLE_AMALGAMATION,以便合并生成open62541.h和open62541.c
./configure --prefix=/home/xxx/open62541/install
make -j4
make install
源码编译:
#进入open62541目录
mkdir build_linux
cd build_linux
cmake .. -DUA_ENABLE_AMALGAMATION=ON -DUA_MULTITHREADING=100 -DPython3_EXECUTABLE="/usr/bin/python3"
make -j4
#open62541.c:68808:43: error: comparison is always false due to limited range of data type [-Werror=type-limits] if(i < T##_MIN || (i > 0 && (t)i >= T##_MAX))
#修改为:if(i < T##_MIN || (i > 0 && (t)i >= T##_MAX)) ,再次编译
make -j4
example编译:
#先调整
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_mingw)
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_vc)
set(build_dir ${PROJECT_SOURCE_DIR}/../../build_linux)#进入open62541目录
cd demo/server_test
mkdir build_linux
cd build_linux
cmake .. -DUA_ENABLE_AMALGAMATION=ON
make -j4
1、pro文件中加入定义和引入库
QMAKE_CFLAGS += -std=c99
DEFINES += UA_ARCHITECTURE_WIN32
LIBS += -lpthread libwsock32 libws2_32
win32: LIBS += -lWs2_32 #同上
2、服务端:Error binding a server socket: 以一种访问权限不允许的方式做了一个访问套接字的尝试。
127.0.0.1:4840端口被占用,即KEPServerEx 6.4 Runtime服务占用此端口,在服务管理中将其停止。
0.0.0.0:4840被占用,C:Program Files (x86)Common FilesOPC FoundationUADiscoverybin,安的太多了,对不上是哪个了。打开任务管理器,点击详细信息,找到PID为9952的进程,将其终止。
tasklist | findstr "19168"
正常的连接端口占用情况 :
open62541中文文档
UA_Boolean //bool
UA_SByte //int8_t
UA_Byte //uint8_t
UA_Int16 //int16_t
UA_UInt16 //uint16_t
UA_Int32 //int32_t
UA_UInt32 //uint32_t
UA_Int64 //int64_t
UA_UInt64 //uint64_t
UA_Float //float
UA_Double //doubleUA_StatusCode //状态 uint32_t
UA_String //字符串 长度加指针: size_t length; UA_Byte *data;
UA_DateTime //日期时间 int64_t 自1601年1月1日(UTC)以来100纳秒间隔的数量
UA_Guid //UA_Guid_equal()
UA_ByteString //字节字符串 UA_ByteString_allocBuffer()、UA_BYTESTRING()、UA_BYTESTRING_ALLOC()
UA_XmlElement //UA_String
UA_NodeId //结构体:类型、联合值//UA_NODEID_NUMERIC()、UA_NODEID_STRING()、UA_NODEID_STRING_ALLOC()//UA_NODEID_GUID()、UA_NODEID_BYTESTRING()、UA_NODEID_BYTESTRING_ALLOC()
UA_ExpandedNodeId //名称空间URI而不是索引,结构体:UA_EXPANDEDNODEID_NUMERIC()
UA_QualifiedName //
UA_LocalizedText //
UA_NumericRange //
UA_Variant //任何数据类型的值,结构体 UA_Variant_hasScalarType()、UA_Variant_setScalar()、UA_Variant_setScalarCopy()
UA_ExtensionObject //任何数据类型的标量,
UA_DataValue //关联状态代码和时间戳的数据值
UA_DiagnosticInfo //与StatusCode关联的详细错误和诊断信息的结构
UA_TYPES //该数组包含所有标准定义类型的描述。利用描述可进行通用操作,//T_init()、T_new()、T_copy()、T_deleteMembers()、T_delete()//UA_Int32_new()#define UA_TYPES_BOOLEAN 0#define UA_TYPES_SBYTE 1#define UA_TYPES_BYTE 2#define UA_TYPES_INT16 3#define UA_TYPES_UINT16 4#define UA_TYPES_INT32 5#define UA_TYPES_UINT32 6#define UA_TYPES_INT64 7#define UA_TYPES_UINT64 8#define UA_TYPES_FLOAT 9#define UA_TYPES_DOUBLE 10#define UA_TYPES_STRING 11#define UA_TYPES_DATETIME 12#define UA_TYPES_GUID 13#define UA_TYPES_BYTESTRING 14#define UA_TYPES_XMLELEMENT 15#define UA_TYPES_NODEID 16#define UA_TYPES_EXPANDEDNODEID 17#define UA_TYPES_STATUSCODE 18#define UA_TYPES_QUALIFIEDNAME 19#define UA_TYPES_LOCALIZEDTEXT 20#define UA_TYPES_EXTENSIONOBJECT 21#define UA_TYPES_DATAVALUE 22#define UA_TYPES_VARIANT 23#define UA_TYPES_#define UA_TYPES_NODECLASS 75#define UA_TYPES_EVENTNOTIFICATIONLIST 189
UA_UtcTime //UA_DateTime
UA_LocaleId //用户区域设置的标识符
UA_Duration //UA_Double (ms)
UA_DataType //
array //UA_Array_new()、UA_Array_copy()、UA_Array_delete()//随机数生成器
void UA_random_seed(UA_UInt64 seed);
UA_UInt32 UA_UInt32_random(void); /* no cryptographic entropy */
UA_Guid UA_Guid_random(void); /* no cryptographic entropy *///发现服务集:
Service_FindServers()
Service_FindServersOnNetwork()
Service_RegisterServer()
Service_OpenSecureChannel()
Service_CloseSecureChannel()
Service_ActivateSession()
Service_CloseSession()//NodeManagement服务集
Service_AddNodes()
Service_AddReferences()
Service_DeleteNodes()
Service_DeleteReferences()
Service_Browse() //发现指定节点的引用
Service_BrowseNext()
Service_TranslateBrowsePathsToNodeIds() //将文本节点路径转换为各自的ID
Service_RegisterNodes() //客户端用于注册他们知道将重复访问的节点
Service_UnregisterNodes()
Service_Read() //读节点的属性
Service_Write() //写节点的属性
Service_Call() //调用方法
Service_CreateMonitoredItems() //创建一个或多个MonitoredItem并将其添加到订阅
Service_DeleteMonitoredItems()
Service_ModifyMonitoredItems() //修改订阅的MonitoredItems
Service_SetMonitoringMode() //为订阅的一个或多个MonitoredItem设置监视模式
Service_CreateSubscription() //创建订阅
Service_ModifySubscription() //修改订阅
Service_SetPublishingMode() //在一个或多个订阅上启用通知发送
Service_Publish() //发布服务
Service_Republish() //请求订阅从其重新传输队列重新发布NotificationMessage
Service_DeleteSubscriptions() //删除属于客户端会话的一个或多个订阅UA_Node //节点,结构体
UA_VariableTypeNode //变量节点
UA_MethodNode //方法节点,定义可调用函数,并使用Call服务调用
UA_ObjectNode //对象节点,可能包含变量,方法和其他对象
UA_ObjectTypeNode //ObjectTypes提供对象的定义
UA_ReferenceTypeNode //
UA_DataTypeNode
UA_ViewNodeUA_Node_setAttributes() //UA_Server_new()
UA_Server_delete()
UA_Server_run()
UA_Server_run_startup()
UA_Server_run_iterate()
UA_Server_run_shutdown()UA_Server_addRepeatedCallback() //重复回调
UA_Server_changeRepeatedCallbackInterval()
UA_Server_removeRepeatedCallback()
UA_Server_read() //
UA_Server_write()
UA_Server_readNodeId()
UA_Server_readNodeClass()
UA_Server_readBrowseName()
UA_Server_readDisplayName()
UA_Server_readWriteMask()
UA_Server_readValue()
UA_Server_writeValue()
UA_Server_browse()UA_Server_register_discovery()
UA_Server_setVariableNode_dataSource()
UA_Server_setMethodNode_callback()
UA_Server_call()UA_Server_addVariableNode()
UA_Server_addObjectNode()
UA_Server_addObjectTypeNode()
UA_Server_addViewNode()
UA_Server_addReferenceTypeNode()
UA_Server_addDataTypeNode()
UA_Server_addDataSourceVariableNode()
UA_Server_addMethodNodeEx()
UA_Server_addMethodNode()
UA_Client //客户端结构体
UA_ClientConfig //客户端配置数据
UA_Client_new()
UA_Client_delete()
UA_Client_reset()
UA_Client_getState()
UA_Client_getContext()UA_Client_connect()
UA_Client_connect_username()
UA_Client_disconnect()
UA_Client_close()
UA_Client_manuallyRenewSecureChannel()
UA_Client_getEndpoints() //Gets a list of endpoints of a server
UA_Client_findServers() //Gets a list of all registered servers at the given server
UA_Client_findServersOnNetwork() //Get a list of all known server in the network
UA_Client_Service_read()
UA_Client_Service_write()
UA_Client_Service_call()
UA_Client_Service_addNodes()
UA_Client_Service_addReferences()
UA_Client_Service_deleteNodes()UA_Client_runAsync()
UA_Client_AsyncService_read()
UA_Client_readNodeIdAttribute()
UA_Client_writeNodeIdAttribute()
UA_Client_writeNodeClassAttribute()
UA_Client_writeValueAttribute()UA_Client_call() //方法调用UA_Client_addVariableNode()
UA_Client_addObjectNode()
UA_Client_addMethodNode()
UA_Client_deleteNode()UA_CreateSubscriptionRequest_default()
UA_Client_Subscriptions_create() //订阅
UA_Client_Subscriptions_modify()
UA_Client_Subscriptions_delete()
UA_Client_Subscriptions_deleteSingle()
UA_Client_Subscriptions_setPublishingMode()UA_Client_MonitoredItems_createDataChanges()
UA_Client_MonitoredItems_createEvents()
UA_Client_MonitoredItems_delete()
UA_Boolean running = true;static void stopHandler(int sign) {UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");running = false;
}int main(int argc, char **argv) {signal(SIGINT, stopHandler);signal(SIGTERM, stopHandler);UA_ServerConfig *config = UA_ServerConfig_new_default();UA_Server *server = UA_Server_new(config);UA_StatusCode retval;/* create nodes from nodeset */if (myNS(server) != UA_STATUSCODE_GOOD) {UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Could not add the example nodeset. ""Check previous output for any error.");retval = UA_STATUSCODE_BADUNEXPECTEDERROR;} else {UA_NodeId createdNodeId;UA_ObjectAttributes object_attr = UA_ObjectAttributes_default; object_attr.description = UA_LOCALIZEDTEXT("en-US", "A pump!");object_attr.displayName = UA_LOCALIZEDTEXT("en-US", "Pump1");// we assume that the myNS nodeset was added in namespace 2.// You should always use UA_Server_addNamespace to check what the// namespace index is for a given namespace URI. UA_Server_addNamespace// will just return the index if it is already added.UA_Server_addObjectNode(server, UA_NODEID_NUMERIC(1, 0), //UA_Server *server, const UA_NodeId requestedNewNodeIdUA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), //const UA_NodeId parentNodeId,UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES), //const UA_NodeId referenceTypeId,UA_QUALIFIEDNAME(1, "Pump1"), //const UA_QualifiedName browseName,UA_NODEID_NUMERIC(2, 1002), //const UA_NodeId typeDefinition,object_attr, NULL, &createdNodeId); //const UA_ObjectAttributes attr,//void *nodeContext, UA_NodeId *outNewNodeIdretval = UA_Server_run(server, &running);}UA_Server_delete(server);UA_ServerConfig_delete(config);return (int) retval;
}
使用UaExpert观察:
open62541版本的不同,使用方法也略有不同。
基于open62541库的OPC UA协议节点信息查询及多节点数值读写案例实践
open62541学习
官方代码:
UA_Client *client = UA_Client_new();
UA_ClientConfig_setDefault(UA_Client_getConfig(client));
UA_StatusCode status = UA_Client_connect(client, p://DESKTOP-xxxxx:4840"); //服务端默认的[0.0.0.0]4840
if(status != UA_STATUSCODE_GOOD)
{UA_Client_delete(client);return status;
}
......UA_Variant_clear(&value);
UA_Client_delete(client); /* Disconnects the client internally */
获取服务器端点(Endpoint)
char *uri = p://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 foundn", (int)endpointArraySize);
for (size_t i = 0; i<endpointArraySize; i++)
{printf("URL of endpoint %i is %.*sn", (int)i,(int)endpointArray[i].endpointUrl.length,endpointArray[i].endpointUrl.data);
}
UA_Array_delete(endpointArray, endpointArraySize, &UA_TYPES[UA_TYPES_ENDPOINTDESCRIPTION]);
UA_StatusCode status; //在connect时就创建了
UA_Variant value;
UA_Variant_init(&value);
status = UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "the.answer"), &value);
if(status == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_INT32]))
{printf("the value is: %in", *(UA_Int32*)value.data);
}
UA_Variant value;
UA_NodeId nodeId = UA_NODEID_NUMERIC(0, 27647);
status = UA_Client_readValueAttribute(client, nodeId, &value);if(status == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DOUBLE]))
{ UA_String *logic_value = (UA_String*) value.data;printf("logic value is: %sn", logic_value->data);
}
UA_Variant value;
UA_Variant_init(&value);
const UA_NodeId nodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER_SERVERSTATUS_CURRENTTIME);
retval = UA_Client_readValueAttribute(client, nodeId, &value);if(retval == UA_STATUSCODE_GOOD && UA_Variant_hasScalarType(&value, &UA_TYPES[UA_TYPES_DATETIME]))
{UA_DateTime raw_date = *(UA_DateTime *) value.data;UA_DateTimeStruct dts = UA_DateTime_toStruct(raw_date);UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "date is: %u-%u-%u %u:%u:%u.%03un",dts.day, h, ar, dts.hour, dts.min, dts.sec, dts.milliSec);
}
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);//value对应type类型的值 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;
}
open62541 client批量监测
open62541 浏览服务器中节点 博客中有其他相关文章
官方浏览节点
#include <open62541.h>
#include "common_amal.h"
#include <iostream>
#include <string>
#include <vector>using namespace std;void listTreeRecursive(UA_Client *client, UA_NodeId nodeId)
{size_t i, j;/* Browse some objects */UA_BrowseRequest bReq;UA_BrowseRequest_init(&bReq);questedMaxReferencesPerNode = desToBrowse = UA_BrowseDescription_new();desToBrowseSize = 1;UA_NodeId_copy(&nodeId, &desToBrowse[0].nodeId);desToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; /* return everything */UA_BrowseResponse bResp = UA_Client_Service_browse(client, bReq);UA_BrowseNextRequest bNextReq;UA_BrowseNextRequest_init(&bNextReq);// normally is set to 0, to get all the nodes, but we want to test leaseContinuationPoints = UA_inuationPoints = &sults[0].inuationPointsSize = 1;UA_BrowseNextResponse bNextResp = UA_Client_Service_browseNext(client, bNextReq);for ( i = 0; i < sultsSize; i++){for ( j = 0; j < sults[i].referencesSize; j++){UA_ReferenceDescription *ref = &(sults[i].references[j]);if ((ref->nodeClass == UA_NODECLASS_OBJECT || ref->nodeClass == UA_NODECLASS_VARIABLE||ref->nodeClass == UA_NODECLASS_METHOD)) {if (ref-&deId.identifierType == UA_NODEIDTYPE_NUMERIC) {printf("%-9d %-16d %-16.*s %-16.*sn", ref-&deId.namespaceIndex,ref-&deId.identifier.numeric, (int) ref->browseName.name.length,ref->browseName.name.data, (int) ref-&length,ref-&data);listTreeRecursive(client, UA_NODEID_NUMERIC(ref-&deId.namespaceIndex,ref-&deId.identifier.numeric));} else if (ref-&deId.identifierType == UA_NODEIDTYPE_STRING) {printf("%-9d %-16.*s %-16.*s %-16.*sn", ref-&deId.namespaceIndex,(int) ref-&deId.identifier.string.length,ref-&deId.identifier.string.data,(int) ref->browseName.name.length, ref->browseName.name.data,(int) ref-&length, ref-&data);listTreeRecursive(client, UA_NODEID_STRING(ref-&deId.namespaceIndex,(char *) ref-&deId.identifier.string.data));}}/* TODO: distinguish further types */}}for ( i = 0; i < sultsSize; i++){for ( j = 0; j < sults[i].referencesSize; j++){UA_ReferenceDescription *ref = &(sults[i].references[j]);if ((ref->nodeClass == UA_NODECLASS_OBJECT || ref->nodeClass == UA_NODECLASS_VARIABLE||ref->nodeClass == UA_NODECLASS_METHOD)) {if (ref-&deId.identifierType == UA_NODEIDTYPE_NUMERIC) {printf("%-9d %-16d %-16.*s %-16.*sn", ref-&deId.namespaceIndex,ref-&deId.identifier.numeric, (int) ref->browseName.name.length,ref->browseName.name.data, (int) ref-&length,ref-&data);listTreeRecursive(client, UA_NODEID_NUMERIC(ref-&deId.namespaceIndex,ref-&deId.identifier.numeric));} else if (ref-&deId.identifierType == UA_NODEIDTYPE_STRING) {printf("%-9d %-16.*s %-16.*s %-16.*sn", ref-&deId.namespaceIndex,(int) ref-&deId.identifier.string.length,ref-&deId.identifier.string.data,(int) ref->browseName.name.length, ref->browseName.name.data,(int) ref-&length, ref-&data);listTreeRecursive(client, UA_NODEID_STRING(ref-&deId.namespaceIndex,(char *) ref-&deId.identifier.string.data));}}/* TODO: distinguish further types */}}UA_BrowseRequest_deleteMembers(&bReq);UA_BrowseResponse_deleteMembers(&bResp);
}int main(int argc, char **argv)
{UA_Client *client = UA_Client_new();UA_ClientConfig_setDefault(UA_Client_getConfig(client));UA_StatusCode retval = UA_Client_connect(client, p://192.168.3.50:4840");if(retval != UA_STATUSCODE_GOOD) {printf("Unable to connect!");UA_Client_delete(client);return (int)retval;}printf("Browsing nodes in objects folder:n");printf("%-9s %-16s %-16s %-16sn", "NAMESPACE", "NODEID", "BROWSE NAME", "DISPLAY NAME");//listTreeRecursive(client, UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));listTreeRecursive(client, UA_NODEID_STRING_ALLOC(3, "PLC"));printf("Program Endn");/* Clean up */UA_Client_delete(client); /* Disconnects the client internally */return UA_STATUSCODE_GOOD;
}
Log 输出:
/mnt/c/Users/eirik/Documents/RocketFarmAS/OPCUATest/browseTest
[2019-09-05 13:27:29.668 (UTC+0200)] info/client Connecting to p://192.168.3.50:4840
[2019-09-05 13:27:29.668 (UTC+0200)] info/client SecurityPolicy not specified -> use default #None
[2019-09-05 13:27:29.668 (UTC+0200)] warn/securitypolicy Security policy None is used to create SecureChannel. Accepting all certificates
[2019-09-05 13:27:29.671 (UTC+0200)] info/client TCP connection established
[2019-09-05 13:27:29.682 (UTC+0200)] info/client Opened SecureChannel with SecurityPolicy
[2019-09-05 13:27:29.682 (UTC+0200)] info/client Endpoint and UserTokenPolicy unconfigured, perform GetEndpoints
[2019-09-05 13:27:29.696 (UTC+0200)] info/client Found 7 endpoints
[2019-09-05 13:27:29.696 (UTC+0200)] info/client Endpoint 0 has 2 user token policies
[2019-09-05 13:27:29.696 (UTC+0200)] info/client Selected p://192.168.3.50:4840 with SecurityMode None and SecurityPolicy
[2019-09-05 13:27:29.696 (UTC+0200)] info/client Selected UserTokenPolicy Anonymous with UserTokenType Anonymous and SecurityPolicy
Browsing nodes in objects folder:
NAMESPACE NODEID BROWSE NAME DISPLAY NAME
3 Counters Counters Counters
3 5204 Icon Icon
3 "Counter" Counter Counter
3 DataBlocksGlobal DataBlocksGlobal DataBlocksGlobal
3 5202 Icon Icon
3 "dbOpc" dbOpc dbOpc
3 "dbOpc"."Sine01" Sine01 Sine01
3 "dbOpc"."Sine02" Sine02 Sine02
3 "dbOpc"."Sine03" Sine03 Sine03
3 "dbOpc"."Sine04" Sine04 Sine04
3 "dbOpc"."Random01" Random01 Random01
3 "dbOpc"."Random02" Random02 Random02
3 "dbOpc"."Random03" Random03 Random03
3 "dbOpc"."Random04" Random04 Random04
3 "dbOpc"."Sine01Ah" Sine01Ah Sine01Ah
3 "dbOpc"."Sine02Ah" Sine02Ah Sine02Ah
3 "dbOpc"."Sine03Al" Sine03Al Sine03Al
3 "dbOpc"."Sine04Al" Sine04Al Sine04Al
3 "dbOpc"."Random01Ah" Random01Ah Random01Ah
3 "dbOpc"."Random02Ah" Random02Ah Random02Ah
3 "dbOpc"."Random03Ah" Random03Ah Random03Ah
3 "dbOpc"."Random04Ah" Random04Ah Random04Ah
3 DataBlocksInstance DataBlocksInstance DataBlocksInstance
3 5203 Icon Icon
3 "idbSineAa01" idbSineAa01 idbSineAa01
3 "idbSineAa01".Inputs Inputs Inputs
3 "idbSineAa01"."XL" XL XL
3 "idbSineAa02" idbSineAa02 idbSineAa02
3 "idbSineAa02".Inputs Inputs Inputs
3 "idbSineAa02"."XL" XL XL
3 "idbSineAa04" idbSineAa04 idbSineAa04
3 "idbSineAa04".Inputs Inputs Inputs
3 "idbSineAa04"."XL" XL XL
3 "idbSineAa03" idbSineAa03 idbSineAa03
3 "idbSineAa03".Inputs Inputs Inputs
3 "idbSineAa03"."XL" XL XL
3 "idbPrngAa01" idbPrngAa01 idbPrngAa01
3 "idbPrngAa01".Inputs Inputs Inputs
3 "idbPrngAa01"."XL" XL XL
3 "idbPrngAa02" idbPrngAa02 idbPrngAa02
3 "idbPrngAa02".Inputs Inputs Inputs
3 "idbPrngAa02"."XL" XL XL
3 "idbPrngAa04" idbPrngAa04 idbPrngAa04
3 "idbPrngAa04".Inputs Inputs Inputs
3 "idbPrngAa04"."XL" XL XL
3 "idbPrngAa03" idbPrngAa03 idbPrngAa03
3 "idbPrngAa03".Inputs Inputs Inputs
3 "idbPrngAa03"."XL" XL XL
3 DeviceManual DeviceManual DeviceManual
3 DeviceRevision DeviceRevision DeviceRevision
3 EngineeringRevision EngineeringRevision EngineeringRevision
3 HardwareRevision HardwareRevision HardwareRevision
3 Inputs Inputs Inputs
3 5204 Icon Icon
3 "InputBit" InputBit InputBit
3 Manufacturer Manufacturer Manufacturer
3 Memory Memory Memory
3 5204 Icon Icon
3 "MemoryBit" MemoryBit MemoryBit
3 "MemoryFloat" MemoryFloat MemoryFloat
3 Model Model Model
3 OperatingMode OperatingMode OperatingMode
3 OrderNumber OrderNumber OrderNumber
3 Outputs Outputs Outputs
3 5204 Icon Icon
3 "OutputBit" OutputBit OutputBit
3 RevisionCounter RevisionCounter RevisionCounter
3 SerialNumber SerialNumber SerialNumber
3 SoftwareRevision SoftwareRevision SoftwareRevision
3 Timers Timers Timers
3 5204 Icon Icon
3 "Timer" Timer Timer
3 5201 Icon Icon
Program EndProcess finished with exit code 0
open62541 事件(复杂的世界)
学习open62541 — [10] Client监测变量值
open62541 client批量监测
1、主要函数:
(1)变化处理回调函数:
static void handler_DataChanged(UA_Client *client, UA_UInt32 subId,void *subContext, UA_UInt32 monId,void *monContext, UA_DataValue *value)
订阅处理回调参数中:monContext为空,只能根据subId、monId来判断是哪个发来的。
其中double发回来的value,是string格式的UA_Variant。
夏令时(DST)
void dataChangeNotificationCallback(UA_Server *server, UA_UInt32 monitoredItemId,void *monitoredItemContext, const UA_NodeId *nodeId,void *nodeContext, UA_UInt32 attributeId,const UA_DataValue *value)
{UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Received Notification"); UA_NodeId * targetNodeId = (UA_NodeId*)monitoredItemContext;if (monitoredItemId == monid && UA_NodeId_equal(nodeId, targetNodeId)){UA_Int32 currentValue = *(UA_Int32*)(value->value.data);UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "Current Value: %dn", currentValue);}
}
(2)单个订阅
UA_MonitoredItemCreateResult
UA_Client_MonitoredItems_createDataChange(UA_Client *client, UA_UInt32 subscriptionId,UA_TimestampsToReturn timestampsToReturn,const UA_MonitoredItemCreateRequest item, void *context,UA_Client_DataChangeNotificationCallback callback,UA_Client_DeleteMonitoredItemCallback deleteCallback)
(3)多个订阅
UA_CreateMonitoredItemsResponse
UA_Client_MonitoredItems_createDataChanges(UA_Client *client,const UA_CreateMonitoredItemsRequest request,void **contexts,UA_Client_DataChangeNotificationCallback *callbacks,UA_Client_DeleteMonitoredItemCallback *deleteCallbacks)
2、添加订阅
nodeId注意不能是局部变量,否则处理函数中收不到。这里用的是static
void main()
{static UA_NodeId targetNodeId = UA_NODEID_STRING(1, "the.answer");addMonitoredItemToVariable(client, &targetNodeId);
}
void addMonitoredItemToVariable(UA_Client *client, UA_NodeId *target)
{UA_MonitoredItemCreateResult monResponse =UA_Client_MonitoredItems_createDataChange(client, response.subscriptionId,UA_TIMESTAMPSTORETURN_BOTH,monRequest, (void*)target, handler_DataChanged, NULL);
}
以下使用动态创建监控nodeId,会造成内存泄漏:
UA_NodeId * pContext = (UA_NodeId *)UA_malloc(sizeof(UA_NodeId));
UA_NodeId_copy(&TargetNodeId, pContext);
用智能指针传给回调的:
学习open62541 — [76] 使用智能指针处理内存释放问题
用智能指针来存放context内存
std::shared_ptr<UA_NodeId>
addMonitoredItemToVariable(UA_Server *server, UA_NodeId TargetNodeId, UA_UInt32& monitoredItemId)
{ std::shared_ptr<UA_NodeId> spContext = std::make_shared<UA_NodeId>();UA_NodeId_copy(&TargetNodeId, ());result = UA_Server_createDataChangeMonitoredItem(server, UA_TIMESTAMPSTORETURN_BOTH,monRequest, (void*)(), dataChangeNotificationCallback); return spContext;
}
main() 创建一个holdSP来承接addMonitoredItemToVariable()的返回值,有了这个holdSP,那么智能指针指向的内存就不会被释放,程序结束时就会自动释放。(可用类成员变量来接)
int main(void)
{ std::shared_ptr<UA_NodeId> spContext;monid = addMonitoredItemToVariable(server, targetNodeId, spContext);}
open62541Server中添加方法并由Client调用
学习open62541 — (24) 定时执行任务
typedef void (*UA_ClientCallback)(UA_Client *client, void *data); //回调
UA_Server_addRepeatedCallback() //添加循环定时任务,并获取其callbackId
UA_Server_addTimedCallback() //添加oneshot定时任务
UA_Client_changeRepeatedCallbackInterval() //更改循环周期
UA_Client_removeCallback() //移除定时
//特别注意:
//UA_Server_addRepeatedCallback()里的时间参数类型是UA_Double,是个相对时间
//UA_Server_addTimedCallback()里的时间参数类型是UA_DateTime,是个绝对时间。
学习OPEN62541 — [37] 与KEPSERVEREX进行简单通信
学习open62541 — [33] 加密(使用OpenSSL)
基于Windows手动编译openssl和直接安装openssl
Openssl库编译篇(Windows平台)(有编译好的)
UA_SignedSoftwareCertificate_new()
UA_SignedSoftwareCertificate_copy()
signActivateSessionRequest()
activateSessionAsync()
connectIterate()
本文发布于:2024-01-28 08:44:22,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/17064026676200.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |