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

XmlExporter._get_xml_nodeid()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
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
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 export_xml(self, node_list, xmlpath):
20
        self.logger.info('Exporting XML file to %s', xmlpath)
21
22
        # add all nodes in the list to an XML etree
23
        for node in node_list:
24
            self.node_to_xml(node)
25
26
        # try to write the XML etree to a file
27
        try:
28
            self.etree.write(xmlpath, short_empty_elements=False)
29
        except TypeError as e:  # TODO where to find which exceptions etree.write() raises?
30
            self.logger.error("Error writing XML to file: ", e)
31
32
    def dump_xml(self):
33
        self.logger.info('Dumping XML file to console')
34
        Et.dump(self.etree)
35
36
    def node_to_xml(self, node):
37
        node_class = node.get_node_class()
38
39
        if node_class is ua.NodeClass.Object:
40
            self.add_object(node)
41
        elif node_class is ua.NodeClass.UaObjectType:
42
            self.add_object_type(node)
43
        elif node_class is ua.NodeClass.Variable:
44
            self.add_variable(node)
45
        elif node_class is ua.NodeClass.VariableType:
46
            self.add_variable_type(node)
47
        elif node_class is ua.NodeClass.RefernceType:
48
            self.add_reference(node)
49
        elif node_class is ua.NodeClass.DataType:
50
            self.add_datatype(node)
51
        elif node_class is ua.NodeClass.Method:
52
            self.add_method(node)
53
        else:
54
            self.logger.info("Not implemented node type: %s ", node_class)
55
56
    def _get_node(self, obj):
57
        # TODO not sure if this is required for exporter, check on functionality
58
        pass
59
60
        # ORIGINAL CODE FROM IMPORTER
61
        # node = ua.AddNodesItem()
62
        # node.RequestedNewNodeId = ua.NodeId.from_string(obj.nodeid)
63
        # node.BrowseName = ua.QualifiedName.from_string(obj.browsename)
64
        # node.NodeClass = getattr(ua.NodeClass, obj.nodetype[2:])
65
        # if obj.parent:
66
        #     node.ParentNodeId = ua.NodeId.from_string(obj.parent)
67
        # if obj.parentlink:
68
        #     node.ReferenceTypeId = self.to_nodeid(obj.parentlink)
69
        # if obj.typedef:
70
        #     node.TypeDefinition = ua.NodeId.from_string(obj.typedef)
71
        # return node
72
73
    def to_nodeid(self, nodeid):
74
        # TODO migrate this function to _get_xml_nodeid and delete
75
        pass
76
77
        # ORIGINAL CODE FROM IMPORTER
78
        # if not nodeid:
79
        #     return ua.NodeId(ua.ObjectIds.String)
80
        # elif "=" in nodeid:
81
        #     return ua.NodeId.from_string(nodeid)
82
        # elif hasattr(ua.ObjectIds, nodeid):
83
        #     return ua.NodeId(getattr(ua.ObjectIds, nodeid))
84
        # else:
85
        #     if nodeid in self.parser.aliases:
86
        #         nodeid = self.parser.aliases[nodeid]
87
        #     else:
88
        #         nodeid = "i={}".format(getattr(ua.ObjectIds, nodeid))
89
        #     return ua.NodeId.from_string(nodeid)
90
91
    def add_object(self, obj):
92
        pass
93
94
        # ORIGINAL CODE FROM IMPORTER
95
        # node = self._get_node(obj)
96
        # attrs = ua.ObjectAttributes()
97
        # if obj.desc:
98
        #     attrs.Description = ua.LocalizedText(obj.desc)
99
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
100
        # attrs.EventNotifier = obj.eventnotifier
101
        # node.NodeAttributes = attrs
102
        # self.server.add_nodes([node])
103
        # self._add_refs(obj)
104
105
    def add_object_type(self, obj):
106
        pass
107
108
        # ORIGINAL CODE FROM IMPORTER
109
        # node = self._get_node(obj)
110
        # attrs = ua.ObjectTypeAttributes()
111
        # if obj.desc:
112
        #     attrs.Description = ua.LocalizedText(obj.desc)
113
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
114
        # attrs.IsAbstract = obj.abstract
115
        # node.NodeAttributes = attrs
116
        # self.server.add_nodes([node])
117
        # self._add_refs(obj)
118
119
    def add_variable(self, obj):
120
        """
121
        Add a UA variable element to the XML tree
122
        """
123
        browsename = self._get_xml_browsename(obj)
124
        datatype = o_ids.ObjectIdNames[obj.get_data_type().Identifier]
125
        nodeid = self._get_xml_nodeid(obj)
126
        parent = self._get_xml_parent(obj)
127
        acccesslevel = str(obj.get_access_level().val)
128
        useraccesslevel = str(obj.get_user_access_level().val)
129
130
        displayname = obj.get_display_name().Text.decode(encoding='UTF8')
131
132
        value = str(obj.get_value())
133
134
        refs = []  # TODO get list of refs here
135
136
        var_el = Et.SubElement(self.etree.getroot(),
137
                               'UAVariable',
138
                               BrowseName=browsename,
139
                               DataType=datatype,
140
                               NodeId=nodeid,
141
                               ParentNodeId=parent,
142
                               AccessLevel=acccesslevel,
143
                               UserAccessLevel=useraccesslevel)
144
145
        disp_el = Et.SubElement(var_el, 'DisplayName', )
146
        disp_el.text = displayname
147
148
        refs_el = Et.SubElement(var_el, 'References')
149
150
        # TODO creating XML ref elements not done yet
151
        for ref in refs:
152
            refx_el = Et.SubElement(refs_el, 'Reference', ReferenceType=ref)
153
154
        val_el = Et.SubElement(var_el, 'Value')
155
156
        valx_el = Et.SubElement(val_el, 'uax:' + datatype)
157
        valx_el.text = value
158
159
        # ORIGINAL CODE FROM IMPORTER
160
        # node = self._get_node(obj)
161
        # attrs = ua.VariableAttributes()
162
        # if obj.desc:
163
        #     attrs.Description = ua.LocalizedText(obj.desc)
164
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
165
        # attrs.DataType = self.to_nodeid(obj.datatype)
166
        # # if obj.value and len(obj.value) == 1:
167
        # if obj.value is not None:
168
        #     attrs.Value = self._add_variable_value(obj, )
169
        # if obj.rank:
170
        #     attrs.ValueRank = obj.rank
171
        # if obj.accesslevel:
172
        #     attrs.AccessLevel = obj.accesslevel
173
        # if obj.useraccesslevel:
174
        #     attrs.UserAccessLevel = obj.useraccesslevel
175
        # if obj.minsample:
176
        #     attrs.MinimumSamplingInterval = obj.minsample
177
        # if obj.dimensions:
178
        #     attrs.ArrayDimensions = obj.dimensions
179
        # node.NodeAttributes = attrs
180
        # self.server.add_nodes([node])
181
        # self._add_refs(obj)
182
183
    def _add_variable_value(self, obj):
184
        """
185
        Returns the value for a Variable based on the objects valuetype.
186
        """
187
        # MAY NOT BE NEEDED
188
        pass
189
190
        # ORIGINAL CODE FROM IMPORTER
191
        # if obj.valuetype == 'ListOfLocalizedText':
192
        #     return ua.Variant([ua.LocalizedText(txt) for txt in obj.value], None)
193
        # elif obj.valuetype == 'EnumValueType':
194
        #     values = []
195
        #     for ev in obj.value:
196
        #         enum_value = ua.EnumValueType()
197
        #         enum_value.DisplayName = ua.LocalizedText(ev['DisplayName'])
198
        #         enum_value.Description = ua.LocalizedText(ev['Description'])
199
        #         enum_value.Value = int(ev['Value'])
200
        #         values.append(enum_value)
201
        #     return values
202
        # elif obj.valuetype == 'Argument':
203
        #     values = []
204
        #     for arg in obj.value:
205
        #         argument = ua.Argument()
206
        #         argument.Name = arg['Name']
207
        #         argument.Description = ua.LocalizedText(arg['Description'])
208
        #         argument.DataType = self.to_nodeid(arg['DataType'])
209
        #         argument.ValueRank = int(arg['ValueRank'])
210
        #         argument.ArrayDimensions = arg['ArrayDimensions']
211
        #         values.append(argument)
212
        #     return values
213
        #
214
        # return ua.Variant(obj.value, getattr(ua.VariantType, obj.valuetype))
215
216
217
    def add_variable_type(self, obj):
218
        pass
219
220
        # ORIGINAL CODE FROM IMPORTER
221
        # node = self._get_node(obj)
222
        # attrs = ua.VariableTypeAttributes()
223
        # if obj.desc:
224
        #     attrs.Description = ua.LocalizedText(obj.desc)
225
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
226
        # attrs.DataType = self.to_nodeid(obj.datatype)
227
        # if obj.value and len(obj.value) == 1:
228
        #     attrs.Value = obj.value[0]
229
        # if obj.rank:
230
        #     attrs.ValueRank = obj.rank
231
        # if obj.abstract:
232
        #     attrs.IsAbstract = obj.abstract
233
        # if obj.dimensions:
234
        #     attrs.ArrayDimensions = obj.dimensions
235
        # node.NodeAttributes = attrs
236
        # self.server.add_nodes([node])
237
        # self._add_refs(obj)
238
239
    def add_method(self, obj):
240
        pass
241
242
        # ORIGINAL CODE FROM IMPORTER
243
        # node = self._get_node(obj)
244
        # attrs = ua.MethodAttributes()
245
        # if obj.desc:
246
        #     attrs.Description = ua.LocalizedText(obj.desc)
247
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
248
        # if obj.accesslevel:
249
        #     attrs.AccessLevel = obj.accesslevel
250
        # if obj.useraccesslevel:
251
        #     attrs.UserAccessLevel = obj.useraccesslevel
252
        # if obj.minsample:
253
        #     attrs.MinimumSamplingInterval = obj.minsample
254
        # if obj.dimensions:
255
        #     attrs.ArrayDimensions = obj.dimensions
256
        # node.NodeAttributes = attrs
257
        # self.server.add_nodes([node])
258
        # self._add_refs(obj)
259
260
    def add_reference(self, obj):
261
        # MAY NOT BE NEEDED
262
        pass
263
264
        # ORIGINAL CODE FROM IMPORTER
265
        # node = self._get_node(obj)
266
        # attrs = ua.ReferenceTypeAttributes()
267
        # if obj.desc:
268
        #     attrs.Description = ua.LocalizedText(obj.desc)
269
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
270
        # if obj. inversename:
271
        #     attrs.InverseName = ua.LocalizedText(obj.inversename)
272
        # if obj.abstract:
273
        #     attrs.IsAbstract = obj.abstract
274
        # if obj.symmetric:
275
        #     attrs.Symmetric = obj.symmetric
276
        # node.NodeAttributes = attrs
277
        # self.server.add_nodes([node])
278
        # self._add_refs(obj)
279
280
    def add_datatype(self, obj):
281
        pass
282
283
        # ORIGINAL CODE FROM IMPORTER
284
        # node = self._get_node(obj)
285
        # attrs = ua.DataTypeAttributes()
286
        # if obj.desc:
287
        #     attrs.Description = ua.LocalizedText(obj.desc)
288
        # attrs.DisplayName = ua.LocalizedText(obj.displayname)
289
        # if obj.abstract:
290
        #     attrs.IsAbstract = obj.abstract
291
        # node.NodeAttributes = attrs
292
        # self.server.add_nodes([node])
293
        # self._add_refs(obj)
294
295
    def _add_refs(self, obj):
296
        # MAY NOT BE NEEDED
297
        pass
298
299
        # ORIGINAL CODE FROM IMPORTER
300
        # if not obj.refs:
301
        #     return
302
        # refs = []
303
        # for data in obj.refs:
304
        #     ref = ua.AddReferencesItem()
305
        #     ref.IsForward = True
306
        #     ref.ReferenceTypeId = self.to_nodeid(data.reftype)
307
        #     ref.SourceNodeId = ua.NodeId.from_string(obj.nodeid)
308
        #     ref.TargetNodeClass = ua.NodeClass.DataType
309
        #     ref.TargetNodeId = ua.NodeId.from_string(data.target)
310
        #     refs.append(ref)
311
        # self.server.add_references(refs)
312
313
    @staticmethod
314
    def _get_xml_nodeid(obj):
315
        """
316
        Convert a UA NodeId object to a formatted string for XML
317
        :param obj:
318
        :return:
319
        """
320
        return 'ns=' + str(obj.nodeid.NamespaceIndex) + ';' + str(obj.nodeid.Identifier)
321
322
    @staticmethod
323
    def _get_xml_browsename(obj):
324
        bn = obj.get_browse_name()
325
        return str(bn.NamespaceIndex) + ':' + bn.Name
326
327
    def _get_xml_parent(self, obj):
328
        return self._get_xml_nodeid(obj.get_parent())