00001 """
00002 High level node object, to access node attribute
00003 and browse address space
00004 """
00005
00006 from opcua import ua
00007 from opcua.common import events
00008 import opcua.common
00009
00010 class Node(object):
00011
00012 """
00013 High level node object, to access node attribute,
00014 browse and populate address space.
00015 Node objects are usefull as-is but they do not expose the entire
00016 OPC-UA protocol. Feel free to look at the code of this class and call
00017 directly UA services methods to optimize your code
00018 """
00019
00020 def __init__(self, server, nodeid):
00021 self.server = server
00022 self.nodeid = None
00023 if isinstance(nodeid, Node):
00024 self.nodeid = nodeid.nodeid
00025 elif isinstance(nodeid, ua.NodeId):
00026 self.nodeid = nodeid
00027 elif type(nodeid) in (str, bytes):
00028 self.nodeid = ua.NodeId.from_string(nodeid)
00029 elif isinstance(nodeid, int):
00030 self.nodeid = ua.NodeId(nodeid, 0)
00031 else:
00032 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)))
00033
00034 def __eq__(self, other):
00035 if isinstance(other, Node) and self.nodeid == other.nodeid:
00036 return True
00037 return False
00038
00039 def __ne__(self, other):
00040 return not self.__eq__(other)
00041
00042 def __str__(self):
00043 return "Node({0})".format(self.nodeid)
00044 __repr__ = __str__
00045
00046 def __hash__(self):
00047 return self.nodeid.__hash__()
00048
00049 def get_browse_name(self):
00050 """
00051 Get browse name of a node. A browse name is a QualifiedName object
00052 composed of a string(name) and a namespace index.
00053 """
00054 result = self.get_attribute(ua.AttributeIds.BrowseName)
00055 return result.Value.Value
00056
00057 def get_display_name(self):
00058 """
00059 get description attribute of node
00060 """
00061 result = self.get_attribute(ua.AttributeIds.DisplayName)
00062 return result.Value.Value
00063
00064 def get_data_type(self):
00065 """
00066 get data type of node as NodeId
00067 """
00068 result = self.get_attribute(ua.AttributeIds.DataType)
00069 return result.Value.Value
00070
00071 def get_data_type_as_variant_type(self):
00072 """
00073 get data type of node as VariantType
00074 This only works if node is a variable, otherwise type
00075 may not be convertible to VariantType
00076 """
00077 result = self.get_attribute(ua.AttributeIds.DataType)
00078 return ua.datatype_to_varianttype(result.Value.Value)
00079
00080 def get_access_level(self):
00081 """
00082 Get the access level attribute of the node as a set of AccessLevel enum values.
00083 """
00084 result = self.get_attribute(ua.AttributeIds.AccessLevel)
00085 return ua.AccessLevel.parse_bitfield(result.Value.Value)
00086
00087 def get_user_access_level(self):
00088 """
00089 Get the user access level attribute of the node as a set of AccessLevel enum values.
00090 """
00091 result = self.get_attribute(ua.AttributeIds.UserAccessLevel)
00092 return ua.AccessLevel.parse_bitfield(result.Value.Value)
00093
00094 def get_event_notifier(self):
00095 """
00096 Get the event notifier attribute of the node as a set of EventNotifier enum values.
00097 """
00098 result = self.get_attribute(ua.AttributeIds.EventNotifier)
00099 return ua.EventNotifier.parse_bitfield(result.Value.Value)
00100
00101 def set_event_notifier(self, values):
00102 """
00103 Set the event notifier attribute.
00104
00105 :param values: an iterable of EventNotifier enum values.
00106 """
00107 event_notifier_bitfield = ua.EventNotifier.to_bitfield(values)
00108 self.set_attribute(ua.AttributeIds.EventNotifier, ua.DataValue(ua.Variant(event_notifier_bitfield, ua.VariantType.Byte)))
00109
00110 def get_node_class(self):
00111 """
00112 get node class attribute of node
00113 """
00114 result = self.get_attribute(ua.AttributeIds.NodeClass)
00115 return result.Value.Value
00116
00117 def get_description(self):
00118 """
00119 get description attribute class of node
00120 """
00121 result = self.get_attribute(ua.AttributeIds.Description)
00122 return result.Value.Value
00123
00124 def get_value(self):
00125 """
00126 Get value of a node as a python type. Only variables ( and properties) have values.
00127 An exception will be generated for other node types.
00128 """
00129 result = self.get_data_value()
00130 return result.Value.Value
00131
00132 def get_data_value(self):
00133 """
00134 Get value of a node as a DataValue object. Only variables (and properties) have values.
00135 An exception will be generated for other node types.
00136 DataValue contain a variable value as a variant as well as server and source timestamps
00137 """
00138 return self.get_attribute(ua.AttributeIds.Value)
00139
00140 def set_array_dimensions(self, value):
00141 """
00142 Set attribute ArrayDimensions of node
00143 make sure it has the correct data type
00144 """
00145 v = ua.Variant(value, ua.VariantType.UInt32)
00146 self.set_attribute(ua.AttributeIds.ArrayDimensions, ua.DataValue(v))
00147
00148 def get_array_dimensions(self):
00149 """
00150 Read and return ArrayDimensions attribute of node
00151 """
00152 res = self.get_attribute(ua.AttributeIds.ArrayDimensions)
00153 return res.Value.Value
00154
00155 def set_value_rank(self, value):
00156 """
00157 Set attribute ArrayDimensions of node
00158 """
00159 v = ua.Variant(value, ua.VariantType.Int32)
00160 self.set_attribute(ua.AttributeIds.ValueRank, ua.DataValue(v))
00161
00162 def get_value_rank(self):
00163 """
00164 Read and return ArrayDimensions attribute of node
00165 """
00166 res = self.get_attribute(ua.AttributeIds.ValueRank)
00167 return res.Value.Value
00168
00169 def set_value(self, value, varianttype=None):
00170 """
00171 Set value of a node. Only variables(properties) have values.
00172 An exception will be generated for other node types.
00173 value argument is either:
00174 * a python built-in type, converted to opc-ua
00175 optionnaly using the variantype argument.
00176 * a ua.Variant, varianttype is then ignored
00177 * a ua.DataValue, you then have full control over data send to server
00178 """
00179 datavalue = None
00180 if isinstance(value, ua.DataValue):
00181 datavalue = value
00182 elif isinstance(value, ua.Variant):
00183 datavalue = ua.DataValue(value)
00184 else:
00185 datavalue = ua.DataValue(ua.Variant(value, varianttype))
00186 self.set_attribute(ua.AttributeIds.Value, datavalue)
00187
00188 set_data_value = set_value
00189
00190 def set_writable(self, writable=True):
00191 """
00192 Set node as writable by clients.
00193 A node is always writable on server side.
00194 """
00195 if writable:
00196 self.set_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
00197 self.set_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
00198 else:
00199 self.unset_attr_bit(ua.AttributeIds.AccessLevel, ua.AccessLevel.CurrentWrite)
00200 self.unset_attr_bit(ua.AttributeIds.UserAccessLevel, ua.AccessLevel.CurrentWrite)
00201
00202 def set_attr_bit(self, attr, bit):
00203 val = self.get_attribute(attr)
00204 val.Value.Value = ua.ua_binary.set_bit(val.Value.Value, bit)
00205 self.set_attribute(attr, val)
00206
00207 def unset_attr_bit(self, attr, bit):
00208 val = self.get_attribute(attr)
00209 val.Value.Value = ua.ua_binary.unset_bit(val.Value.Value, bit)
00210 self.set_attribute(attr, val)
00211
00212 def set_read_only(self):
00213 """
00214 Set a node as read-only for clients.
00215 A node is always writable on server side.
00216 """
00217 return self.set_writable(False)
00218
00219 def set_attribute(self, attributeid, datavalue):
00220 """
00221 Set an attribute of a node
00222 attributeid is a member of ua.AttributeIds
00223 datavalue is a ua.DataValue object
00224 """
00225 attr = ua.WriteValue()
00226 attr.NodeId = self.nodeid
00227 attr.AttributeId = attributeid
00228 attr.Value = datavalue
00229 params = ua.WriteParameters()
00230 params.NodesToWrite = [attr]
00231 result = self.server.write(params)
00232 result[0].check()
00233
00234 def get_attribute(self, attr):
00235 """
00236 Read one attribute of a node
00237 result code from server is checked and an exception is raised in case of error
00238 """
00239 rv = ua.ReadValueId()
00240 rv.NodeId = self.nodeid
00241 rv.AttributeId = attr
00242 params = ua.ReadParameters()
00243 params.NodesToRead.append(rv)
00244 result = self.server.read(params)
00245 result[0].StatusCode.check()
00246 return result[0]
00247
00248 def get_attributes(self, attrs):
00249 """
00250 Read several attributes of a node
00251 list of DataValue is returned
00252 """
00253 params = ua.ReadParameters()
00254 for attr in attrs:
00255 rv = ua.ReadValueId()
00256 rv.NodeId = self.nodeid
00257 rv.AttributeId = attr
00258 params.NodesToRead.append(rv)
00259
00260 results = self.server.read(params)
00261 return results
00262
00263 def get_children(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified):
00264 """
00265 Get all children of a node. By default hierarchical references and all node classes are returned.
00266 Other reference types may be given:
00267 References = 31
00268 NonHierarchicalReferences = 32
00269 HierarchicalReferences = 33
00270 HasChild = 34
00271 Organizes = 35
00272 HasEventSource = 36
00273 HasModellingRule = 37
00274 HasEncoding = 38
00275 HasDescription = 39
00276 HasTypeDefinition = 40
00277 GeneratesEvent = 41
00278 Aggregates = 44
00279 HasSubtype = 45
00280 HasProperty = 46
00281 HasComponent = 47
00282 HasNotifier = 48
00283 HasOrderedComponent = 49
00284 """
00285 return self.get_referenced_nodes(refs, ua.BrowseDirection.Forward, nodeclassmask)
00286
00287 def get_properties(self):
00288 """
00289 return properties of node.
00290 properties are child nodes with a reference of type HasProperty and a NodeClass of Variable
00291 """
00292 return self.get_children(refs=ua.ObjectIds.HasProperty, nodeclassmask=ua.NodeClass.Variable)
00293
00294 def get_variables(self):
00295 """
00296 return variables of node.
00297 properties are child nodes with a reference of type HasComponent and a NodeClass of Variable
00298 """
00299 return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Variable)
00300
00301 def get_methods(self):
00302 """
00303 return methods of node.
00304 properties are child nodes with a reference of type HasComponent and a NodeClass of Method
00305 """
00306 return self.get_children(refs=ua.ObjectIds.HasComponent, nodeclassmask=ua.NodeClass.Method)
00307
00308 def get_children_descriptions(self, refs=ua.ObjectIds.HierarchicalReferences, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
00309 return self.get_references(refs, ua.BrowseDirection.Forward, nodeclassmask, includesubtypes)
00310
00311 def get_references(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
00312 """
00313 returns references of the node based on specific filter defined with:
00314
00315 refs = ObjectId of the Reference
00316 direction = Browse direction for references
00317 nodeclassmask = filter nodes based on specific class
00318 includesubtypes = If true subtypes of the reference (ref) are also included
00319 """
00320 desc = ua.BrowseDescription()
00321 desc.BrowseDirection = direction
00322 desc.ReferenceTypeId = ua.TwoByteNodeId(refs)
00323 desc.IncludeSubtypes = includesubtypes
00324 desc.NodeClassMask = nodeclassmask
00325 desc.ResultMask = ua.BrowseResultMask.All
00326
00327 desc.NodeId = self.nodeid
00328 params = ua.BrowseParameters()
00329 params.View.Timestamp = ua.get_win_epoch()
00330 params.NodesToBrowse.append(desc)
00331 results = self.server.browse(params)
00332 return results[0].References
00333
00334 def get_referenced_nodes(self, refs=ua.ObjectIds.References, direction=ua.BrowseDirection.Both, nodeclassmask=ua.NodeClass.Unspecified, includesubtypes=True):
00335 """
00336 returns referenced nodes based on specific filter
00337 Paramters are the same as for get_references
00338
00339 """
00340 references = self.get_references(refs, direction, nodeclassmask, includesubtypes)
00341 nodes = []
00342 for desc in references:
00343 node = Node(self.server, desc.NodeId)
00344 nodes.append(node)
00345 return nodes
00346
00347 def get_type_definition(self):
00348 """
00349 returns type definition of the node.
00350 """
00351 references = self.get_references(refs=ua.ObjectIds.HasTypeDefinition, direction=ua.BrowseDirection.Forward)
00352 if len(references) == 0:
00353 return None
00354 return references[0].NodeId
00355
00356 def get_path_as_string(self, max_length=20):
00357 """
00358 Attempt to find path of node from root node and return it as a list of strings.
00359 There might several possible paths to a node, this function will return one
00360 Some nodes may be missing references, so this method may
00361 return an empty list
00362 Since address space may have circular references, a max length is specified
00363
00364 """
00365 path = self._get_path(max_length)
00366 path = [ref.BrowseName.to_string() for ref in path]
00367 path.append(self.get_browse_name().to_string())
00368 return path
00369
00370 def get_path(self, max_length=20):
00371 """
00372 Attempt to find path of node from root node and return it as a list of Nodes.
00373 There might several possible paths to a node, this function will return one
00374 Some nodes may be missing references, so this method may
00375 return an empty list
00376 Since address space may have circular references, a max length is specified
00377
00378 """
00379 path = self._get_path(max_length)
00380 path = [Node(self.server, ref.NodeId) for ref in path]
00381 path.append(self)
00382 return path
00383
00384 def _get_path(self, max_length=20):
00385 """
00386 Attempt to find path of node from root node and return it as a list of Nodes.
00387 There might several possible paths to a node, this function will return one
00388 Some nodes may be missing references, so this method may
00389 return an empty list
00390 Since address space may have circular references, a max length is specified
00391
00392 """
00393 path = []
00394 node = self
00395 while True:
00396 refs = node.get_references(refs=ua.ObjectIds.HierarchicalReferences, direction=ua.BrowseDirection.Inverse)
00397 if len(refs) > 0:
00398 path.insert(0, refs[0])
00399 node = Node(self.server, refs[0].NodeId)
00400 if len(path) >= (max_length -1):
00401 return path
00402 else:
00403 return path
00404
00405 def get_parent(self):
00406 """
00407 returns parent of the node.
00408 A Node may have several parents, the first found is returned.
00409 This method uses reverse references, a node might be missing such a link,
00410 thus we will not find its parent.
00411 """
00412 refs = self.get_references(refs=ua.ObjectIds.HierarchicalReferences, direction=ua.BrowseDirection.Inverse)
00413 if len(refs) > 0:
00414 return Node(self.server, refs[0].NodeId)
00415 else:
00416 return None
00417
00418 def get_child(self, path):
00419 """
00420 get a child specified by its path from this node.
00421 A path might be:
00422 * a string representing a qualified name.
00423 * a qualified name
00424 * a list of string
00425 * a list of qualified names
00426 """
00427 if type(path) not in (list, tuple):
00428 path = [path]
00429 rpath = self._make_relative_path(path)
00430 bpath = ua.BrowsePath()
00431 bpath.StartingNode = self.nodeid
00432 bpath.RelativePath = rpath
00433 result = self.server.translate_browsepaths_to_nodeids([bpath])
00434 result = result[0]
00435 result.StatusCode.check()
00436
00437 return Node(self.server, result.Targets[0].TargetId)
00438
00439 def _make_relative_path(self, path):
00440 rpath = ua.RelativePath()
00441 for item in path:
00442 el = ua.RelativePathElement()
00443 el.ReferenceTypeId = ua.TwoByteNodeId(ua.ObjectIds.HierarchicalReferences)
00444 el.IsInverse = False
00445 el.IncludeSubtypes = True
00446 if isinstance(item, ua.QualifiedName):
00447 el.TargetName = item
00448 else:
00449 el.TargetName = ua.QualifiedName.from_string(item)
00450 rpath.Elements.append(el)
00451 return rpath
00452
00453 def read_raw_history(self, starttime=None, endtime=None, numvalues=0):
00454 """
00455 Read raw history of a node
00456 result code from server is checked and an exception is raised in case of error
00457 If numvalues is > 0 and number of events in period is > numvalues
00458 then result will be truncated
00459 """
00460 details = ua.ReadRawModifiedDetails()
00461 details.IsReadModified = False
00462 if starttime:
00463 details.StartTime = starttime
00464 else:
00465 details.StartTime = ua.get_win_epoch()
00466 if endtime:
00467 details.EndTime = endtime
00468 else:
00469 details.EndTime = ua.get_win_epoch()
00470 details.NumValuesPerNode = numvalues
00471 details.ReturnBounds = True
00472 result = self.history_read(details)
00473 return result.HistoryData.DataValues
00474
00475 def history_read(self, details):
00476 """
00477 Read raw history of a node, low-level function
00478 result code from server is checked and an exception is raised in case of error
00479 """
00480 valueid = ua.HistoryReadValueId()
00481 valueid.NodeId = self.nodeid
00482 valueid.IndexRange = ''
00483
00484 params = ua.HistoryReadParameters()
00485 params.HistoryReadDetails = details
00486 params.TimestampsToReturn = ua.TimestampsToReturn.Both
00487 params.ReleaseContinuationPoints = False
00488 params.NodesToRead.append(valueid)
00489 result = self.server.history_read(params)[0]
00490 return result
00491
00492 def read_event_history(self, starttime=None, endtime=None, numvalues=0, evtypes=ua.ObjectIds.BaseEventType):
00493 """
00494 Read event history of a source node
00495 result code from server is checked and an exception is raised in case of error
00496 If numvalues is > 0 and number of events in period is > numvalues
00497 then result will be truncated
00498 """
00499
00500 details = ua.ReadEventDetails()
00501 if starttime:
00502 details.StartTime = starttime
00503 else:
00504 details.StartTime = ua.get_win_epoch()
00505 if endtime:
00506 details.EndTime = endtime
00507 else:
00508 details.EndTime = ua.get_win_epoch()
00509 details.NumValuesPerNode = numvalues
00510
00511 if not isinstance(evtypes, (list, tuple)):
00512 evtypes = [evtypes]
00513
00514 evtypes = [Node(self.server, evtype) for evtype in evtypes]
00515
00516 evfilter = events.get_filter_from_event_type(evtypes)
00517 details.Filter = evfilter
00518
00519 result = self.history_read_events(details)
00520 event_res = []
00521 for res in result.HistoryData.Events:
00522 event_res.append(events.Event.from_event_fields(evfilter.SelectClauses, res.EventFields))
00523 return event_res
00524
00525 def history_read_events(self, details):
00526 """
00527 Read event history of a node, low-level function
00528 result code from server is checked and an exception is raised in case of error
00529 """
00530 valueid = ua.HistoryReadValueId()
00531 valueid.NodeId = self.nodeid
00532 valueid.IndexRange = ''
00533
00534 params = ua.HistoryReadParameters()
00535 params.HistoryReadDetails = details
00536 params.TimestampsToReturn = ua.TimestampsToReturn.Both
00537 params.ReleaseContinuationPoints = False
00538 params.NodesToRead.append(valueid)
00539 result = self.server.history_read(params)[0]
00540 return result
00541
00542 def delete(self, delete_references=True):
00543 """
00544 Delete node from address space
00545 """
00546 ditem = ua.DeleteNodesItem()
00547 ditem.NodeId = self.nodeid
00548 ditem.DeleteTargetReferences = delete_references
00549 params = ua.DeleteNodesParameters()
00550 params.NodesToDelete = [ditem]
00551 result = self.server.delete_nodes(params)
00552 result[0].check()
00553
00554 def add_folder(self, nodeid, bname):
00555 return opcua.common.manage_nodes.create_folder(self, nodeid, bname)
00556
00557 def add_object(self, nodeid, bname, objecttype=None):
00558 return opcua.common.manage_nodes.create_object(self, nodeid, bname, objecttype)
00559
00560 def add_variable(self, nodeid, bname, val, varianttype=None, datatype=None):
00561 return opcua.common.manage_nodes.create_variable(self, nodeid, bname, val, varianttype, datatype)
00562
00563 def add_object_type(self, nodeid, bname):
00564 return opcua.common.manage_nodes.create_object_type(self, nodeid, bname)
00565
00566 def add_variable_type(self, nodeid, bname, datatype):
00567 return opcua.common.manage_nodes.create_variable_type(self, nodeid, bname, datatype)
00568
00569 def add_data_type(self, nodeid, bname, description=None):
00570 return opcua.common.manage_nodes.create_data_type(self, nodeid, bname, description=None)
00571
00572 def add_property(self, nodeid, bname, val, varianttype=None, datatype=None):
00573 return opcua.common.manage_nodes.create_property(self, nodeid, bname, val, varianttype, datatype)
00574
00575 def add_method(self, *args):
00576 return opcua.common.manage_nodes.create_method(self, *args)
00577
00578 def add_reference_type(self, parent, nodeid, bname):
00579 return opcua.common.manage_nodes.create_reference_type(parent, nodeid, bname)
00580
00581 def call_method(self, methodid, *args):
00582 return opcua.common.methods.call_method(self, methodid, *args)