2020.4.20 更新内容:2.6版本网关使用问题.
本文内容将帮助你通过`thingsboard网关`快速接入`opc ua`协议的设备。并演示一个简单的数据读取与控制的例子。 ## 准备环境 具体搭建步骤不在本文介绍
- thingsboard 3.2.1
- tb-gateway 2.5.5.2
- python3 3.7
OPC UA服务器
使用python的opcua搭建,各个步骤含义看注释 依赖安装 pip install opcua
import sys
sys.path.insert(0, "..")
import time
from opcua import ua, Server, uamethod
@uamethod
def set_value(parent, node_id, value):
"""
给node_id的节点赋值为value
:param parent:
:param node_id:
:param value:
:return: res:boolean
"""
node = server.get_node(node_id)
res = False
if ua.AccessLevel.CurrentWrite in list(node.get_access_level()):
node.set_value(value)
res = True
return res
def argument(name, date_type, value_rank):
"""
构建参数
:param name:
:param date_type: ua.ObjectIds.xxx
:param value_rank:
:return:
"""
v = ua.Argument()
v.Name = name
v.DataType = ua.NodeId(date_type)
v.ValueRank = value_rank
v.ArrayDimensions = []
v.Description = ua.LocalizedText(name)
return v
if __name__ == "__main__":
global server
server = Server()
# 服务地址
server.set_endpoint("opc.tcp://0.0.0.0:4840/freeopcua/server/")
# 允许的访问策略
server.set_security_policy([
ua.SecurityPolicyType.NoSecurity,
ua.SecurityPolicyType.Basic256Sha256_SignAndEncrypt,
ua.SecurityPolicyType.Basic256Sha256_Sign])
uri = "http://examples.freeopcua.github.io"
idx = server.register_namespace(uri)
objects = server.get_objects_node()
# 添加节点
dev = objects.add_object(idx, "Device")
dev_name = dev.add_variable(idx, "Name", "opcua_sensor")
dev_battery = dev.add_variable(idx, "BatteryLevel", 9.0)
dev_humidity = dev.add_variable(idx, "Humidity", 13)
dev_switch = dev.add_variable(idx, "Switch", 1)
dev_temperature = dev.add_variable(idx, "Temperature", 19)
# 设置客户端可写
dev_switch.set_writable()
# 定义方法参数
in_arg_node_id = argument("node_id", ua.ObjectIds.String, -1)
in_arg_value = argument("value", ua.ObjectIds.Int64, -1)
out_arg = argument("result", ua.ObjectIds.Boolean, -1)
# 添加方法
set_value_node = dev.add_method(idx, "set_value", set_value, [in_arg_node_id, in_arg_value], [out_arg])
# 启动服务
server.start()
try:
count = 0
while True:
time.sleep(1)
count += 0.1
dev_battery.set_value(count)
finally:
server.stop()
OPC UA py可视化客户端
安装
pip3 install opcua-client
pip3 install PyQt5
命令行执行opcua-client
就可以看到下面界面,在前面的python opcua服务器
启动的情况下,点击右上方的Connect按钮
就能看到数据
路径就是图中左边的树形路径,推荐使用图中右边的,命名空间标识符+节点标识符
。像这样:Switch
的值就是 ${ns=2;i=5}
原因文章末尾说。
tb网关配置
tb_gateway.yaml中
打开opcua连接器
thingsboard:
host: localhost
port: 1883
remoteShell: false
remoteConfiguration: false
security:
accessToken: z5zRQmSXe64U39my83jd
qos: 0
storage:
type: memory
read_records_count: 100
max_records_count: 100000
connectors: #打开opcua连接器
-
name: OPC-UA Connector
type: opcua
configuration: opcua-test.json
opcua-test.json配置 「和python代码中的服务器设置对应」
- path的俩种配置方法「路径」「nodeId」可按照opcua客户端快速定位
- switch开关量
ns=2;i=5
是opcua中的nodeid 作为属性上传到tb - humidity湿度、batteryLevel、temperature 作为遥测数据
- set_value对应py中定义控制方法供tb服务端rpc调用
不推荐使用下列路径方式
"deviceNodePattern": "${Root\\.Objects\\.Device}",
"deviceNamePattern": "${Root\\.Objects\\.Device\\.Name}",
{
"server": {
"name": "OPC-UA Default Server",
"url": "localhost:4840/freeopcua/server/",
"timeoutInMillis": 5000,
"scanPeriodInMillis": 5000,
"disableSubscriptions":true,
"subCheckPeriodInMillis": 100,
"showMap": false,
"security": "Basic128Rsa15",
"identity": {
"type": "anonymous"
},
"mapping": [
{
"deviceNodePattern": "${ns=2;i=1}",
"deviceNamePattern": "${ns=2;i=2}",
"attributes": [
{
"key": "switch",
"path": "${ns=2;i=5}"
}
],
"timeseries": [
{
"key": "humidity",
"path": "${ns=2;i=4}"
},
{
"key": "batteryLevel",
"path": "${ns=2;i=3}"
},
{
"key": "temperature °C",
"path": "${ns=2;i=6}"
}
],
"rpc_methods": [
{
"method": "set_value"
}
],
"attributes_updates": [
{
"attributeOnThingsBoard": "deviceName",
"attributeOnDevice": "${ns=2;i=2}"
}
]
}
]
}
}
thingsboard ui观察结果
设备列表出现opcua-test.json中配置的设备,设备名称是在py服务器中定义的。
这一步第一次使用tb网关的朋友很容易忽视,直接去tb网关查看设备遥测数据,网关实际工作中会接入多种协议的多个设备,所以遥测数据等 不会出现在网关上面。而是如果对应的连接器配置文件定义正确的情况下会在设备列表新出现json文件定义的设备
服务端rpc远程调用控制方法
postman示例:图1请求jwt_token;图2发送双向控制命令,得到响应
2.6版本网关使用问题
有朋友用2.6版本的tb网关,完全按照上面方式操作,网关会报下面的错list index out of range
。
""2021-04-20 14:07:59" - ERROR - [opcua_connector.py] - opcua_connector - 285 - list index out of range"
Traceback (most recent call last):
File "/Users/weijixin/PycharmProjects/thingsboard-gateway/thingsboard_gateway/connectors/opcua/opcua_connector.py", line 268, in scan_nodes_from_config
devices_info_array = self.__search_general_info(device_configuration)
File "/Users/weijixin/PycharmProjects/thingsboard-gateway/thingsboard_gateway/connectors/opcua/opcua_connector.py", line 410, in __search_general_info
device_name_node = device_name_node[0]
加群740168592
联系后,源码debug看了下
这个方法内出了问题
如果使用命名空间标识符+节点标识符
。像这样:${ns=2;i=5}
代码执行的就是图中A代码块
,如果使用path方式就执行图中B代码块,debug可以看到B代码块找某个节点是循坏所有的子节点,性能低。而且2.6的C代码块部分
我认为是有问题的,他会把Root.Objects.Device.Name
解析成Root.Objects.Name
,然后就报错说找不到节点,如果直接配置成Root.Objects.Name
不符合opcua的路径,也不符合官网文档的节点介绍。
说这么多,一句话就是:用${ns=2;i=5}
这种方式就可以了。