【OpcUA开发笔记 2】open62541在Linux下编译及Qt开发

前言

       在上一篇中,我们记录了在windows下通过CMake编译mingw版本的open62541,事实上是为了这一篇做铺垫,我们本次就在ubuntu下编译open62541,并通过Qt来调用它。

一、编译

1. 建立工程文件夹

   我在主目录中建立文件夹project,并将其设置为具有可读写权限,即cd到该文件夹,然后执行以下命令,这很关键,否则编译好的东西在关掉控制台的时候就消失白干了。

sudo chmod 777 ./

2. 下载源码

git clone https://github.com/open62541/open62541.git

3. 配置

       确保自己安装了g++,cmake之后,在open62541文件夹下建立build文件夹,cd到里面之后,执行以下代码,该选项用于生成open62541.h和open62541.c文件,当然还会生成open62541.a静态库文件,在这三个文件中,可以将.h和.c文件配合使用,也可以将.h和.a文件配合使用,前者是源码,后者是封装,但以下选项不打开为ON,这三者都不会生成。

cmake .. -D UA_ENABLE_AMALGAMATION=ON

 4. 编译

       在该目录下继续执行make:

make

        完成后,build\bin中只有open62541.a文件,如果想要继续编译例子,则需要先关闭UA_ENABLE_AMALGAMATION,再打开UA_BUILD_EXAMPLES,这两个编译时是互斥的。

5. 编译例子

        进入build文件夹,分别执行以下命令:

cmake .. -D UA_ENABLE_AMALGAMATION=OFF
cmake .. -D UA_BUILD_EXAMPLES=ON

然后执行 make,就会发现build\bin文件夹下多了个examples文件夹,你可以用他们试着玩一玩了。

二、用Qt调用open62541

        在windows下就用CMake生成VS版本比较好,一些windows依赖库也就自己加到工程里去了,如果要用Qt,注意以下几点:

1. Qt5建立Non-Qt-Project / C++ Plain Application工程,Qt6目前测试有问题;

2. 要用qMake,这样不用写cMake代码配置工程;

3. 我们将上一节生成的.h和.a文件放到open62541文件夹中并拷贝到工程目录下,在工程中添加库.a和.h文件(或者.c搭配.h文件,这种方式可以改一些配置。另外,在windows下基于mingw编译的.a静态库也能执行,不过要跨平台,在Linux下仍然需要重新编译.a静态库);

4. 如果有using namespace std,要放到 #include "open62541/open62541.h" 上面;

5. 需要在.pro中加入LIBS += -lpthread libwsock32 libws2_32

        本座在ubuntu下用Qt5.14.2调用open62541,跟以上类似,只是不需要第五步。以下给一个Server的例子,该例子来自于源码根目录下的examples\tutorial_server_variable.c,只是我们已经生成了open62541.h就可以干掉源码里的以下两句:

#include <open62541/plugin/log_stdout.h>
#include <open62541/server.h>

用一句 #include "open62541/open62541.h"代替就好了,免得有其他引用问题

//#include <open62541/plugin/log_stdout.h>
//#include <open62541/server.h>

#include "open62541/open62541.h"
#include <stdio.h>

static void
addVariable(UA_Server *server) {
    /* Define the attribute of the myInteger variable node */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    UA_Int32 myInteger = 42;
    UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    attr.description = UA_LOCALIZEDTEXT("en-US","the answer");
    attr.displayName = UA_LOCALIZEDTEXT("en-US","the answer");
    attr.dataType = UA_TYPES[UA_TYPES_INT32].typeId;
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Add the variable node to the information model */
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "the answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE), attr, NULL, NULL);
}

static void
addMatrixVariable(UA_Server *server) {
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "Double Matrix");
    attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE;

    /* Set the variable value constraints */
    attr.dataType = UA_TYPES[UA_TYPES_DOUBLE].typeId;
    attr.valueRank = UA_VALUERANK_TWO_DIMENSIONS;
    UA_UInt32 arrayDims[2] = {2,2};
    attr.arrayDimensions = arrayDims;
    attr.arrayDimensionsSize = 2;

    /* Set the value. The array dimensions need to be the same for the value. */
    UA_Double zero[4] = {0.0, 0.0, 0.0, 0.0};
    UA_Variant_setArray(&attr.value, zero, 4, &UA_TYPES[UA_TYPES_DOUBLE]);
    attr.value.arrayDimensions = arrayDims;
    attr.value.arrayDimensionsSize = 2;

    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "double.matrix");
    UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME(1, "double matrix");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
    UA_Server_addVariableNode(server, myIntegerNodeId, parentNodeId,
                              parentReferenceNodeId, myIntegerName,
                              UA_NODEID_NUMERIC(0, UA_NS0ID_BASEDATAVARIABLETYPE),
                              attr, NULL, NULL);
}

/**
 * Now we change the value with the write service. This uses the same service
 * implementation that can also be reached over the network by an OPC UA client.
 */

static void
writeVariable(UA_Server *server) {
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");

    /* Write a different integer value */
    UA_Int32 myInteger = 43;
    UA_Variant myVar;
    UA_Variant_init(&myVar);
    UA_Variant_setScalar(&myVar, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
    UA_Server_writeValue(server, myIntegerNodeId, myVar);

    /* Set the status code of the value to an error code. The function
     * UA_Server_write provides access to the raw service. The above
     * UA_Server_writeValue is syntactic sugar for writing a specific node
     * attribute with the write service. */
    UA_WriteValue wv;
    UA_WriteValue_init(&wv);
    wv.nodeId = myIntegerNodeId;
    wv.attributeId = UA_ATTRIBUTEID_VALUE;
    wv.value.status = UA_STATUSCODE_BADNOTCONNECTED;
    wv.value.hasStatus = true;
    UA_Server_write(server, &wv);

    /* Reset the variable to a good statuscode with a value */
    wv.value.hasStatus = false;
    wv.value.value = myVar;
    wv.value.hasValue = true;
    UA_Server_write(server, &wv);
}

/**
 * Note how we initially set the DataType attribute of the variable node to the
 * NodeId of the Int32 data type. This forbids writing values that are not an
 * Int32. The following code shows how this consistency check is performed for
 * every write.
 */

static void
writeWrongVariable(UA_Server *server) {
    UA_NodeId myIntegerNodeId = UA_NODEID_STRING(1, "the.answer");

    /* Write a string */
    UA_String myString = UA_STRING("test");
    UA_Variant myVar;
    UA_Variant_init(&myVar);
    UA_Variant_setScalar(&myVar, &myString, &UA_TYPES[UA_TYPES_STRING]);
    UA_StatusCode retval = UA_Server_writeValue(server, myIntegerNodeId, myVar);
    printf("Writing a string returned statuscode %s\n", UA_StatusCode_name(retval));
}

/** It follows the main server code, making use of the above definitions. */

int main(void) {
    UA_Server *server = UA_Server_new();

    addVariable(server);
    addMatrixVariable(server);
    writeVariable(server);
    writeWrongVariable(server);

    UA_Server_runUntilInterrupt(server);
    UA_Server_delete(server);
    return 0;
}

至此,你发现已经可以运行了,下载一个UaExpert当做客户端来测一下吧。

相关推荐

  1. OpcUA开发笔记 2open62541Linux编译Qt开发

    2024-02-23 14:22:02       27 阅读
  2. 7、Qt5开发实列(笔记

    2024-02-23 14:22:02       37 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-23 14:22:02       19 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-23 14:22:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-23 14:22:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-23 14:22:02       20 阅读

热门阅读

  1. 日常leetcode代码思路总结(持续更新)

    2024-02-23 14:22:02       36 阅读
  2. 【机器学习】机器学习是什么?

    2024-02-23 14:22:02       24 阅读
  3. Sora技术——AI的热辣滚烫

    2024-02-23 14:22:02       31 阅读