有个项目中需要兼容OPC-UA,后台是java的微服务,看到git上有个用Pythonpython-opcua写的不错,不过纯C写的这个open62541更适合项目。
open62541的编译和使用相对简洁,这里用release版本的库来做尝试。
1 下载open62541的release版本
open62541当前最新的版本是v0.3-rc4,手头是32位的linux系统,下载open62541-linux32.tar.gz。连接如下:

https://github.com/open62541/open62541/releases

2 搭建编译环境
下载的v0.3-rc4版本中有:

AUTHORS  bin  doc_latex  LICENSE   open62541.c  open62541.h   README.md

bin目录下是动态连接库,open62541.c和open62541.h是所有的实现,可以直接加到工程中使用。动态连接库和源码都可以满足需要。
如果是java或C++,把libopen62541.so加到工程中,并包一下open62541.h,就可以调动open62541.h中的接口来实现功能了。

3 简单接口尝试
服务侧参考github上给的例子:
首先,创建UA server,监听4840端口。
其次,定义一个UA节点,这个节点有名字、ID、父节点、浏览名等一些乱七八糟参数,关键是节点ID和节点上设置的scalar,后面client上来会读取这个设置的scalar。这里的节点id是the.answer,对应的scalar是42。这块代码有点类似给redis的某个key设置value。
最后,将这个节点添加到sever上。下面是代码

#include <signal.h>
#include "open62541.h"

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

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

    /* Create a server listening on port 4840 */
    UA_ServerConfig *config = UA_ServerConfig_new_default();
    UA_Server *server = UA_Server_new(config);

    /* Add a variable node */
    /* 1) Define the node attributes */
    UA_VariableAttributes attr = UA_VariableAttributes_default;
    attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
    UA_Int32 myInteger = 42;
    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, "the.answer");
    UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
    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, "the answer");

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

    /* Run the server loop */
    UA_StatusCode status = UA_Server_run(server, &running);
    UA_Server_delete(server);
    UA_ServerConfig_delete(config);
    return status;
}

客户端例子,也是参考github上给出的例子。
首先,创建一个client连接。
其次,连接到服务器端口。
最后,读取节点ID为the.answer的值,并打印出来。

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

int main(int argc, char *argv[])
{
    /* Create a client and connect */
    UA_Client *client = UA_Client_new(UA_ClientConfig_default);
    UA_StatusCode status = UA_Client_connect(client, "opc.tcp://localhost:4840");
    if(status != UA_STATUSCODE_GOOD) {
        UA_Client_delete(client);
        return status;
    }

    /* Read the value attribute of the node. UA_Client_readValueAttribute is a
     * wrapper for the raw read service available as UA_Client_Service_read. */
    UA_Variant value; /* Variants can hold scalar values and arrays of any type */
    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: %i\n", *(UA_Int32*)value.data);
    }

    /* Clean up */
    UA_Variant_deleteMembers(&value);
    UA_Client_delete(client); /* Disconnects the client internally */
    return status;
}

编译运行也相对简单,这里采用最简单的方式

gcc -std=c99 open62541.c opcuaclient.cpp -o opcuaclient
gcc -std=c99 open62541.c opcuaserver.cpp -o opcuaserver

注意open62541.c和open62541.h放在当前路径下。
编译完就可以启动测试了
先启动服务器

./opcuaserver 
[2018-12-11 17:03:49.531 (UTC+0800)] info/session       Connection 0 | SecureChannel 0 | Session 00000001-0000-0000-0000-000000000000 | AddNodes: No TypeDefinition; Use the default TypeDefinition for the Variable/Object
[2018-12-11 17:03:49.532 (UTC+0800)] info/network       TCP network layer listening on opc.tcp://ubuntu:4840/

在启动客户端

./opcuaclient 
the value is: 42

客户端取到了server设置的value 42。
服务器也有日志:

[2018-12-11 17:04:10.064 (UTC+0800)] info/network       Connection 5 | New connection over TCP from ::1
[2018-12-11 17:04:10.064 (UTC+0800)] info/channel       Creating a new SecureChannel
[2018-12-11 17:04:10.064 (UTC+0800)] warn/securitypolicy        No PKI plugin set. Accepting all certificates
[2018-12-11 17:04:10.064 (UTC+0800)] info/channel       Connection 5 | SecureChannel 1 | Opened SecureChannel
[2018-12-11 17:04:10.065 (UTC+0800)] info/session       Connection 5 | SecureChannel 1 | Session a8ee8c0b-b9bf-ae08-5fd5-2d820360c6ac | ActivateSession: Session activated
[2018-12-11 17:04:10.065 (UTC+0800)] info/session       Connection 5 | SecureChannel 1 | Session a8ee8c0b-b9bf-ae08-5fd5-2d820360c6ac | CloseSession
[2018-12-11 17:04:10.065 (UTC+0800)] info/channel       Connection 5 | SecureChannel 1 | CloseSecureChannel
[2018-12-11 17:04:10.065 (UTC+0800)] info/network       Connection 5 | Closed

能看到客户端上来获取了sever设置的值,并断开了连接。

以上只是最简单的使用,github有丰富的例子供参考,比如loop、events、pubsub、discovery等,项目中有需要就去看对应的例子就可以,连接如下:

https://github.com/open62541/open62541/tree/master/examples
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐