Thingsboard gateway 3.4 连接opc_ua源码解析篇

流程图读取opc属性流程图

流程分析

获取协议连接

Thingsboard gateway中实现了很多协议连接类,代码根据配置的连接类型进行初始化并动态调用连接类,源码如下

if connector_config["config"][config] is not None:
   connector_name = connector_config["name"]

   if not self.available_connectors.get(connector_name):
       connector = self._implemented_connectors[connector_type](self,
                                                              connector_config["config"][
                                                                    config],
                                                                connector_type)
       connector.setName(connector_name)
       self.available_connectors[connector.get_name()] = connector
       connector.open()
   else:
       break
else:
   log.info("Config not found for %s", connector_type)

_implemented_connectors来源如下

if connector['type'] != "grpc":
       connector_class = None
       if connector.get('useGRPC', False):
           module_name = f'Grpc{self._default_connectors.get(connector["type"], connector.get("class"))}'
           connector_class = TBModuleLoader.import_module(connector['type'], module_name)

       if self.__grpc_manager and self.__grpc_manager.is_alive() and connector_class:
           connector_persistent_key = self._generate_persistent_key(connector,
                                                                    connectors_persistent_keys)
       else:
           connector_class = TBModuleLoader.import_module(connector['type'],
                                                          self._default_connectors.get(
                                                              connector['type'],
                                                              connector.get('class')))

       if connector_class is None:
           log.warning("Connector implementation not found for %s", connector["name"])
       else:
           self._implemented_connectors[connector['type']] = connector_class

协议类型实现类,是根据TBModuleLoader进行导入的,connector_class = TBModuleLoader.import_module(connector[‘type’], module_name)

建立连接

connector.open()

获取节点

从流程图可以看出来,获取属性主要是__search_node方法,因此我们主要来看下他的逻辑。

一般来说,如果要连接一个服务获取指定的某个属性,我们直接获取服务端值就可以了
如:
node = self.client.get_node(fullpath)
我们看下源码如何实现的

#获取全路径表达式
fullpath_pattern = regex.compile(fullpath)
#转义路径分隔符
full1 = fullpath.replace('\\\\.', '.')
#获取当前节点路径
current_node_path = self.get_node_path(current_node)
#获取当前节点类型
child_node_parent_class = current_node.get_node_class()
new_parent = current_node
#遍历子节点
for child_node in current_node.get_children():
	#获取子节点类型
    new_node_class = child_node.get_node_class()
    #获取子节点路径
    new_node_path = self.get_node_path(child_node)
    #子节点类型为view并且当前节点不为None时将当前节点路径替换为子节点路径方便下次循环
    if child_node_parent_class == ua.NodeClass.View and new_parent is not None:
        parent_path = self.get_node_path(new_parent)
        fullpath = fullpath.replace(current_node_path, parent_path)
    #转义路径分隔符
    nnp1 = new_node_path.replace('\\\\.', '.')
    nnp2 = new_node_path.replace('\\\\', '\\')
    if self.__show_map:
        self._log.debug("SHOW MAP: Current node path: %s", new_node_path)
    # 判断是否能完全匹配
    regex_fullmatch = regex.fullmatch(fullpath_pattern, nnp1) or \
                      nnp2 == full1 or \
                      nnp2 == fullpath or \
                      nnp1 == full1
    if regex_fullmatch:
        if self.__show_map:
            self._log.debug("SHOW MAP: Current node path: %s - NODE FOUND", nnp2)
        #能完全匹配则写入结果返回
        result.append(child_node)
    else:
    	#判断是否可以前缀匹配
        regex_search = fullpath_pattern.fullmatch(nnp1, partial=True) or \
                       nnp2 in full1 or \
                       nnp1 in full1
         #可以前缀匹配则进行循环遍历
         if regex_search:
	         if self.__show_map:
	             self._log.debug("SHOW MAP: Current node path: %s - NODE FOUND", new_node_path)
	         #如果类型是object,则继续往下匹配,知道完全匹配
	         if new_node_class == ua.NodeClass.Object:
	             if self.__show_map:
	                 self._log.debug("SHOW MAP: Search in %s", new_node_path)
	             self.__search_node(child_node, fullpath, result=result)
	       	 # 如果类型是method,则返回节点
	         elif new_node_class == ua.NodeClass.Method and search_method:
	             self._log.debug("Found in %s", new_node_path)
	             result.append(child_node)

写的比较复杂,原因是opc无法直接根据路径获取节点,只能通过遍历的方式进行反复对比,因此出现了源码的逻辑,对应的逻辑上我均加了注释说明

运行后发现实际上并没有进入完全匹配的逻辑,从而导致没有获取到节点,为什么会产生这样的原因是因为

if child_node_parent_class == ua.NodeClass.View and new_parent is not None:
        parent_path = self.get_node_path(new_parent)
        fullpath = fullpath.replace(current_node_path, parent_path)

这一步我这边遍历的属性里面压根就没有View类型的数据,不知道是否是我的模拟器问题,从代码逻辑上来看,这一步主要是要获取到父节点路径,并将当前节点路径拼接上父节点路径,因此直接对代码进行了二次修改,引入了parentPath变量,在遍历的时候将当前节点的路径拼接好传递下去,从而解决问题

def __search_node(self, current_node, fullpath, search_method=False, parentPath=None, result=None):
        if result is None:
            result = []
        try:
            if regex.match(r"ns=\d*;[isgb]=.*", fullpath, regex.IGNORECASE):
                if self.__show_map:
                    self._log.debug("Looking for node with config")
                node = self.client.get_node(fullpath)
                if node is None:
                    self._log.warning("NODE NOT FOUND - using configuration %s", fullpath)
                else:
                    self._log.debug("Found in %s", node)
                    result.append(node)
            else:
                fullpath_pattern = regex.compile(fullpath)
                full1 = fullpath.replace('\\\\.', '.')
     
                current_node_path = self.get_node_path(current_node)
                if parentPath is None:
                    parentPath = current_node_path
                else:
                    parentPath = parentPath + "\\." + current_node_path
                # we are allways the parent
                child_node_parent_class = current_node.get_node_class()
                new_parent = current_node
                for child_node in current_node.get_children():
                    child_node.parentPath = parentPath
                    new_node_class = child_node.get_node_class()
               
                    new_node_path = self.get_node_path(child_node)
                    if(new_node_path.startswith(parentPath) == False):
                        new_node_path = parentPath + '\\.' + new_node_path
                
                    nnp1 = new_node_path.replace('\\\\.', '.')
                    nnp2 = new_node_path.replace('\\\\', '\\')
                    if self.__show_map:
                        self._log.debug("SHOW MAP: Current node path: %s", new_node_path)
                    regex_fullmatch = regex.fullmatch(fullpath_pattern, nnp1) or \
                                      nnp2 == full1 or \
                                      nnp2 == fullpath or \
                                      nnp1 == full1
                    if regex_fullmatch:
                        if self.__show_map:
                            self._log.debug("SHOW MAP: Current node path: %s - NODE FOUND", nnp2)
                        result.append(child_node)
                    else:
                        regex_search = fullpath_pattern.fullmatch(nnp1, partial=True) or \
                                       nnp2 in full1 or \
                                       nnp1 in full1
                        if regex_search:
                            if self.__show_map:
                                self._log.debug("SHOW MAP: Current node path: %s - NODE FOUND", new_node_path)
                            if new_node_class == ua.NodeClass.Object:
                                if self.__show_map:
                                    self._log.debug("SHOW MAP: Search in %s", new_node_path)
                                self.__search_node(child_node, fullpath,parentPath=parentPath, result=result)
                            elif new_node_class == ua.NodeClass.Variable:
                                self._log.debug("Found in %s", new_node_path)
                                result.append(child_node)
                            elif new_node_class == ua.NodeClass.Method and search_method:
                                self._log.debug("Found in %s", new_node_path)
                                result.append(child_node)
        except CancelledError:
            self._log.error("Request during search has been canceled by the OPC-UA server.")
        except BrokenPipeError:
            self._log.error("Broken Pipe. Connection lost.")
        except OSError:
            self._log.debug("Stop on scanning.")
        except Exception as e:
            self._log.exception(e)

这样就能够获取到节点了,同时还需要修改__search_nodes_and_subscribe中的代码,获取到父节点路径传递进去

self.__search_node(device_info["deviceNode"], information_path,parentPath=device_info["deviceNode"].parentPath,result=information_nodes)

这样就可以正常监听数据变化了
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_三石_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值