xmlimporter.py
Go to the documentation of this file.
00001 """
00002 add nodes defined in XML to address space
00003 format is the one from opc-ua specification
00004 """
00005 import logging
00006 import uuid
00007 from copy import copy
00008 
00009 import dateutil.parser
00010 
00011 from opcua import ua
00012 from opcua.common import xmlparser
00013 
00014 
00015 class XmlImporter(object):
00016 
00017     def __init__(self, server):
00018         self.logger = logging.getLogger(__name__)
00019         self.parser = None
00020         self.server = server
00021         self.namespaces = {}
00022         self.aliases = {}
00023 
00024     def _map_namespaces(self, namespaces_uris):
00025         """
00026         creates a mapping between the namespaces in the xml file and in the server.
00027         if not present the namespace is registered.
00028         """
00029         namespaces = {}
00030         for ns_index, ns_uri in enumerate(namespaces_uris):
00031             ns_server_index = self.server.register_namespace(ns_uri)
00032             namespaces[ns_index + 1] = ns_server_index
00033         return namespaces
00034 
00035     def _map_aliases(self, aliases):
00036         """
00037         maps the import aliases to the correct namespaces        
00038         """
00039         aliases_mapped = {}
00040         for alias, node_id in aliases.items():
00041             aliases_mapped[alias] = self._migrate_ns(self.to_nodeid(node_id))
00042         return aliases_mapped
00043 
00044     def import_xml(self, xmlpath):
00045         """
00046         import xml and return added nodes
00047         """
00048         self.logger.info("Importing XML file %s", xmlpath)
00049         self.parser = xmlparser.XMLParser(xmlpath)
00050 
00051         dnodes = self.parser.get_node_datas()
00052         dnodes = self.make_objects(dnodes)
00053 
00054         self.namespaces = self._map_namespaces(self.parser.get_used_namespaces())
00055         self.aliases = self._map_aliases(self.parser.get_aliases())
00056 
00057         nodes_parsed = self._sort_nodes_by_parentid(dnodes)
00058 
00059         nodes = []
00060         for nodedata in nodes_parsed:  # self.parser:
00061             if nodedata.nodetype == 'UAObject':
00062                 node = self.add_object(nodedata)
00063             elif nodedata.nodetype == 'UAObjectType':
00064                 node = self.add_object_type(nodedata)
00065             elif nodedata.nodetype == 'UAVariable':
00066                 node = self.add_variable(nodedata)
00067             elif nodedata.nodetype == 'UAVariableType':
00068                 node = self.add_variable_type(nodedata)
00069             elif nodedata.nodetype == 'UAReferenceType':
00070                 node = self.add_reference_type(nodedata)
00071             elif nodedata.nodetype == 'UADataType':
00072                 node = self.add_datatype(nodedata)
00073             elif nodedata.nodetype == 'UAMethod':
00074                 node = self.add_method(nodedata)
00075             else:
00076                 self.logger.warning("Not implemented node type: %s ", nodedata.nodetype)
00077                 continue
00078             nodes.append(node)
00079         return nodes
00080     
00081     def make_objects(self, node_datas):
00082         new_nodes = []
00083         for ndata in node_datas:
00084             ndata.nodeid = ua.NodeId.from_string(ndata.nodeid)
00085             ndata.browsename = ua.QualifiedName.from_string(ndata.browsename)
00086             if ndata.parent:
00087                 ndata.parent = ua.NodeId.from_string(ndata.parent)
00088             if ndata.parentlink:
00089                 ndata.parentlink = self.to_nodeid(ndata.parentlink)
00090             if ndata.typedef:
00091                 ndata.typedef = self.to_nodeid(ndata.typedef)
00092             new_nodes.append(ndata)
00093         return new_nodes
00094 
00095     def _migrate_ns(self, nodeid):
00096         """
00097         Check if the index of nodeid or browsename  given in the xml model file
00098         must be converted to a already existing namespace id based on the files
00099         namespace uri
00100 
00101         :returns: NodeId (str)
00102         """
00103         if nodeid.NamespaceIndex in self.namespaces:
00104             nodeid = copy(nodeid)
00105             nodeid.NamespaceIndex = self.namespaces[nodeid.NamespaceIndex]
00106         return nodeid
00107 
00108     def _get_node(self, obj):
00109         node = ua.AddNodesItem()
00110         node.RequestedNewNodeId = self._migrate_ns(obj.nodeid)
00111         node.BrowseName = self._migrate_ns(obj.browsename)
00112         self.logger.info("Importing xml node (%s, %s) as (%s %s)", obj.browsename, obj.nodeid, node.BrowseName, node.RequestedNewNodeId)
00113         node.NodeClass = getattr(ua.NodeClass, obj.nodetype[2:])
00114         if obj.parent:
00115             node.ParentNodeId = self._migrate_ns(obj.parent)
00116         if obj.parentlink:
00117             node.ReferenceTypeId = self._migrate_ns(obj.parentlink)
00118         if obj.typedef:
00119             node.TypeDefinition = self._migrate_ns(obj.typedef)
00120         return node
00121 
00122     def to_nodeid(self, nodeid):
00123         if isinstance(nodeid, ua.NodeId):
00124             return nodeid
00125         elif not nodeid:
00126             return ua.NodeId(ua.ObjectIds.String)
00127         elif "=" in nodeid:
00128             return ua.NodeId.from_string(nodeid)
00129         elif hasattr(ua.ObjectIds, nodeid):
00130             return ua.NodeId(getattr(ua.ObjectIds, nodeid))
00131         else:
00132             if nodeid in self.aliases:
00133                 return self.aliases[nodeid]
00134             else:
00135                 return ua.NodeId(getattr(ua.ObjectIds, nodeid))
00136 
00137     def add_object(self, obj):
00138         node = self._get_node(obj)
00139         attrs = ua.ObjectAttributes()
00140         if obj.desc:
00141             attrs.Description = ua.LocalizedText(obj.desc)
00142         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00143         attrs.EventNotifier = obj.eventnotifier
00144         node.NodeAttributes = attrs
00145         res = self.server.iserver.isession.add_nodes([node])
00146         self._add_refs(obj)
00147         res[0].StatusCode.check()
00148         return res[0].AddedNodeId
00149 
00150     def add_object_type(self, obj):
00151         node = self._get_node(obj)
00152         attrs = ua.ObjectTypeAttributes()
00153         if obj.desc:
00154             attrs.Description = ua.LocalizedText(obj.desc)
00155         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00156         attrs.IsAbstract = obj.abstract
00157         node.NodeAttributes = attrs
00158         res = self.server.iserver.isession.add_nodes([node])
00159         self._add_refs(obj)
00160         res[0].StatusCode.check()
00161         return res[0].AddedNodeId
00162 
00163     def add_variable(self, obj):
00164         node = self._get_node(obj)
00165         attrs = ua.VariableAttributes()
00166         if obj.desc:
00167             attrs.Description = ua.LocalizedText(obj.desc)
00168         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00169         attrs.DataType = self.to_nodeid(obj.datatype)
00170         if obj.value is not None:
00171             attrs.Value = self._add_variable_value(obj,)
00172         if obj.rank:
00173             attrs.ValueRank = obj.rank
00174         if obj.accesslevel:
00175             attrs.AccessLevel = obj.accesslevel
00176         if obj.useraccesslevel:
00177             attrs.UserAccessLevel = obj.useraccesslevel
00178         if obj.minsample:
00179             attrs.MinimumSamplingInterval = obj.minsample
00180         if obj.dimensions:
00181             attrs.ArrayDimensions = obj.dimensions
00182         node.NodeAttributes = attrs
00183         res = self.server.iserver.isession.add_nodes([node])
00184         self._add_refs(obj)
00185         res[0].StatusCode.check()
00186         return res[0].AddedNodeId
00187 
00188     def _make_ext_obj(self, obj):
00189         ext = getattr(ua, obj.objname)()
00190         for name, val in obj.body:
00191             if isinstance(val, str):
00192                 raise Exception("Error val should a dict", name, val)
00193             else:
00194                 for attname, v in val:
00195                     self._set_attr(ext, attname, v)
00196         return ext
00197 
00198     def _set_attr(self, obj, attname, val):
00199         # tow possible values:
00200         # either we get value directly
00201         # or a dict if it s an object or a list
00202         if isinstance(val, str):
00203             pval = xmlparser.ua_type_to_python(val, obj.ua_types[attname])
00204             setattr(obj, attname, pval)
00205         else:
00206             # so we have either an object or a list...
00207             obj2 = getattr(obj, attname)
00208             if isinstance(obj2, ua.NodeId):  # NodeId representation does not follow common rules!!
00209                 for attname2, v2 in val:
00210                     if attname2 == "Identifier":
00211                         obj2 = ua.NodeId.from_string(v2)
00212                         setattr(obj, attname, obj2)
00213                         break
00214             elif not isinstance(obj2, ua.NodeId) and not hasattr(obj2, "ua_types"):
00215                 # we probably have a list
00216                 my_list = []
00217                 for vtype, v2 in val:
00218                     my_list.append(xmlparser.ua_type_to_python(v2, vtype))
00219                 setattr(obj, attname, my_list)
00220             else:
00221                 for attname2, v2 in val:
00222                     self._set_attr(obj2, attname2, v2)
00223                 setattr(obj, attname, obj2)
00224 
00225     def _add_variable_value(self, obj):
00226         """
00227         Returns the value for a Variable based on the objects value type.
00228         """
00229         self.logger.debug("Setting value with type %s and value %s", obj.valuetype, obj.value)
00230         if obj.valuetype == 'ListOfExtensionObject':
00231             values = []
00232             for ext in obj.value:
00233                 extobj = self._make_ext_obj(ext)
00234                 values.append(extobj)
00235             return values
00236         elif obj.valuetype.startswith("ListOf"):
00237             vtype = obj.valuetype[6:]
00238             if hasattr(ua.ua_binary.Primitives, vtype):
00239                 return ua.Variant(obj.value, getattr(ua.VariantType, vtype))
00240             else:
00241                 return [getattr(ua, vtype)(v) for v in obj.value]
00242         elif obj.valuetype == 'ExtensionObject':
00243             extobj = self._make_ext_obj(obj.value)
00244             return ua.Variant(extobj, getattr(ua.VariantType, obj.valuetype))
00245         elif obj.valuetype == 'DateTime':
00246             return ua.Variant(dateutil.parser.parse(obj.value), getattr(ua.VariantType, obj.valuetype))
00247         elif obj.valuetype == 'Guid':
00248             return ua.Variant(uuid.UUID(obj.value), getattr(ua.VariantType, obj.valuetype))
00249         elif obj.valuetype == 'LocalizedText':
00250             ltext = ua.LocalizedText()
00251             for name, val in obj.value:
00252                 if name == "Text":
00253                     ltext.Text = val.encode("utf-8")
00254                 else:
00255                     self.logger.warning("While parsing localizedText value, unkown element: %s with val: %s", name, val)
00256             return ua.Variant(ltext, ua.VariantType.LocalizedText)
00257         elif obj.valuetype == 'NodeId':
00258             return ua.Variant(ua.NodeId.from_string(obj.value))
00259         else:
00260             return ua.Variant(obj.value, getattr(ua.VariantType, obj.valuetype))
00261 
00262     def add_variable_type(self, obj):
00263         node = self._get_node(obj)
00264         attrs = ua.VariableTypeAttributes()
00265         if obj.desc:
00266             attrs.Description = ua.LocalizedText(obj.desc)
00267         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00268         attrs.DataType = self.to_nodeid(obj.datatype)
00269         if obj.value and len(obj.value) == 1:
00270             attrs.Value = obj.value[0]
00271         if obj.rank:
00272             attrs.ValueRank = obj.rank
00273         if obj.abstract:
00274             attrs.IsAbstract = obj.abstract
00275         if obj.dimensions:
00276             attrs.ArrayDimensions = obj.dimensions
00277         node.NodeAttributes = attrs
00278         res = self.server.iserver.isession.add_nodes([node])
00279         self._add_refs(obj)
00280         res[0].StatusCode.check()
00281         return res[0].AddedNodeId
00282 
00283     def add_method(self, obj):
00284         node = self._get_node(obj)
00285         attrs = ua.MethodAttributes()
00286         if obj.desc:
00287             attrs.Description = ua.LocalizedText(obj.desc)
00288         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00289         if obj.accesslevel:
00290             attrs.AccessLevel = obj.accesslevel
00291         if obj.useraccesslevel:
00292             attrs.UserAccessLevel = obj.useraccesslevel
00293         if obj.minsample:
00294             attrs.MinimumSamplingInterval = obj.minsample
00295         if obj.dimensions:
00296             attrs.ArrayDimensions = obj.dimensions
00297         node.NodeAttributes = attrs
00298         res = self.server.iserver.isession.add_nodes([node])
00299         self._add_refs(obj)
00300         res[0].StatusCode.check()
00301         return res[0].AddedNodeId
00302 
00303     def add_reference_type(self, obj):
00304         node = self._get_node(obj)
00305         attrs = ua.ReferenceTypeAttributes()
00306         if obj.desc:
00307             attrs.Description = ua.LocalizedText(obj.desc)
00308         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00309         if obj. inversename:
00310             attrs.InverseName = ua.LocalizedText(obj.inversename)
00311         if obj.abstract:
00312             attrs.IsAbstract = obj.abstract
00313         if obj.symmetric:
00314             attrs.Symmetric = obj.symmetric
00315         node.NodeAttributes = attrs
00316         res = self.server.iserver.isession.add_nodes([node])
00317         self._add_refs(obj)
00318         res[0].StatusCode.check()
00319         return res[0].AddedNodeId
00320 
00321     def add_datatype(self, obj):
00322         node = self._get_node(obj)
00323         attrs = ua.DataTypeAttributes()
00324         if obj.desc:
00325             attrs.Description = ua.LocalizedText(obj.desc)
00326         attrs.DisplayName = ua.LocalizedText(obj.displayname)
00327         if obj.abstract:
00328             attrs.IsAbstract = obj.abstract
00329         node.NodeAttributes = attrs
00330         res = self.server.iserver.isession.add_nodes([node])
00331         self._add_refs(obj)
00332         res[0].StatusCode.check()
00333         return res[0].AddedNodeId
00334 
00335     def _add_refs(self, obj):
00336         if not obj.refs:
00337             return
00338         refs = []
00339         for data in obj.refs:
00340             ref = ua.AddReferencesItem()
00341             ref.IsForward = True
00342             ref.ReferenceTypeId = self.to_nodeid(data.reftype)
00343             ref.SourceNodeId = self._migrate_ns(obj.nodeid)
00344             ref.TargetNodeClass = ua.NodeClass.DataType
00345             ref.TargetNodeId = self._migrate_ns(self.to_nodeid(data.target))
00346             refs.append(ref)
00347         self.server.iserver.isession.add_references(refs)
00348 
00349     def _sort_nodes_by_parentid(self, ndatas):
00350         """
00351         Sort the list of nodes according their parent node in order to respect
00352         the dependency between nodes.
00353 
00354         :param nodes: list of NodeDataObjects
00355         :returns: list of sorted nodes
00356         """
00357         _ndatas = list(ndatas)
00358         # list of node ids that are already sorted / inserted
00359         sorted_nodes_ids = []
00360         # list of sorted nodes (i.e. XML Elements)
00361         sorted_ndatas = []
00362         all_node_ids = [data.nodeid for data in ndatas]
00363         # list of namespace indexes that are relevant for this import
00364         # we can only respect ordering nodes for namespaces indexes that
00365         # are defined in the xml file itself. Thus we assume that all other
00366         # references namespaces are already known to the server and should
00367         # not create any dependency problems (like "NodeNotFound")
00368         while len(_ndatas) > 0:
00369             pop_nodes = []
00370             for ndata in _ndatas:
00371                 # Insert nodes that
00372                 #   (1) have no parent / parent_ns is None (e.g. namespace 0)
00373                 #   (2) ns is not in list of relevant namespaces
00374                 if ndata.nodeid.NamespaceIndex not in self.namespaces or \
00375                         ndata.parent is None or \
00376                         ndata.parent not in all_node_ids:
00377                     sorted_ndatas.append(ndata)
00378                     sorted_nodes_ids.append(ndata.nodeid)
00379                     pop_nodes.append(ndata)
00380                 else:
00381                     # Check if the nodes parent is already in the list of
00382                     # inserted nodes
00383                     if ndata.parent in sorted_nodes_ids:
00384                         sorted_ndatas.append(ndata)
00385                         sorted_nodes_ids.append(ndata.nodeid)
00386                         pop_nodes.append(ndata)
00387             # Remove inserted nodes from the list
00388             for ndata in pop_nodes:
00389                 _ndatas.pop(_ndatas.index(ndata))
00390         return sorted_ndatas


ros_opcua_impl_python_opcua
Author(s): Denis Štogl , Daniel Draper
autogenerated on Sat Jun 8 2019 18:26:24