00001 """
00002 Usefull method and classes not belonging anywhere and depending on opcua library
00003 """
00004
00005 from dateutil import parser
00006 from datetime import datetime
00007 from enum import Enum, IntEnum
00008 import uuid
00009
00010 from opcua import ua
00011 from opcua.ua.uaerrors import UaError
00012
00013
00014 def val_to_string(val):
00015 """
00016 convert a python object or python-opcua object to a string
00017 which should be easy to understand for human
00018 easy to modify, and not too hard to parse back ....not easy
00019 meant for UI or command lines
00020
00021 """
00022 if isinstance(val, (list, tuple)):
00023 res = []
00024 for v in val:
00025 res.append(val_to_string(v))
00026 return "[" + ", ".join(res) + "]"
00027
00028 if hasattr(val, "to_string"):
00029 val = val.to_string()
00030 elif isinstance(val, ua.StatusCode):
00031 val = val.name
00032 elif isinstance(val, (Enum, IntEnum)):
00033 val = val.name
00034 elif isinstance(val, ua.DataValue):
00035 val = variant_to_string(val.Value)
00036 elif isinstance(val, str):
00037 pass
00038 elif isinstance(val, bytes):
00039 val = str(val)
00040 elif isinstance(val, datetime):
00041 val = val.isoformat()
00042 elif isinstance(val, (int, float)):
00043 val = str(val)
00044 else:
00045
00046 val = str(val)
00047 return val
00048
00049
00050 def variant_to_string(var):
00051 """
00052 convert a variant to a string which should be easy to understand for human
00053 easy to modify, and not too hard to parse back ....not easy
00054 meant for UI or command lines
00055 """
00056 return val_to_string(var.Value)
00057
00058
00059 def string_to_val(string, vtype):
00060 """
00061 Convert back a string to a python or python-opcua object
00062 Note: no error checking is done here, supplying null strings could raise exceptions (datetime and guid)
00063 """
00064 string = string.strip()
00065 if string.startswith("["):
00066 string = string[1:-1]
00067 var = []
00068 for s in string.split(","):
00069 s = s.strip()
00070 val = string_to_val(s, vtype)
00071 var.append(val)
00072 return var
00073
00074 if vtype == ua.VariantType.Null:
00075 val = None
00076 elif vtype == ua.VariantType.Boolean:
00077 if string in ("True", "true", "on", "On", "1"):
00078 val = True
00079 else:
00080 val = False
00081 elif vtype in (ua.VariantType.Int16, ua.VariantType.Int32, ua.VariantType.Int64):
00082 val = int(string)
00083 elif vtype in (ua.VariantType.UInt16, ua.VariantType.UInt32, ua.VariantType.UInt64):
00084 val = int(string)
00085 elif vtype in (ua.VariantType.Float, ua.VariantType.Double):
00086 val = float(string)
00087 elif vtype in (ua.VariantType.String, ua.VariantType.XmlElement):
00088 val = string
00089 elif vtype in (ua.VariantType.SByte, ua.VariantType.ByteString):
00090 val = string.encode("utf-8")
00091 elif vtype in (ua.VariantType.NodeId, ua.VariantType.ExpandedNodeId):
00092 val = ua.NodeId.from_string(string)
00093 elif vtype == ua.VariantType.QualifiedName:
00094 val = ua.QualifiedName.from_string(string)
00095 elif vtype == ua.VariantType.DateTime:
00096 val = parser.parse(string)
00097 elif vtype == ua.VariantType.LocalizedText:
00098 val = ua.LocalizedText(string)
00099 elif vtype == ua.VariantType.StatusCode:
00100 val = ua.StatusCode(string)
00101 elif vtype == ua.VariantType.Guid:
00102 val = uuid.UUID(string)
00103 else:
00104
00105 raise NotImplementedError
00106 return val
00107
00108
00109 def string_to_variant(string, vtype):
00110 """
00111 convert back a string to an ua.Variant
00112 """
00113 return ua.Variant(string_to_val(string, vtype), vtype)
00114
00115
00116 def get_node_children(node, nodes=None):
00117 """
00118 Get recursively all children of a node
00119 """
00120 if nodes is None:
00121 nodes = [node]
00122 for child in node.get_children():
00123 nodes.append(child)
00124 get_node_children(child, nodes)
00125 return nodes
00126
00127
00128 def get_node_subtypes(node, nodes=None):
00129 if nodes is None:
00130 nodes = [node]
00131 for child in node.get_children(refs=ua.ObjectIds.HasSubtype):
00132 nodes.append(child)
00133 get_node_subtypes(child, nodes)
00134 return nodes
00135
00136
00137 def get_node_supertypes(node, includeitself=False, skipbase=True):
00138 """
00139 return get all subtype parents of node recursive
00140 :param node: can be a ua.Node or ua.NodeId
00141 :param includeitself: include also node to the list
00142 :param skipbase don't include the toplevel one
00143 :returns list of ua.Node, top parent first
00144 """
00145 parents = []
00146 if includeitself:
00147 parents.append(node)
00148 parents.extend(_get_node_supertypes(node))
00149 if skipbase and len(parents) > 1:
00150 parents = parents[:-1]
00151
00152 return parents
00153
00154
00155 def _get_node_supertypes(node):
00156 """
00157 recursive implementation of get_node_derived_from_types
00158 """
00159 basetypes = []
00160 parent = get_node_supertype(node)
00161 if parent:
00162 basetypes.append(parent)
00163 basetypes.extend(_get_node_supertypes(parent))
00164
00165 return basetypes
00166
00167
00168 def get_node_supertype(node):
00169 """
00170 return node supertype or None
00171 """
00172 supertypes = node.get_referenced_nodes(refs=ua.ObjectIds.HasSubtype,
00173 direction=ua.BrowseDirection.Inverse,
00174 includesubtypes=True)
00175 if supertypes:
00176 return supertypes[0]
00177 else:
00178 return None
00179
00180
00181 def is_child_present(node, browsename):
00182 """
00183 return if a browsename is present a child from the provide node
00184 :param node: node wherein to find the browsename
00185 :param browsename: browsename to search
00186 :returns returne True if the browsename is present else False
00187 """
00188 child_descs = node.get_children_descriptions()
00189 for child_desc in child_descs:
00190 if child_desc.BrowseName == browsename:
00191 return True
00192
00193 return False
00194
00195
00196 def data_type_to_variant_type(dtype_node):
00197 """
00198 Given a Node datatype, find out the variant type to encode
00199 data. This is not exactly straightforward...
00200 """
00201 base = get_base_data_type(dtype_node)
00202
00203 if base.nodeid.Identifier != 29:
00204 return ua.VariantType(base.nodeid.Identifier)
00205 else:
00206
00207 descs = dtype_node.get_children_descriptions()
00208 bnames = [d.BrowseName.Name for d in descs]
00209 if "EnumStrings" in bnames:
00210 return ua.VariantType.LocalizedText
00211 elif "EnumValues" in bnames:
00212 return ua.VariantType.ExtensionObject
00213 else:
00214 raise ua.UaError("Enumeration must have a child node describing its type and values")
00215
00216
00217 def get_base_data_type(datatype):
00218 """
00219 Looks up the base datatype of the provided datatype Node
00220 The base datatype is either:
00221 A primitive type (ns=0, i<=21) or a complex one (ns=0 i>21 and i<=30) like Enum and Struct.
00222
00223 Args:
00224 datatype: NodeId of a datype of a variable
00225 Returns:
00226 NodeId of datatype base or None in case base datype can not be determined
00227 """
00228 base = datatype
00229 while base:
00230 if base.nodeid.NamespaceIndex == 0 and isinstance(base.nodeid.Identifier, int) and base.nodeid.Identifier <= 30:
00231 return base
00232 base = get_node_supertype(base)
00233 raise ua.UaError("Datatype must be a subtype of builtin types {0!s}".format(datatype))
00234
00235
00236 def get_nodes_of_namespace(server, namespaces=None):
00237 """
00238 Get the nodes of one or more namespaces .
00239 Args:
00240 server: opc ua server to use
00241 namespaces: list of string uri or int indexes of the namespace to export
00242 Returns:
00243 List of nodes that are part of the provided namespaces
00244 """
00245 if namespaces is None:
00246 namespaces = []
00247 ns_available = server.get_namespace_array()
00248
00249 if not namespaces:
00250 namespaces = ns_available[1:]
00251 elif isinstance(namespaces, (str, int)):
00252 namespaces = [namespaces]
00253
00254
00255 namespace_indexes = [n if isinstance(n, int) else ns_available.index(n) for n in namespaces]
00256
00257
00258 nodes = [server.get_node(nodeid) for nodeid in server.iserver.aspace.keys()
00259 if nodeid.NamespaceIndex != 0 and nodeid.NamespaceIndex in namespace_indexes]
00260 return nodes