Completed
Push — master ( f1dcd1...d7870d )
by Denis
04:21
created

Node.get_methods()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 6
ccs 2
cts 2
cp 1
crap 1
rs 9.4285
c 0
b 0
f 0
1
"""
2
High level node object, to access node attribute
3
and browse address space
4
"""
5
6 1
from opcua import ua
7
from opcua.common import events
8 1
9
10
class Node(object):
11 1
12
    """
13
    High level node object, to access node attribute,
14
    browse and populate address space.
15
    Node objects are usefull as-is but they do not expose the entire
16
    OPC-UA protocol. Feel free to look at the code of this class and call
17
    directly UA services methods to optimize your code
18
    """
19
20
    def __init__(self, server, nodeid):
21 1
        self.server = server
22 1
        self.nodeid = None
23 1
        if isinstance(nodeid, Node):
24 1
            self.nodeid = nodeid.nodeid
25 1
        elif isinstance(nodeid, ua.NodeId):
26 1
            self.nodeid = nodeid
27 1
        elif type(nodeid) in (str, bytes):
28 1
            self.nodeid = ua.NodeId.from_string(nodeid)
29 1
        elif isinstance(nodeid, int):
30
            self.nodeid = ua.NodeId(nodeid, 0)
31
        else:
32
            raise ua.UaError("argument to node must be a NodeId object or a string defining a nodeid found {} of type {}".format(nodeid, type(nodeid)))
33 1
34 1
    def __eq__(self, other):
35 1
        if isinstance(other, Node) and self.nodeid == other.nodeid:
36 1
            return True
37
        return False
38 1
39
    def __ne__(self, other):
40
        return not self.__eq__(other)
41 1
42
    def __str__(self):
43 1
        return "Node({})".format(self.nodeid)
44
    __repr__ = __str__
45 1
46 1
    def __hash__(self):
47
        return self.nodeid.__hash__()
48 1
49
    def get_browse_name(self):
50
        """
51
        Get browse name of a node. A browse name is a QualifiedName object
52
        composed of a string(name) and a namespace index.
53 1
        """
54 1
        result = self.get_attribute(ua.AttributeIds.BrowseName)
55
        return result.Value.Value
56 1
57
    def get_display_name(self):
58
        """
59
        get description attribute of node
60 1
        """
61 1
        result = self.get_attribute(ua.AttributeIds.DisplayName)
62
        return result.Value.Value
63 1
64
    def get_data_type(self):
65
        """
66
        get data type of node as NodeId
67 1
        """
68 1
        result = self.get_attribute(ua.AttributeIds.DataType)
69
        return result.Value.Value
70 1
71
    def get_data_type_as_variant_type(self):
72
        """
73
        get data type of node as VariantType
74
        This only works if node is a variable, otherwise type
75
        may not be convertible to VariantType
76
        """
77 1
        result = self.get_attribute(ua.AttributeIds.DataType)
78
        return ua.DataType_to_VariantType(result.Value.Value)
79
80
    def get_access_level(self):
81
        """
82
        get access level of node as a list of AccessLevel Enum
83
        """
84 1
        result = self.get_attribute(ua.AttributeIds.AccessLevel)
85
        return ua.int_to_AccessLevel(result.Value.Value)
86
87
    def get_user_access_level(self):
88
        """
89 1
        get user access level of node as a list of AccessLevel Enum
90 1
        """
91
        result = self.get_attribute(ua.AttributeIds.UserAccessLevel)
92 1
        return ua.int_to_AccessLevel(result.Value.Value)
93
94
    def get_event_notifier(self):
95
        """
96
        get EventNotifier attribute value as a list of EventNotifier Enum
97
        """
98 1
        result = self.get_attribute(ua.AttributeIds.EventNotifier)
99
        return ua.int_to_EventNotifier(result.Value.Value)
100 1
101
    def set_event_notifier(self, enum_list):
102
        """
103
        set event notifier attribute,
104
        arg is a list of EventNotifier Enum
105 1
        """
106 1
        res = 1
107
        for en in enum_list:
108 1
            ua.set_bit(res, en.value)
109
        self.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(res, ua.VariantType.Byte)))
110
111
    def get_node_class(self):
112 1
        """
113 1
        get node class attribute of node
114
        """
115 1
        result = self.get_attribute(ua.AttributeIds.NodeClass)
116
        return result.Value.Value
117
118
    def get_description(self):
119 1
        """
120 1
        get description attribute class of node
121
        """
122 1
        result = self.get_attribute(ua.AttributeIds.Description)
123
        return result.Value.Value
124
125
    def get_value(self):
126 1
        """
127 1
        Get value of a node as a python type. Only variables ( and properties) have values.
128
        An exception will be generated for other node types.
129 1
        """
130
        result = self.get_data_value()
131
        return result.Value.Value
132
133
    def get_data_value(self):
134
        """
135
        Get value of a node as a DataValue object. Only variables (and properties) have values.
136
        An exception will be generated for other node types.
137
        DataValue contain a variable value as a variant as well as server and source timestamps
138
        """
139 1
        return self.get_attribute(ua.AttributeIds.Value)
140 1
141 1
    def set_array_dimensions(self, value):
142 1
        """
143 1
        Set attribute ArrayDimensions of node
144
        make sure it has the correct data type
145 1
        """
146 1
        v = ua.Variant(value, ua.VariantType.UInt32)
147
        self.set_attribute(ua.AttributeIds.ArrayDimensions, ua.DataValue(v))
148 1
149
    def get_array_dimensions(self):
150 1
        """
151
        Read and return ArrayDimensions attribute of node
152
        """
153
        res = self.get_attribute(ua.AttributeIds.ArrayDimensions)
154
        return res.Value.Value
155 1
156 1
    def set_value_rank(self, value):
157 1
        """
158
        Set attribute ArrayDimensions of node
159 1
        """
160 1
        v = ua.Variant(value, ua.VariantType.Int32)
161
        self.set_attribute(ua.AttributeIds.ValueRank, ua.DataValue(v))
162 1
163 1
    def get_value_rank(self):
164 1
        """
165 1
        Read and return ArrayDimensions attribute of node
166
        """
167 1
        res = self.get_attribute(ua.AttributeIds.ValueRank)
168 1
        return res.Value.Value
169 1
170 1
    def set_value(self, value, varianttype=None):
171
        """
172 1
        Set value of a node. Only variables(properties) have values.
173
        An exception will be generated for other node types.
174
        value argument is either:
175
        * a python built-in type, converted to opc-ua
176
        optionnaly using the variantype argument.
177
        * a ua.Variant, varianttype is then ignored
178
        * a ua.DataValue, you then have full control over data send to server
179 1
        """
180
        datavalue = None
181
        if isinstance(value, ua.DataValue):
182
            datavalue = value
183
        elif isinstance(value, ua.Variant):
184
            datavalue = ua.DataValue(value)
185 1
        else:
186 1
            datavalue = ua.DataValue(ua.Variant(value, varianttype))
187 1
        self.set_attribute(ua.AttributeIds.Value, datavalue)
188 1
189 1
    set_data_value = set_value
190 1
191 1
    def set_writable(self, writable=True):
192 1
        """
193
        Set node as writable by clients.
194 1
        A node is always writable on server side.
195
        """
196
        if writable:
197
            self.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
198
            self.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
199 1
        else:
200 1
            self.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
201 1
            self.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
202 1
203 1
    def set_attr_bit(self, attr, bit):
204 1
        val = self.get_attribute(attr)
205 1
        val.Value.Value = ua.set_bit(val.Value.Value, bit)
206 1
        self.set_attribute(attr, val)
207
208 1
    def unset_attr_bit(self, attr, bit):
209
        val = self.get_attribute(attr)
210
        val.Value.Value = ua.unset_bit(val.Value.Value, bit)
211
        self.set_attribute(attr, val)
212
213
    def set_read_only(self):
214
        """
215
        Set a node as read-only for clients.
216
        A node is always writable on server side.
217
        """
218
        return self.set_writable(False)
219
220
    def set_attribute(self, attributeid, datavalue):
221
        """
222
        Set an attribute of a node
223 1
        attributeid is a member of ua.AttributeIds
224
        datavalue is a ua.DataValue object
225
        """
226
        attr = ua.WriteValue()
227
        attr.NodeId = self.nodeid
228
        attr.AttributeId = attributeid
229
        attr.Value = datavalue
230
        params = ua.WriteParameters()
231
        params.NodesToWrite = [attr]
232
        result = self.server.write(params)
233
        result[0].check()
234
235
    def get_attribute(self, attr):
236
        """
237
        Read one attribute of a node
238
        result code from server is checked and an exception is raised in case of error
239
        """
240
        rv = ua.ReadValueId()
241
        rv.NodeId = self.nodeid
242
        rv.AttributeId = attr
243
        params = ua.ReadParameters()
244
        params.NodesToRead.append(rv)
245 1
        result = self.server.read(params)
246
        result[0].StatusCode.check()
247 1
        return result[0]
248
249
    def get_attributes(self, attrs):
250
        """
251
        Read several attributes of a node
252 1
        list of DataValue is returned
253
        """
254 1
        params = ua.ReadParameters()
255
        for attr in attrs:
256
            rv = ua.ReadValueId()
257 1
            rv.NodeId = self.nodeid
258
            rv.AttributeId = attr
259
            params.NodesToRead.append(rv)
260
261
        results = self.server.read(params)
262
        return results
263
264
    def get_children(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified):
265
        """
266 1
        Get all children of a node. By default hierarchical references and all node classes are returned.
267 1
        Other reference types may be given:
268 1
        References = 31
269 1
        NonHierarchicalReferences = 32
270 1
        HierarchicalReferences = 33
271 1
        HasChild = 34
272
        Organizes = 35
273 1
        HasEventSource = 36
274 1
        HasModellingRule = 37
275 1
        HasEncoding = 38
276 1
        HasDescription = 39
277 1
        HasTypeDefinition = 40
278 1
        GeneratesEvent = 41
279
        Aggregates = 44
280 1
        HasSubtype = 45
281
        HasProperty = 46
282
        HasComponent = 47
283
        HasNotifier = 48
284
        HasOrderedComponent = 49
285
        """
286 1
        return self.get_referenced_nodes(refs, ua.BrowseDirection.Forward, nodeclassmask)
287 1
288 1
    def get_properties(self):
289 1
        """
290 1
        return properties of node.
291 1
        properties are child nodes with a reference of type HasProperty and a NodeClass of Variable
292
        """
293 1
        return self.get_children(refs=ua.ObjectIds.HasProperty, nodeclassmask=ua.NodeClass.Variable)
294
295
    def get_variables(self):
296
        """
297 1
        return variables of node.
298 1
        properties are child nodes with a reference of type HasComponent and a NodeClass of Variable
299 1
        """
300 1
        return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Variable)
301
302 1
    def get_methods(self):
303
        """
304
        return methods of node.
305
        properties are child nodes with a reference of type HasComponent and a NodeClass of Method
306 1
        """
307
        return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Method)
308 1
309
    def get_children_descriptions(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
310 1
        return self.get_references(refs, ua.BrowseDirection.Forward, nodeclassmask, includesubtypes)
311
312
    def get_references(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
313
        """
314
        returns references of the node based on specific filter defined with:
315
316
        refs = ObjectId of the Reference
317
        direction = Browse direction for references
318
        nodeclassmask = filter nodes based on specific class
319 1
        includesubtypes = If true subtypes of the reference (ref) are also included
320 1
        """
321 1
        desc = ua.BrowseDescription()
322 1
        desc.BrowseDirection = direction
323 1
        desc.ReferenceTypeId = ua.TwoByteNodeId(refs)
324 1
        desc.IncludeSubtypes = includesubtypes
325 1
        desc.NodeClassMask = nodeclassmask
326 1
        desc.ResultMask = ua.BrowseResultMask.All
327 1
328
        desc.NodeId = self.nodeid
329 1
        params = ua.BrowseParameters()
330
        params.View.Timestamp = ua.win_epoch_to_datetime(0)
331 1
        params.NodesToBrowse.append(desc)
332 1
        results = self.server.browse(params)
333 1
        return results[0].References
334 1
335 1
    def get_referenced_nodes(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
336 1
        """
337 1
        returns referenced nodes based on specific filter
338 1
        Paramters are the same as for get_references
339
340
        """
341 1
        references = self.get_references(refs, direction, nodeclassmask, includesubtypes)
342 1
        nodes = []
343 1
        for desc in references:
344
            node = Node(self.server, desc.NodeId)
345 1
            nodes.append(node)
346
        return nodes
347
348
    def get_type_definition(self):
349
        """
350
        returns type definition of the node.
351
        """
352 1
        references = self.get_references(refs=ua.ObjectIds.HasTypeDefinition, direction=ua.BrowseDirection.Forward)
353 1
        if len(references) == 0:
354 1
            return ua.ObjectIds.BaseObjectType
355 1
        return references[0].NodeId.Identifier
356
357 1
    def get_parent(self):
358 1
        """
359 1
        returns parent of the node.
360
        """
361 1
        refs = self.get_references(refs=ua.ObjectIds.HierarchicalReferences, direction=ua.BrowseDirection.Inverse)
362 1
363 1
        return Node(self.server, refs[0].NodeId)
364 1
365 1
    def get_child(self, path):
366
        """
367 1
        get a child specified by its path from this node.
368
        A path might be:
369
        * a string representing a qualified name.
370
        * a qualified name
371
        * a list of string
372 1
        * a list of qualified names
373 1
        """
374 1
        if type(path) not in (list, tuple):
375
            path = [path]
376 1
        rpath = self._make_relative_path(path)
377 1
        bpath = ua.BrowsePath()
378 1
        bpath.StartingNode = self.nodeid
379 1
        bpath.RelativePath = rpath
380 1
        result = self.server.translate_browsepaths_to_nodeids([bpath])
381 1
        result = result[0]
382 1
        result.StatusCode.check()
383
        # FIXME: seems this method may return several nodes
384 1
        return Node(self.server, result.Targets[0].TargetId)
385
386
    def _make_relative_path(self, path):
387
        rpath = ua.RelativePath()
388
        for item in path:
389
            el = ua.RelativePathElement()
390
            el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
391
            el.IsInverse = False
392
            el.IncludeSubtypes = True
393
            if isinstance(item, ua.QualifiedName):
394
                el.TargetName = item
395
            else:
396 1
                el.TargetName = ua.QualifiedName.from_string(item)
397 1
            rpath.Elements.append(el)
398 1
        return rpath
399
400 1
    def read_raw_history(self, starttime=None, endtime=None, numvalues=0):
401 1
        """
402 1
        Read raw history of a node
403
        result code from server is checked and an exception is raised in case of error
404 1
        If numvalues is > 0 and number of events in period is > numvalues
405 1
        then result will be truncated
406
        """
407 1
        details = ua.ReadRawModifiedDetails()
408
        details.IsReadModified = False
409 1
        if starttime:
410 1
            details.StartTime = starttime
411
        else:
412 1
            details.StartTime = ua.DateTimeMinValue
413
        if endtime:
414
            details.EndTime = endtime
415
        else:
416
            details.EndTime = ua.DateTimeMinValue
417 1
        details.NumValuesPerNode = numvalues
418 1
        details.ReturnBounds = True
419 1
        result = self.history_read(details)
420
        return result.HistoryData.DataValues
421 1
422 1
    def history_read(self, details):
423 1
        """
424 1
        Read raw history of a node, low-level function
425 1
        result code from server is checked and an exception is raised in case of error
426 1
        """
427 1
        valueid = ua.HistoryReadValueId()
428
        valueid.NodeId = self.nodeid
429
        valueid.IndexRange = ''
430
431
        params = ua.HistoryReadParameters()
432
        params.HistoryReadDetails = details
433
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
434 1
        params.ReleaseContinuationPoints = False
435 1
        params.NodesToRead.append(valueid)
436 1
        result = self.server.history_read(params)[0]
437
        return result
438 1
439 1
    def read_event_history(self, starttime=None, endtime=None, numvalues=0, evtypes=ua.ObjectIds.BaseEventType):
440 1
        """
441
        Read event history of a source node
442 1
        result code from server is checked and an exception is raised in case of error
443 1
        If numvalues is > 0 and number of events in period is > numvalues
444 1
        then result will be truncated
445
        """
446 1
447 1
        details = ua.ReadEventDetails()
448 1
        if starttime:
449
            details.StartTime = starttime
450 1
        else:
451 1
            details.StartTime = ua.DateTimeMinValue
452 1
        if endtime:
453
            details.EndTime = endtime
454 1
        else:
455 1
            details.EndTime = ua.DateTimeMinValue
456 1
        details.NumValuesPerNode = numvalues
457
458 1
        if not isinstance(evtypes, (list, tuple)):
459 1
            evtypes = [evtypes]
460 1
461
        evtypes = [Node(self.server, evtype) for evtype in evtypes]
462
463
        evfilter = events.get_filter_from_event_type(evtypes)
464
        details.Filter = evfilter
465
466
        result = self.history_read_events(details)
467
        event_res = []
468
        for res in result.HistoryData.Events:
469
            event_res.append(events.Event.from_event_fields(evfilter.SelectClauses, res.EventFields))
470
        return event_res
471
472
    def history_read_events(self, details):
473
        """
474
        Read event history of a node, low-level function
475
        result code from server is checked and an exception is raised in case of error
476
        """
477
        valueid = ua.HistoryReadValueId()
478
        valueid.NodeId = self.nodeid
479
        valueid.IndexRange = ''
480
481
        params = ua.HistoryReadParameters()
482
        params.HistoryReadDetails = details
483
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
484
        params.ReleaseContinuationPoints = False
485
        params.NodesToRead.append(valueid)
486
        result = self.server.history_read(params)[0]
487
        return result
488
489
    # Hack for convenience methods
490
    # local import is ugly but necessary for python2 support
491
    # feel fri to propose something better but I want to split all those
492
    # create methods from Node
493
494
    def add_folder(*args, **kwargs):
495
        from opcua.common import manage_nodes
496
        return manage_nodes.create_folder(*args, **kwargs)
497
498
    def add_object(*args, **kwargs):
499
        from opcua.common import manage_nodes
500
        return manage_nodes.create_object(*args, **kwargs)
501
502
    def add_variable(*args, **kwargs):
503
        from opcua.common import manage_nodes
504
        return manage_nodes.create_variable(*args, **kwargs)
505
506
    def add_property(*args, **kwargs):
507
        from opcua.common import manage_nodes
508
        return manage_nodes.create_property(*args, **kwargs)
509
510
    def add_method(*args, **kwargs):
511
        from opcua.common import manage_nodes
512
        return manage_nodes.create_method(*args, **kwargs)
513
514
    def add_subtype(*args, **kwargs):
515
        from opcua.common import manage_nodes
516
        return manage_nodes.create_subtype(*args, **kwargs)
517
518
    def call_method(*args, **kwargs):
519
        from opcua.common import methods
520
        return methods.call_method(*args, **kwargs)
521