OPCUA
OPC 是自动化行业与其他行业用于数据安全交换的互操作性标准。OPC DA 只可用于 Windows 操作系统,OPC UA 则可以独立于平台。本插件实现了 DolphinDB 与 OPC UA 服务器之间的数据传输。
目前支持版本:relsease200, release130, relsease120, release110。
在插件市场安装插件
版本要求
- DolphinDB Server: 2.00.10及更高版本
安装步骤
在DolphinDB 客户端中使用 listRemotePlugins 命令查看插件仓库中的插件信息。
login("admin", "123456") listRemotePlugins(, "http://plugins.dolphindb.cn/plugins/")
使用 installPlugin 命令完成插件安装。
installPlugin("opcua")
返回:<path_to_OPCUA_plugin>/PluginOPCUA.txt
使用 loadPlugin 命令加载插件(即上一步返回的.txt文件)。
loadPlugin("<path_to_OPCUA_plugin>/PluginOPCUA.txt")
注意:若使用 Windows 插件,加载时必须指定绝对路径,且路径中使用"\\"或"/"代替"\"。
接口说明
注意:使用 API 前需使用 loadPlugin("/path/to/PluginOPCUA/PluginOPCUA.txt") 导入插件。
测试用例
opc.tcp://118.24.36.220:62547/DataAccessServer 为一个在线服务端的 serverUrl,可用来测试插件的连接、读写 node、订阅等功能。
prosys opc-ua-simulation-server 提供了一个本地服务器,按照使用手册可指定端点,加密策略,用户令牌,管理证书等,可用来测试插件的加密通信等功能。
获取 OPC Server
语法
opcua::getOpcServerList(serverUrl)
参数
serverUrl
是字符串,表示 server 地址。例如: opc.tcp://127.0.0.1:53530/OPCUA/SimulationServer/。
详情
获取 OPC server。返回的结果是一个包含5列的表,分别是:"ServerUri" 表示 server 的标识符;"ServerName" 表示 server 的名字;"ProductUri" 表示 server 的 product 标识符;"Type" 表示 server 的类型;"DiscoveryUrl" 表示可以用来连接 server 的 url(若有多个,用';'分割)。
例子
opcua::getOpcServerList(serverUrl);
获取 OPC Server Endpoints
语法
opcua::getOpcEndPointList(serverUrl)
参数
- serverUrl 是字符串,表示 server 地址,例如: opc.tcp://127.0.0.1:53530/OPCUA/SimulationServer/。
详情
获取 OPC server。返回的结果是一个包含5列的表,分别是:"endpointUrl" 表示 server 可以用来连接的端点的 url;"transportProfileUri" 表示该端点传输配置文件的标识符;"securityMode" 表示该端点的安全模式;"securityPolicyUri" 表示该端点安全策略的标识符;"securityLevel" 表示该端点安全等级。
例子
opcua::getOpcEndPointList(serverUrl);
连接
语法
opcua::connect(endPointUrl,clientUri,[userName],[userPassword],[securityMode],[securityPolicy],[certificatePath],[privateKeyPath])
参数
endPointUrl 是字符串,表示要连接的 endpoint 的 url。
clientUri 是字符串,表示 client 的标识符,若指定 certificate,则需要与 certificate 里的 URI 保持一致。
userName 是字符串,表示用户名。若 server 未设置,可省略。
userPassword 是字符串,表示用户密码,与 userName 一起使用。
securityMode 是字符串,表示连接的安全模式,必须是 "None", "Sign", "SignAndEncrypt" 中的一个,默认为 "None"。
securityPolicy 是字符串,连接的安全策略,必须是 "None", "Basic256", "Basic128Rsa15", "Basic256Sha256" 中的一个,默认为 "None"。若采用 "Basic256", "Basic128Rsa15", "Basic256Sha256" 加密,则需要指定 certificate 和 privateKey。可以使用 ./open62541/cert/ 目录下的 certificate 和 privateKey,也可以使用自己生成的证书(open62541项目 tool 目录下的工具可用于生成证书)。
certificatePath是字符串,指定证书路径,若不采用加密通讯可不指定。
privateKeyPath是字符串,指定私钥路径,若不采用加密通讯可不指定。
详情
连接OPC server。返回的结果是一个connection,可以显式的调用close函数去关闭。若采用加密通讯,服务端需要信任指定的证书。如果加密通信,使用Prosys作为本地模拟服务器。
需要在Users界面下添加用户名和密码。例如: 用户名: "user1"和 密码: "123456"。然后,在Certificates界面下,信任open62541server@localhost
。
例子
connection=opcua::connect(serverUrl,"myClient");
connection=opcua::connect(serverUrl,"myClient","user1","123456");
connection=opcua::connect(serverUrl,"urn:opcua.client.application","user1","123456","SignAndEncrypt","Basic128Rsa15","./open62541/cert/client_cert.der","./open62541/cert/client_key.der");
查看所有 Node
语法
opcua::browseNode(connection)
参数
- connection 是
connect
函数返回的值。
详情
查看所有Node。返回的结果是一个table,包含两列,一列是nodeNamespace,一列是nodeIdString。
例子
connection=opcua::connect(serverUrl,"myClient");
opcua::browseNode(connection);
读取 Node(同步)
语法
opcua::readNode(connection, nodeNamespace, nodeIdString, [table])
参数
- connection 是
connect
函数返回的值。 - nodeNamespace 是 int 的标量或数组,表示 node 的 Namespace。
- nodeIdString 是字符串的标量或数组,表示 node 的字符串形式的 ID。
- table 是表或表的数组(为数组时,表的个数须与 node 个数相同),用于存放读取的结果。若是一个表,将所有 node 的值都插入这张表中;若多个表,每个 node 读取的值分别插入这些表中。若不输入表,则返回值是一张表,表的记录是读取的 node 值。
详情
读取一个 node 值,使用前需要先建立一个 OPC 连接。每个 node 返回的结果包含四个值,分别是 "node id",表示 Node 的 ID, 用":"连接 nodeNamespace 和 nodeIdString;"value" 表示 node 的值;"timestamp" 表示 source timestamp(本地时间);"status" 表示 node 的 value 状态。
例子
t1 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, INT, TIMESTAMP, SYMBOL])
opcua::readNode(conn, 3, "Counter",t1)
opcua::readNode(conn, 3, ["Counter","Expression","Random","Sawtooth"],t1)
t2 = table(200:0,`nodeID`value`timestamp`status`nodeID`value`sourceTimestamp`status,[SYMBOL, INT, TIMESTAMP, SYMBOL,SYMBOL, INT, TIMESTAMP, SYMBOL])
opcua::readNode(conn, 1, ["test1","test4"],t2)
t3 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, INT, TIMESTAMP, SYMBOL])
t4 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, INT, TIMESTAMP, SYMBOL])
t5 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, INT, TIMESTAMP, SYMBOL])
opc::readNode(conn, 1, ["test1","test4", "test9"],[t3,t4,t5])
写入 Node(同步)
语法
opc::writeTag(connection, nodeNamespace, nodeIdString, value)
参数
- connection 是
connect
函数返回的值。 - nodeNamespace 是 int 的标量或数组,表示 node 的 Namespace。
- nodeIdString 是字符串的标量或数组,表示 node 的字符串形式的 ID。
- value 是 Node 的值或数组。
详情
写入一个或一组 Node 的值。如果写入类型错误,会抛出异常。
例子
opcua::writeNode(conn,1,"testwrite.test1",1)
opcua::writeNode(conn,1,["testwrite.test5","testwrite.test6"],[33,11])
opcua::writeNode(conn,1,"testwrite.test2",[1,2,3,4])//one-dimensional array
m = matrix([[1,2,3],[1,2,3]])
opcua::writeNode(conn,1,"testwrite.test3",m)//two-dimensional array
订阅
语法
opcua::subscribe(connection, nodeNamespace, nodeIdString, handler, [actionName], [reconnect=false], [resubscribeInterval=0])
参数
- connection 是
connect
函数返回的值。 - nodeNamespace 是 int 的标量或数组,表示 node 的 Namespace。
- nodeIdString 是字符串或字符串的数组,表示 node 的字符串形式的 ID。
- handler 是数据发生变化时调用的回调函数或表。
- actionName 可选,STRING 类型标量,表示订阅任务的名称。每个订阅必须指定唯一的 actionName。如不指定,将自动生成一个名称
- reconnect 可选,BOOL 类型标量,表示在订阅断开时是否自动重连。默认为 false;若设置为 true ,则会在订阅断开时自动尝试重连。
- resubscribeInterval 可选,非负整数,表示重新订阅的最小时间间隔,单位毫秒,默认值为 0。
详情
使用 handler 处理订阅的 OPCUA 节点变化的值。
如果启用 reconnect,在订阅断开时,将每隔 resubscribeInterval 时间尝试重连。在重连过程中,已有的订阅信息不会被清除;如果重连成功,相关的订阅信息将基于重连前的状态继续更新。在重连过程中,用户可以取消该订阅,取消后将不再尝试重连。
注意:目前一个订阅需要独占一个 connection 连接,即若一个连接调用 subscribe,不能再用这个连接去做 readNode
和 writeNode
等操作。
例子
t1 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, INT, TIMESTAMP, SYMBOL])
conn1=opcua::connect(serverUrl,"myClient")
opcua::subscribe(conn1,1,"test.subscribe",t1)
t2 = table(200:0,`nodeID`value`timestamp`status, [STRING, INT, TIMESTAMP, STRING])
conn2=opcua::connect(serverUrl,"myClient")
opcua::subscribe(conn2, 3, ["Counter","Expression","Random","Sawtooth"],t2)
t3 = table(200:0,`nodeID`value`timestamp`status, [SYMBOL, BOOL, TIMESTAMP, SYMBOL])
def callback1(mutable t, d) {
t.append!(d)
}
conn3=opcua::connect(serverUrl,"myClient")
opcua::subscribe(conn3,2, "testsubscribe",callback1{t3})
取消订阅
语法
opcua::unsubscribe(subscription)
参数
- subscription 是
connect
函数返回的值或订阅时指定的任务名称(actionName)。
详情
根据任务名称或连接取消对应的订阅。
例子
opcua::unsubscribe(subscription)
查看订阅状态
语法
opcua::getSubscriberStat()
详情
查看当前所有的订阅状态,包括以下信息:
名称 | 类型 | 含义 |
---|---|---|
actionName | STRING | 订阅任务的名称 |
subscriptionId | INT | 表示订阅标识符 |
user | STRING | 表示建立订阅的会话用户 |
endpointUrl | STRING | 表示连接的 endPointUrl |
clientUrl | STRING | 表示连接的 client 标识符 |
nodeID | STRING | 表示订阅的所有 Node,用 "NodeNamespace:nodeIdString" 表示每一个 Node,不同的 Node 用 ';' 分割。 |
createTimestamp | NANOTIMESTAMP | 任务的创建时间 |
isConnected | BOOL | 是否已连接 |
firstMsgTime | NANOTIMESTAMP | 收到第一条消息的时间 |
lastMsgTime | NANOTIMESTAMP | 收到最后一条消息的时间 |
processedMsgCount | LONG | 已经处理的消息数 |
failedMsgCount | LONG | 处理失败的消息数 |
lastErrMsg | STRING | 最后一条错误消息的信息 |
lastFailedTimestamp | NANOTIMESTAMP | 最后一条错误消息发生的时间 |
lastReconnectTime | NANOTIMESTAMP | 最后一次发生重连的时间 |
例子
opcua::getSubscriberStat()
关闭连接
语法
opcua::close(connection)
参数
- connection 是
connect
函数返回的值。
详情
断开与 OPC server 的连接。如果该 connection 订阅了 Node,则在断开 OPC server 的同时也会取消对应的订阅。
例子
opcua::close(connection)
附录:(预编译)安装
如果不通过插件市场安装插件,也可以选择预编译安装或编译安装方式。
预编译安装
可以直接使用已编译好的 libPluginOPCUA.dll 或 libPluginOPCUA.so:
执行 Linux 命令,指定插件运行时需要的动态库路径
export LD_LIBRARY_PATH=/path/to/PluginOPCUA:$LD_LIBRARY_PATH
启动 DolphinDB 服务,并执行 DolphinDB 插件加载脚本
loadPlugin("/path/to/PluginOPCUA/PluginOPCUA.txt")
请注意,若使用 Windows 插件,加载时必须指定绝对路径,且路径中使用\\\\
或/
代替\\
。
编译安装
环境准备
需要先编译 mbedtls 静态库和 open6254 1动态库。步骤如下:
Windows
安装 mbedtls
从 GitHub 上下载最新的 mbedtls 项目:
git clone https://github.com/ARMmbed/mbedtls.git
使用 CMake 编译为静态库:
cd mbedtls mkdir build cd build cmake .. -G "MinGW Makefiles" -DENABLE_PROGRAMS=OFF make
/libmbedcrypto.a $(LIB_DIR)/libmbedx509.a $(LIB_DIR)/libmbedtls.a 编译得到静态库位于 mbedtls/build/library 下,分别是 libmbedcrypto.a、libmbedx509.a、libmbedtls.a。
安装 open62541
- 从 GitHub 上下载1.0版本的 open62541项目:
git clone https://github.com/open62541/open62541.git
git submodule update --init --recursive
cd open62541
git checkout 1.0
- 使用 CMake 编译为动态库:
mkdir build
cd build
cmake .. -G "MinGW Makefiles" -DUA_ENABLE_SUBSCRIPTIONS=ON -DBUILD_SHARED_LIBS=ON -DUA_ENABLE_ENCRYPTION=ON -DMBEDTLS_INCLUDE_DIRS="path_to_mbedtls/include" -DMBEDTLS_LIBRARIES="path_to_mbedtls/build/library" -DMBEDTLS_FOLDER_INCLUDE="path_to_mbedtls/include" -DMBEDTLS_FOLDER_LIBRARY="path_to_mbedtls/build/library"
make
请注意:用户需要根据实际情况替换路径 -DMBEDTLS_INCLUDE_DIRS 和 -DMBEDTLS_LIBRARIES。
Linux Ubuntu
安装 mbedtls
sudo apt-get install libmbedtls-dev
安装 open62541 方法与 Windows 一致,可不指定 -DMBEDTLS_INCLUDE_DIRS 和 -DMBEDTLS_LIBRARIES。
Linux Centos
安装 mbedtls
yum install mbedtls-devel
安装 open62541方法与 Windows 一致,可不指定 -DMBEDTLS_INCLUDE_DIRS 和 -DMBEDTLS_LIBRARIES。
使用 cmake构建 libPluginOPCUA
复制 mbedtls/build/library 目录下的 libmbedcrypto.a、libmbedx509.a、libmbedtls.a 到 ./lib 目录下;复制 mbedtls/include 目录下的 mbedtls 和 psa 文件夹到 ./include 目录下(linux系统可跳过这一步)。
复制 open62541/build/bin 目录下的 .dll 文件,或者所有 .so 文件到 ./lib 目录下;复制 open62541/build/src_generated/、open62541/include/、open62541/plugins/include/ 下的文件夹到 ./include 目录下,open62541/arch/ 下的文件夹到 ./include/open62541 目录下。
使用 cmake 构建 libPluginOPCUA,linux 不需要指定 -G。
mkdir build
cd build
cmake .. -G "MinGW Makefiles" -DLIBDOLPHINDB="path_to_libdolphindb"
make
注意:用户需要根据实际情况替换路径 -DLIBDOLPHINDB。
- 将 libopen62541.dll 或 libopen62541.so 复制到 build 目录下。