Completed
Push — master ( 090d15...5ed66a )
by Olivier
04:07
created

XmlExporter._add_alias_els()   A

Complexity

Conditions 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
1
"""
2
from a list of nodes in the address space, build an XML file
3
format is the one from opc-ua specification
4
"""
5
import logging
6
from collections import OrderedDict
7
import xml.etree.ElementTree as Et
8
9
from opcua import ua
10
from opcua.ua import object_ids as o_ids  # FIXME needed because the reverse look up isn't part of ObjectIds Class
11
12
13
class XmlExporter(object):
14
15
    def __init__(self, server):
16
        self.logger = logging.getLogger(__name__)
17
        self.server = server
18
        self.aliases = {}
19
20
        node_set_attributes = OrderedDict()
21
        node_set_attributes['xmlns:xsi'] = 'http://www.w3.org/2001/XMLSchema-instance'
22
        node_set_attributes['xmlns:uax'] = 'http://opcfoundation.org/UA/2008/02/Types.xsd'
23
        node_set_attributes['xmlns:xsd'] = 'http://www.w3.org/2001/XMLSchema'
24
        node_set_attributes['xmlns'] = 'http://opcfoundation.org/UA/2011/03/UANodeSet.xsd'
25
26
        self.etree = Et.ElementTree(Et.Element('UANodeSet', node_set_attributes))
27
28
    def build_etree(self, node_list, uris=None):
29
        """
30
        Create an XML etree object from a list of nodes; custom namespace uris are optional
31
        Args:
32
            node_list: list of Node objects for export
33
            uris: list of namespace uri strings
34
35
        Returns:
36
        """
37
        self.logger.info('Building XML etree')
38
39
        # add all nodes in the list to the XML etree
40
        for node in node_list:
41
            self.node_to_etree(node)
42
43
        # add all required aliases to the XML etree; must be done after nodes are added
44
        self._add_alias_els()
45
46
        if uris:
47
            # add all namespace uris to the XML etree; must be done after aliases are added
48
            self._add_namespace_uri_els(uris)
49
50
    def write_xml(self, xmlpath):
51
        """
52
        Write the XML etree in the exporter object to a file
53
        Args:
54
            xmlpath: string representing the path/file name
55
56
        Returns:
57
        """
58
        # try to write the XML etree to a file
59
        self.logger.info('Exporting XML file to %s', xmlpath)
60
        try:
61
            self.etree.write(xmlpath, short_empty_elements=False)
62
        except TypeError as e:  # TODO where to find which exceptions etree.write() raises?
63
            self.logger.error("Error writing XML to file: ", e)
64
65
    def dump_etree(self):
66
        """
67
        Dump etree to console for debugging
68
        Returns:
69
        """
70
        self.logger.info('Dumping XML etree to console')
71
        Et.dump(self.etree)
72
73
    def node_to_etree(self, node):
74
        """
75
        Add the necessary XML sub elements to the etree for exporting the node
76
        Args:
77
            node: Node object which will be added to XML etree
78
79
        Returns:
80
        """
81
        node_class = node.get_node_class()
82
83
        if node_class is ua.NodeClass.Object:
84
            self.add_etree_object(node)
85
        elif node_class is ua.NodeClass.ObjectType:
86
            self.add_etree_object_type(node)
87
        elif node_class is ua.NodeClass.Variable:
88
            self.add_etree_variable(node)
89
        elif node_class is ua.NodeClass.VariableType:
90
            self.add_etree_variable_type(node)
91
        elif node_class is ua.NodeClass.RefernceType:
92
            self.add_etree_reference(node)
93
        elif node_class is ua.NodeClass.DataType:
94
            self.add_etree_datatype(node)
95
        elif node_class is ua.NodeClass.Method:
96
            self.add_etree_method(node)
97
        else:
98
            self.logger.info("Exporting node class not implemented: %s ", node_class)
99
100 View Code Duplication
    def add_etree_object(self, obj):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
101
        """
102
        Add a UA object element to the XML etree
103
        """
104
        browsename = obj.get_browse_name().to_string()
105
        nodeid = obj.nodeid.to_string()
106
107
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
108
109
        refs = obj.get_references()
110
111
        obj_el = Et.SubElement(self.etree.getroot(),
112
                               'UAObject',
113
                               BrowseName=browsename,
114
                               NodeId=nodeid)
115
116
        disp_el = Et.SubElement(obj_el, 'DisplayName', )
117
        disp_el.text = displayname
118
119
        self._add_ref_els(obj_el, refs)
120
121 View Code Duplication
    def add_etree_object_type(self, obj):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
122
        """
123
        Add a UA object type element to the XML etree
124
        """
125
        browsename = obj.get_browse_name().to_string()
126
        nodeid = obj.nodeid.to_string()
127
128
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
129
130
        refs = obj.get_references()
131
132
        obj_el = Et.SubElement(self.etree.getroot(),
133
                               'UAObject',
134
                               BrowseName=browsename,
135
                               NodeId=nodeid)
136
137
        disp_el = Et.SubElement(obj_el, 'DisplayName', )
138
        disp_el.text = displayname
139
140
        self._add_ref_els(obj_el, refs)
141
142
    def add_etree_variable(self, obj):
143
        """
144
        Add a UA variable element to the XML etree
145
        """
146
        browsename = obj.get_browse_name().to_string()
147
        datatype = o_ids.ObjectIdNames[obj.get_data_type().Identifier]
148
        datatype_nodeid = obj.get_data_type().to_string()
149
        nodeid = obj.nodeid.to_string()
150
        parent = obj.get_parent().nodeid.to_string()
151
        acccesslevel = str(obj.get_attribute(ua.AttributeIds.AccessLevel).Value.Value)
152
        useraccesslevel = str(obj.get_attribute(ua.AttributeIds.UserAccessLevel).Value.Value)
153
        symbolicname = None  # TODO when to export this?
154
155
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
156
157
        value = str(obj.get_value())
158
159
        refs = obj.get_references()
160
161
        var_el = Et.SubElement(self.etree.getroot(),
162
                               'UAVariable',
163
                               BrowseName=browsename,
164
                               DataType=datatype,
165
                               NodeId=nodeid,
166
                               ParentNodeId=parent,
167
                               AccessLevel=acccesslevel,
168
                               UserAccessLevel=useraccesslevel)
169
170
        disp_el = Et.SubElement(var_el, 'DisplayName', )
171
        disp_el.text = displayname
172
173
        self._add_ref_els(var_el, refs)
174
175
        val_el = Et.SubElement(var_el, 'Value')
176
177
        valx_el = Et.SubElement(val_el, 'uax:' + datatype)
178
        valx_el.text = value
179
180
        # add any references that get used to aliases dict; this gets handled later
181
        self.aliases[datatype] = datatype_nodeid
182
183 View Code Duplication
    def add_etree_variable_type(self, obj):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
184
        """
185
        Add a UA variable type element to the XML etree
186
        """
187
        browsename = obj.get_browse_name().to_string()
188
        nodeid = obj.nodeid.to_string()
189
        valuerank = None  # TODO when to export this?
190
191
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
192
193
        refs = obj.get_references()
194
195
        obj_el = Et.SubElement(self.etree.getroot(),
196
                               'UAObject',
197
                               BrowseName=browsename,
198
                               NodeId=nodeid)
199
200
        disp_el = Et.SubElement(obj_el, 'DisplayName', )
201
        disp_el.text = displayname
202
203
        self._add_ref_els(obj_el, refs)
204
205
    def add_etree_method(self, obj):
206
        raise NotImplemented
207
208
    def add_etree_reference(self, obj):
209
        raise NotImplemented
210
211 View Code Duplication
    def add_etree_datatype(self, obj):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
212
        """
213
        Add a UA data type element to the XML etree
214
        """
215
        browsename = obj.get_browse_name().to_string()
216
        nodeid = obj.nodeid.to_string()
217
218
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
219
220
        refs = obj.get_references()
221
222
        obj_el = Et.SubElement(self.etree.getroot(),
223
                               'UAObject',
224
                               BrowseName=browsename,
225
                               NodeId=nodeid)
226
227
        disp_el = Et.SubElement(obj_el, 'DisplayName', )
228
        disp_el.text = displayname
229
230
        self._add_ref_els(obj_el, refs)
231
232
    def _add_namespace_uri_els(self, uris):
233
        nuris_el = Et.Element('NamespaceUris')
234
235
        for uri in uris:
236
            uri_el = Et.SubElement(nuris_el, 'Uri')
237
            uri_el.text = uri
238
239
        self.etree.getroot().insert(0, nuris_el)
240
241
    def _add_alias_els(self):
242
        aliases_el = Et.Element('Aliases')
243
244
        for k, v in self.aliases.items():
245
            ref_el = Et.SubElement(aliases_el, 'Alias', Alias=k)
246
            ref_el.text = v
247
248
        self.etree.getroot().insert(0, aliases_el)
249
250
    def _add_ref_els(self, parent_el, refs):
251
        refs_el = Et.SubElement(parent_el, 'References')
252
253
        for ref in refs:
254
            ref_name = o_ids.ObjectIdNames[ref.ReferenceTypeId.Identifier]
255
            ref_forward = str(ref.IsForward)
256
            ref_nodeid = ref.NodeId.to_string()
257
            ref_el = Et.SubElement(refs_el, 'Reference', IsForward=ref_forward, ReferenceType=ref_name)
258
            ref_el.text = ref_nodeid
259
260
            # add any references that gets used to aliases dict; this gets handled later
261
            self.aliases[ref_name] = ref_nodeid
262