Completed
Push — master ( a5d685...6787aa )
by Olivier
02:20
created

opcua.common.Node.get_attributes()   A

Complexity

Conditions 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 4.8094
Metric Value
cc 2
dl 0
loc 14
ccs 1
cts 9
cp 0.1111
crap 4.8094
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
8
9 1
class Node(object):
10
11
    """
12
    High level node object, to access node attribute,
13
    browse and populate address space.
14
    Node objects are usefull as-is but they do not expose the entire
15
    OPC-UA protocol. Feel free to look at Node code and call
16
    directly UA services methods to optimize your code
17
    """
18
19 1
    def __init__(self, server, nodeid):
20 1
        self.server = server
21 1
        self.nodeid = None
22 1
        if isinstance(nodeid, ua.NodeId):
23 1
            self.nodeid = nodeid
24 1
        elif type(nodeid) in (str, bytes):
25 1
            self.nodeid = ua.NodeId.from_string(nodeid)
26
        elif isinstance(nodeid, int):
27
            self.nodeid = ua.NodeId(nodeid, 0)
28
        else:
29
            raise ua.UaError("argument to node must be a NodeId object or a string defining a nodeid found {} of type {}".format(nodeid, type(nodeid)))
30
31 1
    def __eq__(self, other):
32 1
        if isinstance(other, Node) and self.nodeid == other.nodeid:
33 1
            return True
34 1
        return False
35
36 1
    def __str__(self):
37
        return "Node({})".format(self.nodeid)
38 1
    __repr__ = __str__
39
40 1
    def get_browse_name(self):
41
        """
42
        Get browse name of a node. A browse name is a QualifiedName object
43
        composed of a string(name) and a namespace index.
44
        """
45 1
        result = self.get_attribute(ua.AttributeIds.BrowseName)
46 1
        return result.Value.Value
47
48 1
    def get_display_name(self):
49
        """
50
        get description attribute of node
51
        """
52 1
        result = self.get_attribute(ua.AttributeIds.DisplayName)
53 1
        return result.Value.Value
54
55 1
    def get_data_type(self):
56
        """
57
        get data type of node
58
        """
59 1
        result = self.get_attribute(ua.AttributeIds.DataType)
60 1
        return result.Value.Value
61
62 1
    def get_node_class(self):
63
        """
64
        get node class attribute of node
65
        """
66 1
        result = self.get_attribute(ua.AttributeIds.NodeClass)
67 1
        return result.Value.Value
68
69 1
    def get_description(self):
70
        """
71
        get description attribute class of node
72
        """
73 1
        result = self.get_attribute(ua.AttributeIds.Description)
74 1
        return result.Value.Value
75
76 1
    def get_value(self):
77
        """
78
        Get value of a node as a python type. Only variables ( and properties) have values.
79
        An exception will be generated for other node types.
80
        """
81 1
        result = self.get_data_value()
82 1
        return result.Value.Value
83
84 1
    def get_data_value(self):
85
        """
86
        Get value of a node as a DataValue object. Only variables (and properties) have values.
87
        An exception will be generated for other node types.
88
        DataValue contain a variable value as a variant as well as server and source timestamps
89
        """
90 1
        return self.get_attribute(ua.AttributeIds.Value)
91
92 1
    def set_value(self, value, varianttype=None):
93
        """
94
        Set value of a node. Only variables(properties) have values.
95
        An exception will be generated for other node types.
96
        value argument is either:
97
        * a python built-in type, converted to opc-ua
98
        optionnaly using the variantype argument.
99
        * a ua.Variant, varianttype is then ignored
100
        * a ua.DataValue, you then have full control over data send to server
101
        """
102 1
        datavalue = None
103 1
        if isinstance(value, ua.DataValue):
104 1
            datavalue = value
105 1
        elif isinstance(value, ua.Variant):
106 1
            datavalue = ua.DataValue(value)
107
        else:
108 1
            datavalue = ua.DataValue(ua.Variant(value, varianttype))
109 1
        self.set_attribute(ua.AttributeIds.Value, datavalue)
110
111 1
    set_data_value = set_value
112
113 1
    def set_writable(self, writable=True):
114
        """
115
        Set node as writable by clients.
116
        A node is always writable on server side.
117
        """
118 1
        if writable:
119 1
            self.set_attribute(ua.AttributeIds.AccessLevel, ua.DataValue(ua.Variant(ua.AccessLevelMask.CurrentWrite, ua.VariantType.Byte)))
120 1
            self.set_attribute(ua.AttributeIds.UserAccessLevel, ua.DataValue(ua.Variant(ua.AccessLevelMask.CurrentWrite, ua.VariantType.Byte)))
121
        else:
122 1
            self.set_attribute(ua.AttributeIds.AccessLevel, ua.DataValue(ua.Variant(ua.AccessLevelMask.CurrentRead, ua.VariantType.Byte)))
123 1
            self.set_attribute(ua.AttributeIds.AccessLevel, ua.DataValue(ua.Variant(ua.AccessLevelMask.CurrentRead, ua.VariantType.Byte)))
124
125 1
    def set_read_only(self):
126
        """
127
        Set a node as read-only for clients.
128
        A node is always writable on server side.
129
        """
130
        return self.set_writable(False)
131
132 1
    def set_attribute(self, attributeid, datavalue):
133
        """
134
        Set an attribute of a node
135
        """
136 1
        attr = ua.WriteValue()
137 1
        attr.NodeId = self.nodeid
138 1
        attr.AttributeId = attributeid
139 1
        attr.Value = datavalue
140 1
        params = ua.WriteParameters()
141 1
        params.NodesToWrite = [attr]
142 1
        result = self.server.write(params)
143 1
        result[0].check()
144
145 1
    def get_attribute(self, attr):
146
        """
147
        Read one attribute of a node
148
        result code from server is checked and an exception is raised in case of error
149
        """
150 1
        rv = ua.ReadValueId()
151 1
        rv.NodeId = self.nodeid
152 1
        rv.AttributeId = attr
153 1
        params = ua.ReadParameters()
154 1
        params.NodesToRead.append(rv)
155 1
        result = self.server.read(params)
156 1
        result[0].StatusCode.check()
157 1
        return result[0]
158
159 1
    def get_attributes(self, attrs):
160
        """
161
        Read several attributes of a node
162
        list of DataValue is returned
163
        """
164
        params = ua.ReadParameters()
165
        for attr in attrs:
166
            rv = ua.ReadValueId()
167
            rv.NodeId = self.nodeid
168
            rv.AttributeId = attr
169
            params.NodesToRead.append(rv)
170
171
        results = self.server.read(params)
172
        return results
173
174 1
    def get_children(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified):
175
        """
176
        Get all children of a node. By default hierarchical references and all node classes are returned.
177
        Other reference types may be given:
178
        References = 31
179
        NonHierarchicalReferences = 32
180
        HierarchicalReferences = 33
181
        HasChild = 34
182
        Organizes = 35
183
        HasEventSource = 36
184
        HasModellingRule = 37
185
        HasEncoding = 38
186
        HasDescription = 39
187
        HasTypeDefinition = 40
188
        GeneratesEvent = 41
189
        Aggregates = 44
190
        HasSubtype = 45
191
        HasProperty = 46
192
        HasComponent = 47
193
        HasNotifier = 48
194
        HasOrderedComponent = 49
195
        """
196 1
        references = self.get_children_descriptions(refs, nodeclassmask)
197 1
        nodes = []
198 1
        for desc in references:
199 1
            node = Node(self.server, desc.NodeId)
200 1
            nodes.append(node)
201 1
        return nodes
202
203 1
    def get_properties(self):
204
        """
205
        return properties of node.
206
        properties are child nodes with a reference of type HasProperty and a NodeClass of Variable
207
        """
208
        return self.get_children(refs=ua.ObjectIds.HasProperty, nodeclassmask=ua.NodeClass.Variable)
209
210 1
    def get_children_descriptions(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
211
        """
212
        return all attributes of child nodes as UA BrowseResult structs
213
        """
214 1
        desc = ua.BrowseDescription()
215 1
        desc.BrowseDirection = ua.BrowseDirection.Forward
216 1
        desc.ReferenceTypeId = ua.TwoByteNodeId(refs)
217 1
        desc.IncludeSubtypes = includesubtypes
218 1
        desc.NodeClassMask = nodeclassmask
219 1
        desc.ResultMask = ua.BrowseResultMask.All
220
221 1
        desc.NodeId = self.nodeid
222 1
        params = ua.BrowseParameters()
223 1
        params.View.Timestamp = ua.win_epoch_to_datetime(0)
224 1
        params.NodesToBrowse.append(desc)
225 1
        results = self.server.browse(params)
226 1
        return results[0].References
227
228 1
    def get_child(self, path):
229
        """
230
        get a child specified by its path from this node.
231
        A path might be:
232
        * a string representing a qualified name.
233
        * a qualified name
234
        * a list of string
235
        * a list of qualified names
236
        """
237 1
        if type(path) not in (list, tuple):
238 1
            path = [path]
239 1
        rpath = ua.RelativePath()
240 1
        for item in path:
241 1
            el = ua.RelativePathElement()
242 1
            el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
243 1
            el.IsInverse = False
244 1
            el.IncludeSubtypes = True
245 1
            if isinstance(item, ua.QualifiedName):
246
                el.TargetName = item
247
            else:
248 1
                el.TargetName = ua.QualifiedName.from_string(item)
249 1
            rpath.Elements.append(el)
250 1
        bpath = ua.BrowsePath()
251 1
        bpath.StartingNode = self.nodeid
252 1
        bpath.RelativePath = rpath
253 1
        result = self.server.translate_browsepaths_to_nodeids([bpath])
254 1
        result = result[0]
255 1
        result.StatusCode.check()
256
        # FIXME: seems this method may return several nodes
257 1
        return Node(self.server, result.Targets[0].TargetId)
258
259 1
    def read_raw_history(self, starttime=None, endtime=None, numvalues=0, returnbounds=True):
260
        """
261
        Read raw history of a node
262
        result code from server is checked and an exception is raised in case of error
263
        """
264
        details = ua.ReadRawModifiedDetails()
265
        details.IsReadModified = False
266
        if starttime:
267
            details.StartTime = starttime
268
        if endtime:
269
            details.EndTime = endtime
270
        details.NumValuesPerNode = numvalues
271
        details.ReturnBounds = returnbounds
272
        return self.history_read(details)
273
274 1
    def history_read(self, details):
275
        """
276
        Read raw history of a node, low-level function
277
        result code from server is checked and an exception is raised in case of error
278
        """
279
        valueid = ua.HistoryReadValueId()
280
        valueid.NodeId = self.nodeid
281
        valueid.IndexRange = ''
282
283
        params = ua.HistoryReadParameters()
284
        params.HistoryReadDetails = details
285
        params.TimestampsToReturn = ua.TimestampsToReturn.Both
286
        params.ReleaseContinuationPoints = False
287
        params.NodesToRead.append(valueid)
288
        result = self.server.history_read(params)[0]
289
        return result.HistoryData
290
291
    # Hack for convenience methods
292
    # local import is ugly but necessary for python2 support
293
    # feel fri to propose something better but I want to split all those
294
    # create methods fro Node
295
296 1
    def add_folder(*args, **kwargs):
297 1
        from opcua.common import manage_nodes
298 1
        return manage_nodes.create_folder(*args, **kwargs)
299
300 1
    def add_object(*args, **kwargs):
301 1
        from opcua.common import manage_nodes
302 1
        return manage_nodes.create_object(*args, **kwargs)
303
304 1
    def add_variable(*args, **kwargs):
305 1
        from opcua.common import manage_nodes
306 1
        return manage_nodes.create_variable(*args, **kwargs)
307
308 1
    def add_property(*args, **kwargs):
309 1
        from opcua.common import manage_nodes
310 1
        return manage_nodes.create_property(*args, **kwargs)
311
312 1
    def add_method(*args, **kwargs):
313 1
        from opcua.common import manage_nodes
314 1
        return manage_nodes.create_method(*args, **kwargs)
315
316 1
    def call_method(*args, **kwargs):
317 1
        from opcua.common import methods
318
        return methods.call_method(*args, **kwargs)
319