Completed
Pull Request — master (#140)
by Olivier
02:30
created

opcua.common.Node.read_raw_history()   A

Complexity

Conditions 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

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