OPC UA protocol node information query and multi-node value reading and writing case practice based on open62541 library

Table of contents

1. Introduction to OPC UA protocol

2. Introduction to open62541 library

3. Multi-point query and multi-point read and write case server opcua_server of opcua protocol

        3.1 opcua_server project directory

       3.2 Program source code

        3.3 Engineering organization documents

        3.4 Compile and start

 4. Multi-point query and multi-point read and write case client opcua_client of opcua protocol

        4.1 opcua_client project directory

        4.2 Program source code

        4.3 Engineering Organization Documentation

      4.4 Compile and test


1. Introduction to OPC UA protocol

        OPC UA (Open Platform Communications Unified Architecture) is a general-purpose, secure, cross-platform communication protocol for transferring data and information in the industrial Internet and Internet of Things (IoT) environments. It is based on the common OPC (Open Platform Communications) protocol in the industrial field, and has been expanded and optimized for the needs of modern device interconnection.

        The OPC UA protocol supports a variety of network protocols, including TCP/IP, UDP, WebSockets, etc. The protocol provides a standardized and scalable communication mechanism for communication and data exchange between devices, and is widely used in industrial automation, intelligent manufacturing and other fields.

        The OPC UA protocol is a powerful, safe, reliable, and scalable general-purpose communication protocol. It is widely used in industrial automation, Internet of Things device access and other fields. The OPC UA protocol has the following characteristics:

1. High security: supports a variety of encryption algorithms, and can perform identity verification and authority management through digital certificates.

2. Cross-platform: Supports communication between applications and devices on different operating systems.

3. Strong scalability: support custom protocol data unit types and protocol objects.

4. Flexible data model: It can adapt to various application scenarios, such as data transmission, event notification, access control, etc.

5. It can carry a variety of application protocols: such as IoT networking protocols, smart manufacturing protocols, etc.

2. Introduction to open62541 library

        open62541 is an OPC UA communication library based on C language. The library implements the client and server sides of the OPC UA standard and supports various operating systems and compilers. The OPC UA communication library is rich in functions, easy to transplant, good in scalability, and superior in performance. It is widely used in industrial automation, intelligent manufacturing, Internet of Things and other fields. It is an important choice for developing OPC UA applications. Its characteristics are as follows:

1. Open source and free: using the MIT license agreement, it can be used for commercial and non-commercial projects.

2. Highly scalable: It supports plug-in development, and can easily add functions such as protocol extensions, authentication mechanisms, and encryption protocols.

3. Ease of transplantation: It realizes the cross-platform feature and can be used on different operating systems and hardware platforms.

4. High performance: It adopts event-based architecture and asynchronous I/O mechanism, which has very good performance and scalability.

5. Support multi-threading: use thread-safe methods to make function calls, and can work stably in a multi-threaded environment.

6. Strong embeddability: It supports embedding library files into applications for easy integration and use.

For the source code compilation, installation, and environment deployment of the open62541 library, please refer to the blog post in this column:

OPC UA/DA protocol library open62541 source code compilation and case testing_open62541 source code_py_free-IOT Smart Blog-CSDN Blog

3. Multi-point query and multi-point read and write case server opcua_server of opcua protocol

        3.1 opcua_server project directory

        Create the opcua_server project, add some nodes to the opc service, and set initial values ​​for these nodes, and then wait for the opc client to link and operate.

        The project directory is as follows:

opcua_server
    bin
    build_linux
    build_mingw
    src
        main.c
    CMakeLists.txt

       3.2 Program source code

        The content of the main.c file is as follows:

#include <signal.h>
#ifdef __linux__
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#else
#include "open62541.h"
#endif

// 用于记录SENSOR_1节点值的变量
UA_Int32 sensor1Value = 0;
UA_StatusCode add_sensor_variable(UA_Server *server,char* nodeid_desc,int val) {     
    /* Add a variable node */
    /* 1) Define the node attributes */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", nodeid_desc);              //属性展示信息
    attr.description = UA_LOCALIZEDTEXT("en-US", nodeid_desc);              //属性描述信息
    // attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    UA_Int32 myInteger = val;
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);   //设置属性值

    /* 2) Define where the node shall be added with which browsename */
    UA_NodeId newNodeId = UA_NODEID_STRING(1, nodeid_desc);                     //节点=命名空间+节点名称,客户端端标识类型是UA_NODEIDTYPE_STRING
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);      //父节点,客户端端标识类型是UA_NODEIDTYPE_NUMERIC
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES); //关联节点
    UA_NodeId variableType = UA_NODEID_NULL; /* take the default variable type *///变量类型
    UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, nodeid_desc);             //节点浏览名

    /* 3) Add the node */
    return UA_Server_addVariableNode(server, newNodeId, parentNodeId, parentReferenceNodeId,
                              browseName, variableType, attr, NULL, NULL);
}

UA_Boolean running = true;
void signalHandler(int sig) {
    running = false;
}

int main() {
    signal(SIGINT, signalHandler); /* catch ctrl-c */

    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

    // 添加节点
    add_sensor_variable(server,"SENSOR_1",41);
    add_sensor_variable(server,"SENSOR_2",42);

    // 启动服务端
    UA_StatusCode retval = UA_Server_run(server, &running);
    UA_Server_delete(server);
    return (int)retval;
}

        The above code description:

        1) The reference of the header file is different between win and linux: under win, whether using mingw-gcc or Visual Studio compilation tool, the open62541.h/open62541.c file will be generated first, and then compiled into a library, so open62541.h It is equivalent to a collection API interface header file, calling this file is equivalent to calling a series of header files; when gcc compiles the source code under linux, it will not generate intermediate files, but directly generate the library in one step, and the header files are still The header files of the source code are used, so the involved header files need to be referenced separately.

        2) Note that when adding a node, the node number is composed of namespace + node name, and the id of the node and the ID of the parent node need to be specified. This information is to be notified to the client so that the client can access it

    UA_NodeId newNodeId = UA_NODEID_STRING(1, nodeid_desc);                     //节点=命名空间+节点名称,客户端端标识类型是UA_NODEIDTYPE_STRING
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);      //父节点,客户端端标识类型是UA_NODEIDTYPE_NUMERIC

        3) The value variable of the node can specify the name, description, access rights, value type and other information. If the client needs to write, it needs to specify the UA_ACCESSLEVELMASK_WRITE permission. If the client needs to judge the value type, it also needs to know its type, such as UA_TYPES_INT32.

    attr.displayName = UA_LOCALIZEDTEXT("en-US", nodeid_desc);              //属性展示信息
    attr.description = UA_LOCALIZEDTEXT("en-US", nodeid_desc);              //属性描述信息
    // attr.dataType = UA_TYPES[UA_TYPES_UINT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;
    UA_Int32 myInteger = val;
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);   //设置属性值

        In this program, the naming, description, and browsing fields of nodes are all assigned the same string, but these can be different in actual projects.

       4) The program sets the default configuration of the server, and its default listening port is 4840:

    UA_Server *server = UA_Server_new();
    UA_ServerConfig_setDefault(UA_Server_getConfig(server));

 

        3.3 Engineering organization documents

        The CMakeLists.txt project file is as follows,

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (opcua_server)
#
if(WIN32)
    message(STATUS "windows compiling...")
    add_definitions(-D_PLATFORM_IS_WINDOWS_)
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
    set(WIN_OS true)
else(WIN32)
    message(STATUS "linux compiling...")
    add_definitions( -D_PLATFORM_IS_LINUX_)
    add_definitions("-Wno-invalid-source-encoding")
	  # add_definitions("-O2")
    set(UNIX_OS true)
    set(_DEBUG true)
    
endif(WIN32)

set(UA_IPV6 0)
set(org_dir ${PROJECT_SOURCE_DIR}/../..)
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_mingw)
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_vc)
set(build_dir ${PROJECT_SOURCE_DIR}/../../build_linux)
#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
 
# 指定源文件的目录,并将名称保存到变量
SET(source_h
    #
	#${build_dir}/open62541.h
  )
  
#头文件目录
include_directories(
	${build_dir}
	${org_dir}/include
	${build_dir}/src_generated
	${org_dir}/arch
	${org_dir}/deps
	${org_dir}/plugins/include
)
 
if (${UNIX_OS})

SET(source_cpp
    #
	${PROJECT_SOURCE_DIR}/src/main.c
  )
  
add_definitions(
	"-DUA_ARCHITECTURE_POSIX"
    "-std=c99"
  )
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
 
link_directories()
# 指定生成目标
add_executable(opcua_server ${source_h} ${source_cpp} )
#link
target_link_libraries(opcua_server 
  -pthread
  ${build_dir}/bin/libopen62541.a 
)
 
endif(${UNIX_OS})
 
if (${WIN_OS})

SET(source_cpp
    #
	${PROJECT_SOURCE_DIR}/src/main.c
	${build_dir}/open62541.c
  )
 
add_definitions(
  "-DUA_ARCHITECTURE_WIN32"
)
 
link_directories(
	"${build_dir}/bin"
)
 
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
 
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(opcua_serverd ${source_h} ${source_cpp} )

#link
target_link_libraries(opcua_serverd 
  ws2_32.lib wsock32.lib Iphlpapi.lib
  ${build_dir}/bin/libopen62541.a #mingw
  #${build_dir}/bin/Debug/open62541d.lib  #vc
)

else(CMAKE_BUILD_TYPE)
 
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(opcua_server ${source_h} ${source_cpp})

#link
target_link_libraries(opcua_server 
  ws2_32.lib wsock32.lib Iphlpapi.lib
  ${build_dir}/bin/libopen62541.a #mingw
  #${build_dir}/bin/Release/open62541.lib  #vc
)

endif (CMAKE_BUILD_TYPE)

endif(${WIN_OS})

        The project file is configured with the support of vs mingw-gcc linux-gcc. This article uses linux-gcc to compile the opcua_server project, so

set(build_dir ${PROJECT_SOURCE_DIR}/../../build_linux)

        In addition, it should be noted that Linux needs pthread library support, and win needs ws2_32.lib wsock32.lib Iphlpapi.lib library support.

        3.4 Compile and start

        The compilation environment of this article is the virtual machine environment of ubuntu20.4-server-64bit, cmake gcc is installed and the open62541 library is compiled and installed:

         Enter build_linux in the project directory:

cmake ..
make -j4

         Then start the compiled program:

../bin/opcua_server

 4. Multi-point query and multi-point read and write case client opcua_client of opcua protocol

        4.1 opcua_client project directory

        Create the opcua_client project, the opc program links to the server, then reads node data, writes node data, queries node information, and prints out the information.

opcua_client
    bin
    build_linux
    build_mingw
    build_vc
    src
        main.c
    CMakeLists.txt

        4.2 Program source code

        Main.c file content, need to pay attention to a few points:

1) The server node is UA_NODEID_STRING, and the node created by the client is also of this type, and a consistent namespace and node name are given, such as 1, SENSOR_1;

2) When parsing the value, judge according to the value type. If you know the value type specified by the server, you can quickly filter out other nodes, such as specifying UA_TYPES_INT32;

3) When writing data, the value type of the set variable to be written must be consistent with the value type of the server node, and the service cannot be successfully written;

4) When querying node information, if you want to query the added node on the server, you need to find the specified parent node, and you will get the node information faster. For example, when adding a node on the server, it is in the parent node UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER) Added below.

#ifdef __linux__
#include <open62541/server.h>
#include <open62541/server_config_default.h>
#else
#include "open62541.h"
#endif

void read_node(UA_Client *client)
{
    // 读取和解析单个节点的值
    UA_Variant value;
    UA_Client_readValueAttribute(client, UA_NODEID_STRING(1, "SENSOR_1"), &value);
    if(value.type == &UA_TYPES[UA_TYPES_INT32]) {
        UA_Int32 intValue = *(UA_Int32 *)value.data;
        printf("Sensor 1 Value: %i\n", intValue);
    } else {
        printf("Unsupported Data Type\n");
    }
    // UA_Variant_deleteMembers(&value);
}

void read_nodes(UA_Client *client)
{
 // 读取和解析多个节点的值
    UA_ReadValueId valueIds[2];
    UA_ReadValueId_init(&valueIds[0]);
    UA_ReadValueId_init(&valueIds[1]);
    //
    valueIds[0].nodeId = UA_NODEID_STRING(1, "SENSOR_1");
    valueIds[0].attributeId = UA_ATTRIBUTEID_VALUE;
    //
    valueIds[1].nodeId = UA_NODEID_STRING(1, "SENSOR_2");
    valueIds[1].attributeId = UA_ATTRIBUTEID_VALUE;


    UA_ReadRequest request;
    UA_ReadRequest_init(&request);
    request.nodesToReadSize = 2;
    request.nodesToRead = valueIds;
    UA_String out = UA_STRING_NULL;
    // UA_print(&request, &UA_TYPES[UA_TYPES_READREQUEST], &out);
    // printf("%.*s\n", (int)out.length, out.data);
    // UA_String_clear(&out);
    //
    UA_ReadResponse response;
    UA_ReadResponse_init(&response);
    // UA_print(&response, &UA_TYPES[UA_TYPES_READRESPONSE], &out);
    // printf("%.*s\n", (int)out.length, out.data);
    // UA_String_clear(&out);

    response = UA_Client_Service_read(client, request);
    //
    // UA_print(&response, &UA_TYPES[UA_TYPES_READRESPONSE], &out);
    // printf("%.*s\n", (int)out.length, out.data);
    // UA_String_clear(&out);

    if(UA_STATUSCODE_GOOD == response.responseHeader.serviceResult) {
        for(size_t i = 0; i < response.resultsSize; ++i) {
            UA_Variant value = response.results[i].value;
            if(value.type == &UA_TYPES[UA_TYPES_INT32]) {
                UA_Int32 intValue = *(UA_Int32 *)value.data;
                printf("Sensor %i Value: %i\n", i+1, intValue);
            } else {
                printf("Unsupported Data Type\n");
            }
            // UA_Variant_deleteMembers(&value);
        }
    }
    //
    printf("UA_ReadResponse_clear call\n");
    UA_ReadResponse_clear(&response);
};

void write_nodes(UA_Client *client)
{
 // 配置及写入多个节点的值
    UA_WriteValue valueIds[2];
    UA_WriteValue_init(&valueIds[0]);
    UA_WriteValue_init(&valueIds[1]);
    //
    valueIds[0].nodeId = UA_NODEID_STRING(1, "SENSOR_1");
    valueIds[0].attributeId = UA_ATTRIBUTEID_VALUE;
    //
    valueIds[1].nodeId = UA_NODEID_STRING(1, "SENSOR_2");
    valueIds[1].attributeId = UA_ATTRIBUTEID_VALUE; 

    UA_Variant infoVar;
    UA_UInt32 uint1Value = 101;
	UA_Variant_init(&infoVar);
	UA_Variant_setScalar(&infoVar, &uint1Value, &UA_TYPES[UA_TYPES_INT32]);
	valueIds[0].value.value = infoVar;
	valueIds[0].value.hasValue = true;

    UA_UInt32 uint2Value = 102;
	UA_Variant_init(&infoVar);
	UA_Variant_setScalar(&infoVar, &uint2Value, &UA_TYPES[UA_TYPES_INT32]);
	valueIds[1].value.value = infoVar;
	valueIds[1].value.hasValue = true;

    printf("WriteReques init\n");
    UA_WriteRequest wReq;
	UA_WriteRequest_init(&wReq);
	wReq.nodesToWrite = valueIds;
	wReq.nodesToWriteSize = 2;

    printf("WriteResponse return\n");
	UA_WriteResponse wResp = UA_Client_Service_write(client, wReq);
    printf("serviceResult analysis\n");
	UA_StatusCode retval = wResp.responseHeader.serviceResult;
	if (retval == UA_STATUSCODE_GOOD) {
		if (wResp.resultsSize == 1)
			retval = wResp.results[0];
		else
			retval = UA_STATUSCODE_BADUNEXPECTEDERROR;
        printf("serviceResult analysis:%0x8d\n",retval);
	}
    printf("UA_WriteResponse_clear call\n");
	UA_WriteResponse_clear(&wResp);
}


//节点查看
void Browse_nodes(UA_Client *client, UA_NodeId nodeId)
{
    /* Browse some objects */
    printf("Browsing nodes in objects folder:\n");
    UA_BrowseRequest bReq;
    UA_BrowseRequest_init(&bReq);
    bReq.requestedMaxReferencesPerNode = 0;//限制查到的最大节点数,0 不限制
    bReq.nodesToBrowse = UA_BrowseDescription_new();
    bReq.nodesToBrowseSize = 1;//需要浏览的节点个数,这里只寻找nodeId节点下的节点所以为1
    /*UA_BROWSEDIRECTION_FORWARD表示向下查找(即查找添加在节点下的节点),
    UA_BROWSEDIRECTION_INVERSE表示向上查找(即查找节点的父节点),
    UA_BROWSEDIRECTION_BOTH表示上下都进行查找*/
	bReq.nodesToBrowse[0].browseDirection = UA_BROWSEDIRECTION_FORWARD;
	bReq.nodesToBrowse[0].includeSubtypes = UA_TRUE;//是否包含subtypes
    bReq.nodesToBrowse[0].nodeId = nodeId;
    
	bReq.nodesToBrowse[0].resultMask = UA_BROWSERESULTMASK_ALL; //返回浏览到的节点包含的信息,名称、显示名称......,UA_BROWSERESULTMASK_ALL表示返回所有信息
    
    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("%-9u %-16u %-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("%-9u %-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);
            }
            /* TODO: distinguish further types */
        }
    }
    // UA_BrowseRequest_clear(&bReq);
    UA_BrowseResponse_clear(&bResp);
}

int main() {
    UA_Client *client = UA_Client_new();
    UA_ClientConfig_setDefault(UA_Client_getConfig(client));

    // 设置OPC UA服务器的URL
    UA_StatusCode retval = UA_Client_connect(client, "opc.tcp://192.168.157.143:4840");
    if(retval != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        return retval;
    }
    //读取单个节点数值
    printf("read one node for one times!\n");
    read_node(client);

   //读取多个节点数值
   printf("read more node for one times!\n");
   read_nodes(client);
   //写入多个节点数值
   printf("write more node for one times!\n");
   write_nodes(client);
   //读取多个节点数值
   printf("read more node for one times again!\n");
   read_nodes(client);
    
    // 浏览节点
    printf("Browse_nodes call!\n");
    Browse_nodes(client,UA_NODEID_NUMERIC(0, UA_NS0ID_SERVER));
    //
    printf("Browse_nodes call!\n");
    Browse_nodes(client,UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER));

    UA_Client_disconnect(client);
    UA_Client_delete(client);

    return 0;
}

        4.3 Engineering Organization Documentation

        CMakeLists.txt

# CMake 最低版本号要求
cmake_minimum_required (VERSION 2.8)
# 项目信息
project (opcua_client)
#
if(WIN32)
    message(STATUS "windows compiling...")
    add_definitions(-D_PLATFORM_IS_WINDOWS_)
    set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /MT")
    set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /MTd")
    set(WIN_OS true)
else(WIN32)
    message(STATUS "linux compiling...")
    add_definitions( -D_PLATFORM_IS_LINUX_)
    add_definitions("-Wno-invalid-source-encoding")
	  # add_definitions("-O2")
    set(UNIX_OS true)
    set(_DEBUG true)
    
endif(WIN32)

set(UA_IPV6 0)
set(org_dir ${PROJECT_SOURCE_DIR}/../..)
set(build_dir ${PROJECT_SOURCE_DIR}/../../build_mingw)
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_vc)
#set(build_dir ${PROJECT_SOURCE_DIR}/../../build_linux)
#
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
 
# 指定源文件的目录,并将名称保存到变量
SET(source_h
    #
	#${build_dir}/open62541.h
  )
  
SET(source_cpp
    #
	${PROJECT_SOURCE_DIR}/src/main.c
	${build_dir}/open62541.c
  )
  
#头文件目录
include_directories(
	${build_dir}
	#${org_dir}/include
	#${build_dir}/src_generated
	#${org_dir}/arch
	#${org_dir}/deps
	#${org_dir}/plugins/include
)
 
if (${UNIX_OS})
 
add_definitions(
  "-DUA_ARCHITECTURE_POSIX"
  )
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0")
 
link_directories()
# 指定生成目标
add_executable(opcua_client ${source_h} ${source_cpp} )
#link
target_link_libraries(opcua_client 
  -lpthread
  ${build_dir}/bin/libopen62541.a 
)
 
endif(${UNIX_OS})
 
if (${WIN_OS})

add_definitions(
  "-DUA_ARCHITECTURE_WIN32"
)
 
link_directories(
	"${build_dir}/bin"
)
 
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
 
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(opcua_clientd ${source_h} ${source_cpp} )

#link
target_link_libraries(opcua_clientd 
  ws2_32.lib wsock32.lib Iphlpapi.lib
  ${build_dir}/bin/libopen62541.a #mingw
  #${build_dir}/bin/Debug/open62541d.lib  #vc
)

else(CMAKE_BUILD_TYPE)
 
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${PROJECT_SOURCE_DIR}/bin)
# 指定生成目标
add_executable(opcua_client ${source_h} ${source_cpp})

#link
target_link_libraries(opcua_client 
  ws2_32.lib wsock32.lib Iphlpapi.lib
  ${build_dir}/bin/libopen62541.a #mingw
  #${build_dir}/bin/Release/open62541.lib  #vc
  #D:\\workForOrgCode\\open62541\\build_vc\\bin\\Release\\open62541.lib  #vc
)

endif (CMAKE_BUILD_TYPE)

endif(${WIN_OS})

      4.4 Compile and test

        The client compilation of this article uses cmake+mingw-gcc compilation,

cmake .. -G "MinGW Makefiles"

cmake --build . --config debug

         After the compilation is complete, start the test program. You can read data from a single node, read data from multiple nodes, write data to multiple nodes at the same time, and read data from multiple nodes to verify the correctness of the write. In addition, you can specify the parent node to query each of its children. Node information:

 

Guess you like

Origin blog.csdn.net/py8105/article/details/131338578