Completed
Pull Request — master (#270)
by
unknown
04:11
created

XmlExporter   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 278
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 278
rs 10
wmc 27

17 Methods

Rating   Name   Duplication   Size   Complexity  
A __init__() 0 4 1
A add_object_xml() 0 20 1
A add_object_type_xml() 0 2 1
A export_xml() 0 14 2
A _get_xml_aliases() 0 3 1
A add_reference_xml() 0 3 1
A _add_refs() 0 3 1
D node_to_etree() 0 26 8
A add_variable_type_xml() 0 2 1
A build_etree() 0 19 2
B add_variable_xml() 0 35 1
A dump_etree() 0 7 1
A _get_xml_namespace_uris() 0 3 1
A _get_node() 0 3 1
A add_datatype_xml() 0 2 1
A add_method_xml() 0 2 1
A _add_ref_sub_els() 0 9 2
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
import xml.etree.ElementTree as Et
7
8
from opcua import ua
9
from opcua.ua import object_ids as o_ids  # FIXME needed because the reverse look up isn't part of ObjectIds Class
10
11
12
class XmlExporter(object):
13
14
    def __init__(self, server, node_set_attrs):
15
        self.logger = logging.getLogger(__name__)
16
        self.etree = Et.ElementTree(Et.Element('UANodeSet', node_set_attrs))
17
        self.server = server
18
19
    def build_etree(self, node_list):
20
        """
21
        Create an XML etree object from a list of nodes
22
        Args:
23
            node_list: list of Node objects for export
24
25
        Returns:
26
        """
27
        self.logger.info('Building XML etree')
28
29
        # add all namespace uris to the XML etree
30
        self._get_xml_namespace_uris()  # TODO not done yet
31
32
        # add all required aliases to the XML etree
33
        self._get_xml_aliases()  # TODO not done yet
34
35
        # add all nodes in the list to the XML etree
36
        for node in node_list:
37
            self.node_to_etree(node)
38
39
    def export_xml(self, xmlpath):
40
        """
41
        Write the XML etree in the exporter object to a file
42
        Args:
43
            xmlpath: string representing the path/file name
44
45
        Returns:
46
        """
47
        # try to write the XML etree to a file
48
        self.logger.info('Exporting XML file to %s', xmlpath)
49
        try:
50
            self.etree.write(xmlpath, short_empty_elements=False)
51
        except TypeError as e:  # TODO where to find which exceptions etree.write() raises?
52
            self.logger.error("Error writing XML to file: ", e)
53
54
    def dump_etree(self):
55
        """
56
        Dump etree to console for debugging
57
        Returns:
58
        """
59
        self.logger.info('Dumping XML etree to console')
60
        Et.dump(self.etree)
61
62
    def node_to_etree(self, node):
63
        """
64
        Add the necessary XML sub elements to the etree for exporting the node
65
        Args:
66
            node: Node object which will be added to XML etree
67
68
        Returns:
69
        """
70
        node_class = node.get_node_class()
71
72
        if node_class is ua.NodeClass.Object:
73
            self.add_object_xml(node)
74
        elif node_class is ua.NodeClass.ObjectType:
75
            self.add_object_type_xml(node)
76
        elif node_class is ua.NodeClass.Variable:
77
            self.add_variable_xml(node)
78
        elif node_class is ua.NodeClass.VariableType:
79
            self.add_variable_type_xml(node)
80
        elif node_class is ua.NodeClass.RefernceType:
81
            self.add_reference_xml(node)
82
        elif node_class is ua.NodeClass.DataType:
83
            self.add_datatype_xml(node)
84
        elif node_class is ua.NodeClass.Method:
85
            self.add_method_xml(node)
86
        else:
87
            self.logger.info("Not implemented node type: %s ", node_class)
88
89
    def _get_node(self, obj):
90
        # TODO not sure if this is required for exporter, check on functionality
91
        pass
92
93
        # ORIGINAL CODE FROM IMPORTER
94
        # node = ua.AddNodesItem()
95
        # node.RequestedNewNodeId = ua.NodeId.from_string(obj.nodeid)
96
        # node.BrowseName = ua.QualifiedName.from_string(obj.browsename)
97
        # node.NodeClass = getattr(ua.NodeClass, obj.nodetype[2:])
98
        # if obj.parent:
99
        #     node.ParentNodeId = ua.NodeId.from_string(obj.parent)
100
        # if obj.parentlink:
101
        #     node.ReferenceTypeId = self.to_nodeid(obj.parentlink)
102
        # if obj.typedef:
103
        #     node.TypeDefinition = ua.NodeId.from_string(obj.typedef)
104
        # return node
105
106
    def add_object_xml(self, obj):
107
        """
108
        Add a UA object element to the XML tree
109
        """
110
        browsename = obj.get_browse_name().to_string()
111
        nodeid = obj.nodeid.to_string()
112
113
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
114
115
        refs = obj.get_references()
116
117
        obj_el = Et.SubElement(self.etree.getroot(),
118
                               'UAObject',
119
                               BrowseName=browsename,
120
                               NodeId=nodeid)
121
122
        disp_el = Et.SubElement(obj_el, 'DisplayName', )
123
        disp_el.text = displayname
124
125
        self._add_ref_sub_els(obj_el, refs)
126
127
    def add_object_type_xml(self, obj):
128
        pass
129
130
        # ORIGINAL CODE FROM IMPORTER
131
        # node = self._get_node(obj)
132
        # attrs = ua.ObjectTypeAttributes()
133
        # if obj.desc:
134
        #     attrs.Description = ua.LocalizedText(obj.desc)
135
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
136
        # attrs.IsAbstract = obj.abstract
137
        # node.NodeAttributes = attrs
138
        # self.server.add_nodes([node])
139
        # self._add_refs(obj)
140
141
    def add_variable_xml(self, obj):
142
        """
143
        Add a UA variable element to the XML tree
144
        """
145
        browsename = obj.get_browse_name().to_string()
146
        datatype = o_ids.ObjectIdNames[obj.get_data_type().Identifier]
147
        nodeid = obj.nodeid.to_string()
148
        parent = obj.get_parent().nodeid.to_string()
149
        acccesslevel = str(obj.get_attribute(ua.AttributeIds.AccessLevel).Value.Value)
150
        useraccesslevel = str(obj.get_attribute(ua.AttributeIds.UserAccessLevel).Value.Value)
151
152
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
153
154
        value = str(obj.get_value())
155
156
        refs = obj.get_references()  # []  # TODO get list of refs here
157
158
        var_el = Et.SubElement(self.etree.getroot(),
159
                               'UAVariable',
160
                               BrowseName=browsename,
161
                               DataType=datatype,
162
                               NodeId=nodeid,
163
                               ParentNodeId=parent,
164
                               AccessLevel=acccesslevel,
165
                               UserAccessLevel=useraccesslevel)
166
167
        disp_el = Et.SubElement(var_el, 'DisplayName', )
168
        disp_el.text = displayname
169
170
        self._add_ref_sub_els(var_el, refs)
171
172
        val_el = Et.SubElement(var_el, 'Value')
173
174
        valx_el = Et.SubElement(val_el, 'uax:' + datatype)
175
        valx_el.text = value
176
177
    def add_variable_type_xml(self, obj):
178
        pass
179
180
        # ORIGINAL CODE FROM IMPORTER
181
        # node = self._get_node(obj)
182
        # attrs = ua.VariableTypeAttributes()
183
        # if obj.desc:
184
        #     attrs.Description = ua.LocalizedText(obj.desc)
185
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
186
        # attrs.DataType = self.to_nodeid(obj.datatype)
187
        # if obj.value and len(obj.value) == 1:
188
        #     attrs.Value = obj.value[0]
189
        # if obj.rank:
190
        #     attrs.ValueRank = obj.rank
191
        # if obj.abstract:
192
        #     attrs.IsAbstract = obj.abstract
193
        # if obj.dimensions:
194
        #     attrs.ArrayDimensions = obj.dimensions
195
        # node.NodeAttributes = attrs
196
        # self.server.add_nodes([node])
197
        # self._add_refs(obj)
198
199
    def add_method_xml(self, obj):
200
        pass
201
202
        # ORIGINAL CODE FROM IMPORTER
203
        # node = self._get_node(obj)
204
        # attrs = ua.MethodAttributes()
205
        # if obj.desc:
206
        #     attrs.Description = ua.LocalizedText(obj.desc)
207
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
208
        # if obj.accesslevel:
209
        #     attrs.AccessLevel = obj.accesslevel
210
        # if obj.useraccesslevel:
211
        #     attrs.UserAccessLevel = obj.useraccesslevel
212
        # if obj.minsample:
213
        #     attrs.MinimumSamplingInterval = obj.minsample
214
        # if obj.dimensions:
215
        #     attrs.ArrayDimensions = obj.dimensions
216
        # node.NodeAttributes = attrs
217
        # self.server.add_nodes([node])
218
        # self._add_refs(obj)
219
220
    def add_reference_xml(self, obj):
221
        # MAY NOT BE NEEDED
222
        pass
223
224
        # ORIGINAL CODE FROM IMPORTER
225
        # node = self._get_node(obj)
226
        # attrs = ua.ReferenceTypeAttributes()
227
        # if obj.desc:
228
        #     attrs.Description = ua.LocalizedText(obj.desc)
229
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
230
        # if obj. inversename:
231
        #     attrs.InverseName = ua.LocalizedText(obj.inversename)
232
        # if obj.abstract:
233
        #     attrs.IsAbstract = obj.abstract
234
        # if obj.symmetric:
235
        #     attrs.Symmetric = obj.symmetric
236
        # node.NodeAttributes = attrs
237
        # self.server.add_nodes([node])
238
        # self._add_refs(obj)
239
240
    def add_datatype_xml(self, obj):
241
        pass
242
243
        # ORIGINAL CODE FROM IMPORTER
244
        # node = self._get_node(obj)
245
        # attrs = ua.DataTypeAttributes()
246
        # if obj.desc:
247
        #     attrs.Description = ua.LocalizedText(obj.desc)
248
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
249
        # if obj.abstract:
250
        #     attrs.IsAbstract = obj.abstract
251
        # node.NodeAttributes = attrs
252
        # self.server.add_nodes([node])
253
        # self._add_refs(obj)
254
255
    def _add_refs(self, obj):
256
        # MAY NOT BE NEEDED
257
        pass
258
259
        # ORIGINAL CODE FROM IMPORTER
260
        # if not obj.refs:
261
        #     return
262
        # refs = []
263
        # for data in obj.refs:
264
        #     ref = ua.AddReferencesItem()
265
        #     ref.IsForward = True
266
        #     ref.ReferenceTypeId = self.to_nodeid(data.reftype)
267
        #     ref.SourceNodeId = ua.NodeId.from_string(obj.nodeid)
268
        #     ref.TargetNodeClass = ua.NodeClass.DataType
269
        #     ref.TargetNodeId = ua.NodeId.from_string(data.target)
270
        #     refs.append(ref)
271
        # self.server.add_references(refs)
272
273
    def _get_xml_namespace_uris(self):
274
        # TODO name space uris should be exported
275
        pass
276
277
    def _get_xml_aliases(self):
278
        # TODO aliases need to be created at the top of the xml
279
        pass
280
281
    @staticmethod
282
    def _add_ref_sub_els(parent_el, refs):
283
        refs_el = Et.SubElement(parent_el, 'References')
284
285
        for ref in refs:
286
            ref_name = o_ids.ObjectIdNames[ref.ReferenceTypeId.Identifier]
287
            ref_forward = str(ref.IsForward)
288
            refx_el = Et.SubElement(refs_el, 'Reference', IsForward=ref_forward, ReferenceType=ref_name)
289
            refx_el.text = ref.NodeId.to_string()
290