OPC-UA学习--Open62541
有个项目中需要兼容OPC-UA,后台是java的微服务,看到git上有个用Pythonpython-opcua写的不错,不过纯C写的这个open62541更适合项目。open62541的编译和使用相对简洁,这里用release版本的库来做尝试。1 下载open62541的release版本open62541当前最新的版本是v0.3-rc4,手头是32位的linux系统,下载open62541...
有个项目中需要兼容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
更多推荐
所有评论(0)