2 from a list of nodes in the address space, build an XML file 3 format is the one from opc-ua specification 6 from collections
import OrderedDict
7 import xml.etree.ElementTree
as Et
11 from opcua.ua import object_ids
as o_ids
18 self.
logger = logging.getLogger(__name__)
23 node_set_attributes = OrderedDict()
24 node_set_attributes[
'xmlns:xsi'] =
'http://www.w3.org/2001/XMLSchema-instance' 25 node_set_attributes[
'xmlns:uax'] =
'http://opcfoundation.org/UA/2008/02/Types.xsd' 26 node_set_attributes[
'xmlns:xsd'] =
'http://www.w3.org/2001/XMLSchema' 27 node_set_attributes[
'xmlns'] =
'http://opcfoundation.org/UA/2011/03/UANodeSet.xsd' 29 self.
etree = Et.ElementTree(Et.Element(
'UANodeSet', node_set_attributes))
33 Create an XML etree object from a list of nodes; custom namespace uris are optional 34 Namespaces used by nodes are always exported for consistency. 36 node_list: list of Node objects for export 37 uris: list of namespace uri strings 41 self.logger.info(
'Building XML etree')
46 for node
in node_list:
55 ns_array = self.server.get_namespace_array()
63 ns_to_export = [ns_array[i]
for i
in sorted(list(self._addr_idx_to_xml_idx.keys()))
if i != 0]
70 addr_idx_to_xml_idx = {0: 0}
71 for xml_idx, addr_idx
in enumerate(idxs):
72 if addr_idx >= len(ns_array):
74 addr_idx_to_xml_idx[addr_idx] = xml_idx + 1
75 return addr_idx_to_xml_idx
79 get a list of all indexes used or references by nodes 83 node_idxs = [node.nodeid.NamespaceIndex]
84 node_idxs.append(node.get_browse_name().NamespaceIndex)
85 node_idxs.extend(ref.NodeId.NamespaceIndex
for ref
in node.get_references())
86 node_idxs = list(set(node_idxs))
88 if i != 0
and i
not in idxs:
95 i = ns_array.index(uri)
102 Write the XML etree in the exporter object to a file 104 xmlpath: string representing the path/file name 109 self.logger.info(
'Exporting XML file to %s', xmlpath)
113 self.
indent(self.etree.getroot())
114 self.etree.write(xmlpath,
119 self.etree.write(xmlpath,
126 Dump etree to console for debugging 129 self.logger.info(
'Dumping XML etree to console')
134 Add the necessary XML sub elements to the etree for exporting the node 136 node: Node object which will be added to XML etree 140 node_class = node.get_node_class()
142 if node_class
is ua.NodeClass.Object:
144 elif node_class
is ua.NodeClass.ObjectType:
146 elif node_class
is ua.NodeClass.Variable:
148 elif node_class
is ua.NodeClass.VariableType:
150 elif node_class
is ua.NodeClass.ReferenceType:
152 elif node_class
is ua.NodeClass.DataType:
154 elif node_class
is ua.NodeClass.Method:
157 self.logger.info(
"Exporting node class not implemented: %s ", node_class)
160 child_el = Et.SubElement(el, name)
166 nodeid = nodeid.nodeid
169 nodeid = copy(nodeid)
171 return nodeid.to_string()
177 return bname.to_string()
180 browsename = node.get_browse_name()
182 parent = node.get_parent()
183 displayname = node.get_display_name().Text.decode(
'utf-8')
184 desc = node.get_description().Text
185 node_el = Et.SubElement(self.etree.getroot(), nodetype)
188 if parent
is not None:
189 node_class = node.get_node_class()
190 if node_class
in (ua.NodeClass.Object, ua.NodeClass.Variable, ua.NodeClass.Method):
192 self.
_add_sub_el(node_el,
'DisplayName', displayname)
193 if desc
not in (
None,
""):
194 self.
_add_sub_el(node_el,
'Description', desc.decode(
'utf-8'))
200 Add a UA object element to the XML etree 203 var = node.get_attribute(ua.AttributeIds.EventNotifier)
204 if var.Value.Value != 0:
205 obj_el.attrib[
"EventNotifier"] = str(var.Value.Value)
210 Add a UA object type element to the XML etree 213 abstract = node.get_attribute(ua.AttributeIds.IsAbstract).Value.Value
215 obj_el.attrib[
"IsAbstract"] =
'true' 219 dtype = node.get_data_type()
220 if dtype.NamespaceIndex == 0
and dtype.Identifier
in o_ids.ObjectIdNames:
221 dtype_name = o_ids.ObjectIdNames[dtype.Identifier]
222 self.
aliases[dtype] = dtype_name
224 dtype_name = dtype.to_string()
225 rank = node.get_value_rank()
227 el.attrib[
"ValueRank"] = str(rank)
228 dim = node.get_attribute(ua.AttributeIds.ArrayDimensions)
230 el.attrib[
"ArrayDimensions"] =
",".join([str(i)
for i
in dim.Value.Value])
231 el.attrib[
"DataType"] = dtype_name
236 Add a UA variable element to the XML etree 241 accesslevel = node.get_attribute(ua.AttributeIds.AccessLevel).Value.Value
242 useraccesslevel = node.get_attribute(ua.AttributeIds.UserAccessLevel).Value.Value
246 if accesslevel
not in (0, ua.AccessLevel.CurrentRead.mask):
247 var_el.attrib[
"AccessLevel"] = str(accesslevel)
248 if useraccesslevel
not in (0, ua.AccessLevel.CurrentRead.mask):
249 var_el.attrib[
"UserAccessLevel"] = str(useraccesslevel)
251 var = node.get_attribute(ua.AttributeIds.MinimumSamplingInterval)
253 var_el.attrib[
"MinimumSamplingInterval"] = str(var.Value.Value)
254 var = node.get_attribute(ua.AttributeIds.Historizing)
256 var_el.attrib[
"Historizing"] =
'true' 261 Add a UA variable type element to the XML etree 267 abstract = node.get_attribute(ua.AttributeIds.IsAbstract)
268 if abstract.Value.Value:
269 var_el.attrib[
"IsAbstract"] =
"true" 276 var = node.get_attribute(ua.AttributeIds.Executable)
277 if var.Value.Value
is False:
278 obj_el.attrib[
"Executable"] =
"false" 279 var = node.get_attribute(ua.AttributeIds.UserExecutable)
280 if var.Value.Value
is False:
281 obj_el.attrib[
"UserExecutable"] =
"false" 286 var = obj.get_attribute(ua.AttributeIds.InverseName)
287 if var
is not None and var.Value.Value
is not None:
288 self.
_add_sub_el(obj_el,
'InverseName', var.Value.Value.Text.decode(
'utf-8'))
293 Add a UA data type element to the XML etree 299 nuris_el = Et.Element(
'NamespaceUris')
304 self.etree.getroot().insert(0, nuris_el)
307 aliases_el = Et.Element(
'Aliases')
309 ordered_keys = list(self.aliases.keys())
311 for nodeid
in ordered_keys:
313 ref_el = Et.SubElement(aliases_el,
'Alias', Alias=name)
314 ref_el.text = nodeid.to_string()
316 self.etree.getroot().insert(0, aliases_el)
319 refs = obj.get_references()
320 refs_el = Et.SubElement(parent_el,
'References')
323 if ref.ReferenceTypeId.Identifier
in o_ids.ObjectIdNames:
324 ref_name = o_ids.ObjectIdNames[ref.ReferenceTypeId.Identifier]
326 ref_name = ref.ReferenceTypeId.to_string()
327 ref_el = Et.SubElement(refs_el,
'Reference')
328 ref_el.attrib[
'ReferenceType'] = ref_name
329 if not ref.IsForward:
330 ref_el.attrib[
'IsForward'] =
'false' 333 self.
aliases[ref.ReferenceTypeId] = ref_name
337 member_el = Et.SubElement(el,
"uax:" + name)
338 if isinstance(val, (list, tuple)):
340 self.
_value_to_etree(member_el, ua.ObjectIdNames[dtype.Identifier], dtype, v)
348 if dtype ==
ua.NodeId(ua.ObjectIds.NodeId):
349 id_el = Et.SubElement(el,
"uax:Identifier")
350 id_el.text = val.to_string()
351 elif dtype ==
ua.NodeId(ua.ObjectIds.Guid):
352 id_el = Et.SubElement(el,
"uax:String")
353 id_el.text = str(val)
354 elif not hasattr(val,
"ua_types"):
355 if isinstance(val, bytes):
356 el.text = val.decode(
"utf-8")
360 for name, vtype
in val.ua_types.items():
365 var = node.get_data_value().Value
366 if var.Value
is not None:
367 val_el = Et.SubElement(el,
'Value')
375 if isinstance(val, (list, tuple)):
376 if dtype.NamespaceIndex == 0
and dtype.Identifier <= 21:
377 elname =
"uax:ListOf" + type_name
379 elname =
"uax:ListOfExtensionObject" 381 list_el = Et.SubElement(el, elname)
386 dtype_base = dtype_base.nodeid
388 if dtype_base ==
ua.NodeId(ua.ObjectIds.Enumeration):
389 dtype_base =
ua.NodeId(ua.ObjectIds.Int32)
390 type_name = ua.ObjectIdNames[dtype_base.Identifier]
392 if dtype_base.NamespaceIndex == 0
and dtype_base.Identifier <= 21:
393 type_name = ua.ObjectIdNames[dtype_base.Identifier]
394 val_el = Et.SubElement(el,
"uax:" + type_name)
401 obj_el = Et.SubElement(val_el,
"uax:ExtensionObject")
402 type_el = Et.SubElement(obj_el,
"uax:TypeId")
403 id_el = Et.SubElement(type_el,
"uax:Identifier")
404 id_el.text = dtype.to_string()
405 body_el = Et.SubElement(obj_el,
"uax:Body")
406 struct_el = Et.SubElement(body_el,
"uax:" + name)
407 for name, vtype
in val.ua_types.items():
413 copy and paste from http://effbot.org/zone/element-lib.htm#prettyprint 414 it basically walks your tree and adds spaces and newlines so the tree is 415 printed in a nice way 417 i =
"\n" + level *
" " 419 if not elem.text
or not elem.text.strip():
421 if not elem.tail
or not elem.tail.strip():
424 self.
indent(elem, level + 1)
425 if not elem.tail
or not elem.tail.strip():
428 if level
and (
not elem.tail
or not elem.tail.strip()):
def __init__(self, server)
def add_etree_method(self, node)
def value_to_etree(self, el, dtype_name, dtype, node)
def indent(self, elem, level=0)
def _bname_to_string(self, bname)
def node_to_etree(self, node)
def add_etree_object(self, node)
def write_xml(self, xmlpath, pretty=True)
def add_variable_common(self, node, el)
def _node_to_string(self, nodeid)
def _add_sub_el(self, el, name, text)
def _add_namespaces(self, nodes, uris)
def _add_idxs_from_uris(self, idxs, uris, ns_array)
def _add_ref_els(self, parent_el, obj)
def build_etree(self, node_list, uris=None)
def _add_node_common(self, nodetype, node)
def add_etree_variable(self, node)
def _make_idx_dict(self, idxs, ns_array)
def _extobj_to_etree(self, val_el, name, dtype, val)
def add_etree_variable_type(self, node)
def _add_namespace_uri_els(self, uris)
def get_base_data_type(datatype)
def _val_to_etree(self, el, dtype, val)
def _value_to_etree(self, el, type_name, dtype, val)
def add_etree_object_type(self, node)
def add_etree_reference_type(self, obj)
def add_etree_datatype(self, obj)
def member_to_etree(self, el, name, dtype, val)
def _get_ns_idxs_of_nodes(self, nodes)