@@ -588,15 +588,14 @@ async def __find_nodes(self, node_list_to_search, current_parent_node, nodes, pa
588588 node_paths = [node ['path' ] if isinstance (node , dict ) else node for node in nodes ]
589589 current_node_path = '.' .join (
590590 node_path .split (':' )[- 1 ] for node_path in node_paths ) + '.' + child_node .Name
591- if not current_node_path in self .__scanning_nodes_cache :
591+ if current_node_path not in self .__scanning_nodes_cache :
592592 self .__scanning_nodes_cache [current_node_path ] = [* nodes , {
593593 'path' : f'{ child_node .NamespaceIndex } :{ child_node .Name } ' , 'node' : node }]
594594 if self .__show_map and path :
595595 if children_nodes_count < 1000 or counter % 1000 == 0 :
596596 self .__log .info ('Checking path: %s' , path + '.' + f'{ child_node .Name } ' )
597597
598- if re .fullmatch (re .escape (node_list_to_search [0 ]), child_node .Name ) or node_list_to_search [0 ].split (':' )[
599- - 1 ] == child_node .Name :
598+ if re .fullmatch (re .escape (node_list_to_search [0 ]), child_node .Name ) or node_list_to_search [0 ].split (':' )[- 1 ] == child_node .Name :
600599 if self .__show_map :
601600 self .__log .info ('Found node: %s' , child_node .Name )
602601 new_nodes = [* nodes , {'path' : f'{ child_node .NamespaceIndex } :{ child_node .Name } ' , 'node' : node }]
@@ -643,7 +642,7 @@ async def find_node_name_space_index(self, path):
643642 unresolved = path [resolved_level :]
644643 return await self .__find_nodes (unresolved , current_parent_node = parent_node , nodes = resolved )
645644
646- async def _get_device_info_by_pattern (self , pattern , get_first = False ):
645+ async def _get_device_info_by_pattern (self , pattern , get_first = False , parent_node = None ):
647646 """
648647 Method used to retrieve device name/profile by pattern.
649648 Device name/profile can consist of path expressions or NodeId expressions but not both.
@@ -653,7 +652,7 @@ async def _get_device_info_by_pattern(self, pattern, get_first=False):
653652 3. If no matches found, returns the pattern as is.
654653 """
655654
656- search_results = await self .__get_device_expression_value_by_path (pattern )
655+ search_results = await self .__get_device_expression_value_by_path (pattern , parent_node = parent_node )
657656 if len (search_results ) > 0 :
658657 return search_results [0 ] if get_first else search_results
659658
@@ -663,10 +662,10 @@ async def _get_device_info_by_pattern(self, pattern, get_first=False):
663662
664663 return [pattern ]
665664
666- async def __get_device_expression_value_by_path (self , pattern : str ) -> List [str ]:
665+ async def __get_device_expression_value_by_path (self , pattern : str , parent_node = None ) -> List [str ]:
667666 results = []
668667
669- search_result = re .search (r"\${([A-Za-z.:\\\d\[\]]+)}" , pattern )
668+ search_result = re .search (r"\${([A-Za-z.:_\- \\\d\[\]]+)}" , pattern )
670669 if search_result :
671670 try :
672671 group = search_result .group (0 )
@@ -675,7 +674,7 @@ async def __get_device_expression_value_by_path(self, pattern: str) -> List[str]
675674 self .__log .error ('Invalid pattern: %s' , pattern )
676675 return results
677676
678- nodes = await self .find_nodes (node_path )
677+ nodes = await self .find_nodes (node_path , current_parent_node = parent_node )
679678 self .__log .debug ('Found device name nodes: %s' , nodes )
680679
681680 for node in nodes :
@@ -785,23 +784,33 @@ async def _create_new_devices(self):
785784 continue
786785 self .__log .debug ('Found devices: %s' , nodes )
787786
788- device_names = await self ._get_device_info_by_pattern (
789- device_config .get ('deviceInfo' , {}).get ('deviceNameExpression' ))
787+ for node in nodes :
788+ device_name_expression = device_config .get ('deviceInfo' , {}).get ('deviceNameExpression' )
789+
790+ is_abs_path = self .__is_absolute_path (device_name_expression )
791+ device_names = await self ._get_device_info_by_pattern (device_name_expression ,
792+ parent_node = node [- 1 ]['node' ] if not is_abs_path else None )
793+
794+ for device_name in device_names :
795+ scanned_devices .append (device_name )
790796
791- for device_name in device_names :
792- scanned_devices .append (device_name )
793- if device_name not in existing_devices :
794- for node in nodes :
797+ if device_name not in existing_devices :
795798 converter = self .__load_converter (device_config )
796- device_profile = await self ._get_device_info_by_pattern (
797- device_config .get ('deviceInfo' , {}).get ('deviceProfileExpression' , 'default' ),
798- get_first = True )
799+
800+ device_profile_expression = device_config .get ('deviceInfo' , {}).get ('deviceProfileExpression' ,
801+ 'default' )
802+ is_abs_path = self .__is_absolute_path (device_profile_expression )
803+ device_profile = await self ._get_device_info_by_pattern (device_profile_expression ,
804+ get_first = True ,
805+ parent_node = node [- 1 ]['node' ] if not is_abs_path else None )
806+
799807 device_config = {** device_config , 'device_name' : device_name , 'device_type' : device_profile }
800808 device_path = [node_path_node_object ['path' ] for node_path_node_object in node ]
801809 self .__device_nodes .append (
802810 Device (path = device_path , name = device_name , device_profile = device_profile ,
803811 config = device_config ,
804- converter = converter (device_config , self .__converter_log ),
812+ converter = converter (
813+ device_config , self .__converter_log ),
805814 converter_for_sub = converter (device_config ,
806815 self .__converter_log ) if self .__enable_subscriptions else None ,
807816 device_node = node [- 1 ]['node' ],
@@ -1467,6 +1476,13 @@ async def __fetch_server_limitations(self):
14671476 def __is_node_identifier (path ):
14681477 return isinstance (path , str ) and re .match (r"(ns=\d+;[isgb]=[^}]+)" , path )
14691478
1479+ @staticmethod
1480+ def __is_absolute_path (path ):
1481+ try :
1482+ return path .replace ('${' , '' ).split ('\\ .' )[0 ] == 'Root'
1483+ except Exception :
1484+ return False
1485+
14701486
14711487class SubHandler :
14721488 def __init__ (self , queue , logger , status_change_callback ):
0 commit comments