Completed
Push — master ( 482a3c...c19b0f )
by Olivier
04:21
created

Node._fill_delete_reference_item()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
c 0
b 0
f 0
dl 0
loc 8
ccs 8
cts 8
cp 1
crap 1
rs 9.4285
1
"""
2
High level node object, to access node attribute
3
and browse address space
4
"""
5
6 1
from opcua import ua
7 1
from opcua.common import events
8 1
import opcua.common
9
10 1
def _check_results(results, reqlen = 1):
11 1
    assert len(results) == reqlen, results
12 1
    for r in results:
13 1
        r.check()
14
15 1
def _to_nodeid(nodeid):
16 1
    if isinstance(nodeid, int):
17 1
        return ua.TwoByteNodeId(nodeid)
18 1
    elif isinstance(nodeid, Node):
19 1
        return nodeid.nodeid
20
    elif isinstance(nodeid, ua.NodeId):
21
        return nodeid
22
    elif type(nodeid) in (str, bytes):
23
        return ua.NodeId.from_string(nodeid)
24
    else:
25
        raise ua.UaError("Could not resolve '{0}' to a type id".format(nodeid))
26
27 1
class Node(object):
28
29
    """
30
    High level node object, to access node attribute,
31
    browse and populate address space.
32
    Node objects are usefull as-is but they do not expose the entire
33
    OPC-UA protocol. Feel free to look at the code of this class and call
34
    directly UA services methods to optimize your code
35
    """
36
37 1
    def __init__(self, server, nodeid):
38 1
        self.server = server
39 1
        self.nodeid = None
40 1
        if isinstance(nodeid, Node):
41 1
            self.nodeid = nodeid.nodeid
42 1
        elif isinstance(nodeid, ua.NodeId):
43 1
            self.nodeid = nodeid
44 1
        elif type(nodeid) in (str, bytes):
45 1
            self.nodeid = ua.NodeId.from_string(nodeid)
46 1
        elif isinstance(nodeid, int):
47 1
            self.nodeid = ua.NodeId(nodeid, 0)
48
        else:
49
            raise ua.UaError("argument to node must be a NodeId object or a string defining a nodeid found {0} of type {1}".format(nodeid, type(nodeid)))
50
51 1
    def __eq__(self, other):
52 1
        if isinstance(other, Node) and self.nodeid == other.nodeid:
53 1
            return True
54 1
        return False
55
56 1
    def __ne__(self, other):
57
        return not self.__eq__(other)
58
59 1
    def __str__(self):
60 1
        return "Node({0})".format(self.nodeid)
61 1
    __repr__ = __str__
62
63 1
    def __hash__(self):
64 1
        return self.nodeid.__hash__()
65
66 1
    def get_browse_name(self):
67
        """
68
        Get browse name of a node. A browse name is a QualifiedName object
69
        composed of a string(name) and a namespace index.
70
        """
71 1
        result = self.get_attribute(ua.AttributeIds.BrowseName)
72 1
        return result.Value.Value
73
74 1
    def get_display_name(self):
75
        """
76
        get description attribute of node
77
        """
78 1
        result = self.get_attribute(ua.AttributeIds.DisplayName)
79 1
        return result.Value.Value
80
81 1
    def get_data_type(self):
82
        """
83
        get data type of node as NodeId
84
        """
85 1
        result = self.get_attribute(ua.AttributeIds.DataType)
86 1
        return result.Value.Value
87
88 1
    def get_data_type_as_variant_type(self):
89
        """
90
        get data type of node as VariantType
91
        This only works if node is a variable, otherwise type
92
        may not be convertible to VariantType
93
        """
94 1
        result = self.get_attribute(ua.AttributeIds.DataType)
95 1
        return opcua.common.ua_utils.data_type_to_variant_type(Node(self.server, result.Value.Value))
96
97 1
    def get_access_level(self):
98
        """
99
        Get the access level attribute of the node as a set of AccessLevel enum values.
100
        """
101
        result = self.get_attribute(ua.AttributeIds.AccessLevel)
102
        return ua.AccessLevel.parse_bitfield(result.Value.Value)
103
104 1
    def get_user_access_level(self):
105
        """
106
        Get the user access level attribute of the node as a set of AccessLevel enum values.
107
        """
108
        result = self.get_attribute(ua.AttributeIds.UserAccessLevel)
109
        return ua.AccessLevel.parse_bitfield(result.Value.Value)
110
111 1
    def get_event_notifier(self):
112
        """
113
        Get the event notifier attribute of the node as a set of EventNotifier enum values.
114
        """
115 1
        result = self.get_attribute(ua.AttributeIds.EventNotifier)
116 1
        return ua.EventNotifier.parse_bitfield(result.Value.Value)
117
118 1
    def set_event_notifier(self, values):
119
        """
120
        Set the event notifier attribute.
121
122
        :param values: an iterable of EventNotifier enum values.
123
        """
124 1
        event_notifier_bitfield = ua.EventNotifier.to_bitfield(values)
125 1
        self.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(event_notifier_bitfield, ua.VariantType.Byte)))
126
127 1
    def get_node_class(self):
128
        """
129
        get node class attribute of node
130
        """
131 1
        result = self.get_attribute(ua.AttributeIds.NodeClass)
132 1
        return result.Value.Value
133
134 1
    def get_description(self):
135
        """
136
        get description attribute class of node
137
        """
138 1
        result = self.get_attribute(ua.AttributeIds.Description)
139 1
        return result.Value.Value
140
141 1
    def get_value(self):
142
        """
143
        Get value of a node as a python type. Only variables ( and properties) have values.
144
        An exception will be generated for other node types.
145
        """
146 1
        result = self.get_data_value()
147 1
        return result.Value.Value
148
149 1
    def get_data_value(self):
150
        """
151
        Get value of a node as a DataValue object. Only variables (and properties) have values.
152
        An exception will be generated for other node types.
153
        DataValue contain a variable value as a variant as well as server and source timestamps
154
        """
155 1
        return self.get_attribute(ua.AttributeIds.Value)
156
157 1
    def set_array_dimensions(self, value):
158
        """
159
        Set attribute ArrayDimensions of node
160
        make sure it has the correct data type
161
        """
162 1
        v = ua.Variant(value, ua.VariantType.UInt32)
163 1
        self.set_attribute(ua.AttributeIds.ArrayDimensions, ua.DataValue(v))
164
165 1
    def get_array_dimensions(self):
166
        """
167
        Read and return ArrayDimensions attribute of node
168
        """
169 1
        res = self.get_attribute(ua.AttributeIds.ArrayDimensions)
170 1
        return res.Value.Value
171
172 1
    def set_value_rank(self, value):
173
        """
174
        Set attribute ArrayDimensions of node
175
        """
176 1
        v = ua.Variant(value, ua.VariantType.Int32)
177 1
        self.set_attribute(ua.AttributeIds.ValueRank, ua.DataValue(v))
178
179 1
    def get_value_rank(self):
180
        """
181
        Read and return ArrayDimensions attribute of node
182
        """
183 1
        res = self.get_attribute(ua.AttributeIds.ValueRank)
184 1
        return res.Value.Value
185
186 1
    def set_value(self, value, varianttype=None):
187
        """
188
        Set value of a node. Only variables(properties) have values.
189
        An exception will be generated for other node types.
190
        value argument is either:
191
        * a python built-in type, converted to opc-ua
192
        optionnaly using the variantype argument.
193
        * a ua.Variant, varianttype is then ignored
194
        * a ua.DataValue, you then have full control over data send to server
195
        """
196 1
        datavalue = None
197 1
        if isinstance(value, ua.DataValue):
198 1
            datavalue = value
199 1
        elif isinstance(value, ua.Variant):
200 1
            datavalue = ua.DataValue(value)
201
        else:
202 1
            datavalue = ua.DataValue(ua.Variant(value, varianttype))
203 1
        self.set_attribute(ua.AttributeIds.Value, datavalue)
204
205 1
    set_data_value = set_value
206
207 1
    def set_writable(self, writable=True):
208
        """
209
        Set node as writable by clients.
210
        A node is always writable on server side.
211
        """
212 1
        if writable:
213 1
            self.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
214 1
            self.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
215
        else:
216 1
            self.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
217 1
            self.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
218
219 1
    def set_attr_bit(self, attr, bit):
220 1
        val = self.get_attribute(attr)
221 1
        val.Value.Value = ua.ua_binary.set_bit(val.Value.Value, bit)
222 1
        self.set_attribute(attr, val)
223
224 1
    def unset_attr_bit(self, attr, bit):
225 1
        val = self.get_attribute(attr)
226 1
        val.Value.Value = ua.ua_binary.unset_bit(val.Value.Value, bit)
227 1
        self.set_attribute(attr, val)
228
229 1
    def set_read_only(self):
230
        """
231
        Set a node as read-only for clients.
232
        A node is always writable on server side.
233
        """
234
        return self.set_writable(False)
235
236 1
    def set_attribute(self, attributeid, datavalue):
237
        """
238
        Set an attribute of a node
239
        attributeid is a member of ua.AttributeIds
240
        datavalue is a ua.DataValue object
241
        """
242 1
        attr = ua.WriteValue()
243 1
        attr.NodeId = self.nodeid
244 1
        attr.AttributeId = attributeid
245 1
        attr.Value = datavalue
246 1
        params = ua.WriteParameters()
247 1
        params.NodesToWrite = [attr]
248 1
        result = self.server.write(params)
249 1
        result[0].check()
250
251 1
    def get_attribute(self, attr):
252
        """
253
        Read one attribute of a node
254
        result code from server is checked and an exception is raised in case of error
255
        """
256 1
        rv = ua.ReadValueId()
257 1
        rv.NodeId = self.nodeid
258 1
        rv.AttributeId = attr
259 1
        params = ua.ReadParameters()
260 1
        params.NodesToRead.append(rv)
261 1
        result = self.server.read(params)
262 1
        result[0].StatusCode.check()
263 1
        return result[0]
264
265 1
    def get_attributes(self, attrs):
266
        """
267
        Read several attributes of a node
268
        list of DataValue is returned
269
        """
270 1
        params = ua.ReadParameters()
271 1
        for attr in attrs:
272 1
            rv = ua.ReadValueId()
273 1
            rv.NodeId = self.nodeid
274 1
            rv.AttributeId = attr
275 1
            params.NodesToRead.append(rv)
276
277 1
        results = self.server.read(params)
278 1
        return results
279
280 1
    def get_children(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified):
281
        """
282
        Get all children of a node. By default hierarchical references and all node classes are returned.
283
        Other reference types may be given:
284
        References = 31
285
        NonHierarchicalReferences = 32
286
        HierarchicalReferences = 33
287
        HasChild = 34
288
        Organizes = 35
289
        HasEventSource = 36
290
        HasModellingRule = 37
291
        HasEncoding = 38
292
        HasDescription = 39
293
        HasTypeDefinition = 40
294
        GeneratesEvent = 41
295
        Aggregates = 44
296
        HasSubtype = 45
297
        HasProperty = 46
298
        HasComponent = 47
299
        HasNotifier = 48
300
        HasOrderedComponent = 49
301
        """
302 1
        return self.get_referenced_nodes(refs, ua.BrowseDirection.Forward, nodeclassmask)
303
304 1
    def get_properties(self):
305
        """
306
        return properties of node.
307
        properties are child nodes with a reference of type HasProperty and a NodeClass of Variable
308
        """
309 1
        return self.get_children(refs=ua.ObjectIds.HasProperty, nodeclassmask=ua.NodeClass.Variable)
310
311 1
    def get_variables(self):
312
        """
313
        return variables of node.
314
        properties are child nodes with a reference of type HasComponent and a NodeClass of Variable
315
        """
316 1
        return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Variable)
317
318 1
    def get_methods(self):
319
        """
320
        return methods of node.
321
        properties are child nodes with a reference of type HasComponent and a NodeClass of Method
322
        """
323 1
        return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Method)
324
325 1
    def get_children_descriptions(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
326 1
        return self.get_references(refs, ua.BrowseDirection.Forward, nodeclassmask, includesubtypes)
327
328 1
    def get_encoding_refs(self):
329
        return self.get_referenced_nodes(ua.ObjectIds.HasEncoding, ua.BrowseDirection.Forward)
330
331 1
    def get_description_refs(self):
332
        return self.get_referenced_nodes(ua.ObjectIds.HasDescription, ua.BrowseDirection.Forward)
333
334 1
    def get_references(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
335
        """
336
        returns references of the node based on specific filter defined with:
337
338
        refs = ObjectId of the Reference
339
        direction = Browse direction for references
340
        nodeclassmask = filter nodes based on specific class
341
        includesubtypes = If true subtypes of the reference (ref) are also included
342
        """
343 1
        desc = ua.BrowseDescription()
344 1
        desc.BrowseDirection = direction
345 1
        desc.ReferenceTypeId = _to_nodeid(refs)
346 1
        desc.IncludeSubtypes = includesubtypes
347 1
        desc.NodeClassMask = nodeclassmask
348 1
        desc.ResultMask = ua.BrowseResultMask.All
349
350 1
        desc.NodeId = self.nodeid
351 1
        params = ua.BrowseParameters()
352 1
        params.View.Timestamp = ua.get_win_epoch()
353 1
        params.NodesToBrowse.append(desc)
354 1
        params.RequestedMaxReferencesPerNode = 0
355 1
        results = self.server.browse(params)
356
357 1
        references = self._browse_next(results)
358 1
        return references
359
360 1
    def _browse_next(self, results):
361 1
        references = results[0].References
362 1
        while results[0].ContinuationPoint:
363
            params = ua.BrowseNextParameters()
364
            params.ContinuationPoints = [results[0].ContinuationPoint]
365
            params.ReleaseContinuationPoints = False
366
            results = self.server.browse_next(params)
367
            references.extend(results[0].References)
368 1
        return references
369
370 1
    def get_referenced_nodes(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
371
        """
372
        returns referenced nodes based on specific filter
373
        Paramters are the same as for get_references
374
375
        """
376 1
        references = self.get_references(refs, direction, nodeclassmask, includesubtypes)
377 1
        nodes = []
378 1
        for desc in references:
379 1
            node = Node(self.server, desc.NodeId)
380 1
            nodes.append(node)
381 1
        return nodes
382
383 1
    def get_type_definition(self):
384
        """
385
        returns type definition of the node.
386
        """
387 1
        references = self.get_references(refs=ua.ObjectIds.HasTypeDefinition, direction=ua.BrowseDirection.Forward)
388 1
        if len(references) == 0:
389 1
            return None
390 1
        return references[0].NodeId
391
392 1
    def get_path(self, max_length=20, as_string=False):
393
        """
394
        Attempt to find path of node from root node and return it as a list of Nodes.
395
        There might several possible paths to a node, this function will return one
396
        Some nodes may be missing references, so this method may
397
        return an empty list
398
        Since address space may have circular references, a max length is specified
399
400
        """
401 1
        path = self._get_path(max_length)
402 1
        path = [Node(self.server, ref.NodeId) for ref in path]
403 1
        path.append(self)
404 1
        if as_string:
405 1
            path = [el.get_browse_name().to_string() for el in path]
406 1
        return path
407
408 1
    def _get_path(self, max_length=20):
409
        """
410
        Attempt to find path of node from root node and return it as a list of Nodes.
411
        There might several possible paths to a node, this function will return one
412
        Some nodes may be missing references, so this method may
413
        return an empty list
414
        Since address space may have circular references, a max length is specified
415
416
        """
417 1
        path = []
418 1
        node = self
419 1
        while True:
420 1
            refs = node.get_references(refs=ua.ObjectIds.HierarchicalReferences, direction=ua.BrowseDirection.Inverse)
421 1
            if len(refs) > 0:
422 1
                path.insert(0, refs[0])
423 1
                node = Node(self.server, refs[0].NodeId)
424 1
                if len(path) >= (max_length -1):
425 1
                    return path
426
            else:
427 1
                return path
428
429 1
    def get_parent(self):
430
        """
431
        returns parent of the node.
432
        A Node may have several parents, the first found is returned.
433
        This method uses reverse references, a node might be missing such a link,
434
        thus we will not find its parent.
435
        """
436 1
        refs = self.get_references(refs=ua.ObjectIds.HierarchicalReferences, direction=ua.BrowseDirection.Inverse)
437 1
        if len(refs) > 0:
438 1
            return Node(self.server, refs[0].NodeId)
439
        else:
440
            return None
441
442 1
    def get_child(self, path):
443
        """
444
        get a child specified by its path from this node.
445
        A path might be:
446
        * a string representing a qualified name.
447
        * a qualified name
448
        * a list of string
449
        * a list of qualified names
450
        """
451 1
        if type(path) not in (list, tuple):
452 1
            path = [path]
453 1
        rpath = self._make_relative_path(path)
454 1
        bpath = ua.BrowsePath()
455 1
        bpath.StartingNode = self.nodeid
456 1
        bpath.RelativePath = rpath
457 1
        result = self.server.translate_browsepaths_to_nodeids([bpath])
458 1
        result = result[0]
459 1
        result.StatusCode.check()
460
        # FIXME: seems this method may return several nodes
461 1
        return Node(self.server, result.Targets[0].TargetId)
462
463 1
    def _make_relative_path(self, path):
464 1
        rpath = ua.RelativePath()
465 1
        for item in path:
466 1
            el = ua.RelativePathElement()
467 1
            el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
468 1
            el.IsInverse = False
469 1
            el.IncludeSubtypes = True
470 1
            if isinstance(item, ua.QualifiedName):
471
                el.TargetName = item
472
            else:
473 1
                el.TargetName = ua.QualifiedName.from_string(item)
474 1
            rpath.Elements.append(el)
475 1
        return rpath
476
477 1
    def read_raw_history(self, starttime=None, endtime=None, numvalues=0):
478
        """
479
        Read raw history of a node
480
        result code from server is checked and an exception is raised in case of error
481
        If numvalues is > 0 and number of events in period is > numvalues
482
        then result will be truncated
483
        """
484 1
        details = ua.ReadRawModifiedDetails()
485 1
        details.IsReadModified = False
486 1
        if starttime:
487 1
            details.StartTime = starttime
488
        else:
489 1
            details.StartTime = ua.get_win_epoch()
490 1
        if endtime:
491 1
            details.EndTime = endtime
492
        else:
493 1
            details.EndTime = ua.get_win_epoch()
494 1
        details.NumValuesPerNode = numvalues
495 1
        details.ReturnBounds = True
496 1
        result = self.history_read(details)
497 1
        return result.HistoryData.DataValues
498
499 1 View Code Duplication
    def history_read(self, details):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
500
        """
501
        Read raw history of a node, low-level function
502
        result code from server is checked and an exception is raised in case of error
503
        """
504 1
        valueid = ua.HistoryReadValueId()
505 1
        valueid.NodeId = self.nodeid
506 1
        valueid.IndexRange = ''
507
508 1
        params = ua.HistoryReadParameters()
509 1
        params.HistoryReadDetails = details
510 1
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
511 1
        params.ReleaseContinuationPoints = False
512 1
        params.NodesToRead.append(valueid)
513 1
        result = self.server.history_read(params)[0]
514 1
        return result
515
516 1
    def read_event_history(self, starttime=None, endtime=None, numvalues=0, evtypes=ua.ObjectIds.BaseEventType):
517
        """
518
        Read event history of a source node
519
        result code from server is checked and an exception is raised in case of error
520
        If numvalues is > 0 and number of events in period is > numvalues
521
        then result will be truncated
522
        """
523
524 1
        details = ua.ReadEventDetails()
525 1
        if starttime:
526 1
            details.StartTime = starttime
527
        else:
528 1
            details.StartTime = ua.get_win_epoch()
529 1
        if endtime:
530 1
            details.EndTime = endtime
531
        else:
532 1
            details.EndTime = ua.get_win_epoch()
533 1
        details.NumValuesPerNode = numvalues
534
535 1
        if not isinstance(evtypes, (list, tuple)):
536 1
            evtypes = [evtypes]
537
538 1
        evtypes = [Node(self.server, evtype) for evtype in evtypes]
539
540 1
        evfilter = events.get_filter_from_event_type(evtypes)
541 1
        details.Filter = evfilter
542
543 1
        result = self.history_read_events(details)
544 1
        event_res = []
545 1
        for res in result.HistoryData.Events:
546 1
            event_res.append(events.Event.from_event_fields(evfilter.SelectClauses, res.EventFields))
547 1
        return event_res
548
549 1 View Code Duplication
    def history_read_events(self, details):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
550
        """
551
        Read event history of a node, low-level function
552
        result code from server is checked and an exception is raised in case of error
553
        """
554 1
        valueid = ua.HistoryReadValueId()
555 1
        valueid.NodeId = self.nodeid
556 1
        valueid.IndexRange = ''
557
558 1
        params = ua.HistoryReadParameters()
559 1
        params.HistoryReadDetails = details
560 1
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
561 1
        params.ReleaseContinuationPoints = False
562 1
        params.NodesToRead.append(valueid)
563 1
        result = self.server.history_read(params)[0]
564 1
        return result
565
566 1
    def delete(self, delete_references=True, recursive=False):
567
        """
568
        Delete node from address space
569
        """
570
        results = opcua.common.manage_nodes.delete_nodes(self.server, [self], recursive, delete_references)
571
        _check_results(results)
572
573 1
    def _fill_delete_reference_item(self, rdesc, bidirectional = False):
574 1
        ditem = ua.DeleteReferencesItem()
575 1
        ditem.SourceNodeId = self.nodeid
576 1
        ditem.TargetNodeId = rdesc.NodeId
577 1
        ditem.ReferenceTypeId = rdesc.ReferenceTypeId
578 1
        ditem.IsForward = rdesc.IsForward
579 1
        ditem.DeleteBidirectional = bidirectional
580 1
        return ditem
581
582 1
    def delete_reference(self, target, reftype, forward=True, bidirectional=True):
583
        """
584
        Delete given node's references from address space
585
        """
586 1
        known_refs = self.get_references(reftype, includesubtypes=False)
587 1
        targetid = _to_nodeid(target)
588
589 1
        for r in known_refs:
590 1
            if r.NodeId == targetid and r.IsForward == forward:
591 1
                rdesc = r
592 1
                break
593
        else:
594 1
            raise ua.UaStatusCodeError(ua.StatusCodes.BadNotFound)
595
596 1
        ditem = self._fill_delete_reference_item(rdesc, bidirectional)
597 1
        self.server.delete_references([ditem])[0].check()
598
599 1
    def add_reference(self, target, reftype, forward=True, bidirectional=True):
600
        """
601
        Add reference to node
602
        """
603
604 1
        aitem = ua.AddReferencesItem()
605 1
        aitem.SourceNodeId = self.nodeid
606 1
        aitem.TargetNodeId = _to_nodeid(target)
607 1
        aitem.ReferenceTypeId = _to_nodeid(reftype)
608 1
        aitem.IsForward = forward
609
610 1
        params = [aitem]
611
612 1
        if bidirectional:
613 1
            aitem2 = ua.AddReferencesItem()
614 1
            aitem2.SourceNodeId = aitem.TargetNodeId
615 1
            aitem2.TargetNodeId = aitem.SourceNodeId
616 1
            aitem2.ReferenceTypeId = aitem.ReferenceTypeId
617 1
            aitem2.IsForward = not forward
618 1
            params.append(aitem2)
619
620 1
        results =  self.server.add_references(params)
621 1
        _check_results(results, len(params))
622
623 1
    def _add_modelling_rule(self, parent, mandatory=True):
624 1
        if mandatory is not None and parent.get_node_class() == ua.NodeClass.ObjectType:
625 1
            rule=ua.ObjectIds.ModellingRule_Mandatory if mandatory else ua.ObjectIds.ModellingRule_Optional
626 1
            self.add_reference(rule, ua.ObjectIds.HasModellingRule, True, False)
627 1
        return self
628
629 1
    def set_modelling_rule(self, mandatory):
630 1
        parent = self.get_parent()
631 1
        if parent is None:
632
            return ua.StatusCode(ua.StatusCodes.BadParentNodeIdInvalid)
633 1
        if parent.get_node_class() != ua.NodeClass.ObjectType:
634 1
            return ua.StatusCode(ua.StatusCodes.BadTypeMismatch)
635
        # remove all existing modelling rule
636 1
        rules = self.get_references(ua.ObjectIds.HasModellingRule)
637 1
        self.server.delete_references(list(map(self._fill_delete_reference_item, rules)))
638
639 1
        self._add_modelling_rule(parent, mandatory)
640 1
        return ua.StatusCode()
641
642 1
    def add_folder(self, nodeid, bname):
643 1
        return  opcua.common.manage_nodes.create_folder(self, nodeid, bname)._add_modelling_rule(self)
644
645 1
    def add_object(self, nodeid, bname, objecttype=None):
646 1
        return opcua.common.manage_nodes.create_object(self, nodeid, bname, objecttype)._add_modelling_rule(self)
647
648 1
    def add_variable(self, nodeid, bname, val, varianttype=None, datatype=None):
649 1
        return opcua.common.manage_nodes.create_variable(self, nodeid, bname, val, varianttype, datatype)._add_modelling_rule(self)
650
651 1
    def add_object_type(self, nodeid, bname):
652 1
        return opcua.common.manage_nodes.create_object_type(self, nodeid, bname)
653
654 1
    def add_variable_type(self, nodeid, bname, datatype):
655
        return opcua.common.manage_nodes.create_variable_type(self, nodeid, bname, datatype)
656
657 1
    def add_data_type(self, nodeid, bname, description=None):
658 1
        return opcua.common.manage_nodes.create_data_type(self, nodeid, bname, description=None)
659
660 1
    def add_property(self, nodeid, bname, val, varianttype=None, datatype=None):
661 1
        return opcua.common.manage_nodes.create_property(self, nodeid, bname, val, varianttype, datatype)._add_modelling_rule(self)
662
663 1
    def add_method(self, *args):
664 1
        return opcua.common.manage_nodes.create_method(self, *args)._add_modelling_rule(self)
665
666 1
    def add_reference_type(self, nodeid, bname, symmetric=True, inversename=None):
667 1
        return opcua.common.manage_nodes.create_reference_type(self, nodeid, bname, symmetric, inversename)
668
669 1
    def call_method(self, methodid, *args):
670
        return opcua.common.methods.call_method(self, methodid, *args)
671