Completed
Pull Request — master (#283)
by Olivier
05:03
created

get_node_children()   A

Complexity

Conditions 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 3
c 1
b 1
f 0
dl 0
loc 10
rs 9.4285
1
"""
2
Usefull method and classes not belonging anywhere and depending on opcua library
3
"""
4
5
from dateutil import parser
6
from datetime import datetime
7
from enum import Enum, IntEnum
8
9
from opcua import ua
10
from opcua.common.uaerrors import UaError
11
12
13
def val_to_string(val):
14
    """
15
    convert a python object or python-opcua object to a string
16
    which should be easy to understand for human
17
    easy to modify, and not too hard to parse back ....not easy
18
    meant for UI or command lines
19
20
    """
21
    if isinstance(val, (list, tuple)):
22
        res = []
23
        for v in val:
24
            res.append(val_to_string(v))
25
        return "[" + ", ".join(res) + "]"
26
27
    if hasattr(val, "to_string"):
28
        val = val.to_string()
29
    elif isinstance(val, ua.StatusCode):
30
        val = val.name
31
    elif isinstance(val, (Enum, IntEnum)):
32
        val = val.name
33
    elif isinstance(val, ua.DataValue):
34
        val = variant_to_string(val.Value)
35
    elif isinstance(val, str):
36
        pass
37
    elif isinstance(val, bytes):
38
        val = str(val)
39
    elif isinstance(val, datetime):
40
        val = val.isoformat()
41
    elif isinstance(val, (int, float)):
42
        val = str(val)
43
    else:
44
        # FIXME: Some types are probably missing!
45
        val = str(val)
46
    return val
47
48
49
def variant_to_string(var):
50
    """
51
    convert a variant to a string which should be easy to understand for human
52
    easy to modify, and not too hard to parse back ....not easy
53
    meant for UI or command lines
54
    """
55
    return val_to_string(var.Value)
56
57
58
def string_to_val(string, vtype):
59
    """
60
    Convert back a string to a python or python-opcua object 
61
    """
62
    string = string.strip()
63
    if string.startswith("["):
64
        string = string[1:-1]
65
        var = []
66
        for s in string.split(","):
67
            s = s.strip()
68
            val = string_to_val(s, vtype)
69
            var.append(val)
70
        return var
71
72
    if vtype == ua.VariantType.Null:
73
        val = None
74
    elif vtype == ua.VariantType.Boolean:
75
        val = bool(string)
76
    elif 4 <= vtype.value < 9:
77
        val = int(string)
78
    elif vtype in (ua.VariantType.Float, ua.VariantType.Double):
79
        val = float(string)
80
    elif vtype in (ua.VariantType.String, ua.VariantType.XmlElement):
81
        val = string
82
    elif vtype in (ua.VariantType.SByte, ua.VariantType.Guid, ua.VariantType.ByteString):
83
        val = bytes(string)
84
    elif vtype in (ua.VariantType.NodeId, ua.VariantType.ExpandedNodeId):
85
        val = ua.NodeId.from_string(string)
86
    elif vtype == ua.VariantType.QualifiedName:
87
        val = ua.QualifiedName.from_string(string)
88
    elif vtype == ua.VariantType.DateTime:
89
        val = parser.parse(string)
90
    elif vtype == ua.VariantType.LocalizedText:
91
        val = ua.LocalizedText(string)
92
    elif vtype == ua.VariantType.StatusCode:
93
        val = ua.StatusCode(string)
94
    else:
95
        # FIXME: Some types are probably missing!
96
        raise NotImplementedError
97
    return val
98
99
100
def string_to_variant(string, vtype):
101
    """
102
    convert back a string to an ua.Variant
103
    """
104
    return ua.Variant(string_to_val(string, vtype), vtype)
105
106
107
def get_node_children(node, nodes=None):
108
    """
109
    Get recursively all children of a node
110
    """
111
    if nodes is None:
112
        nodes = [node]
113
    for child in node.get_children():
114
        nodes.append(child)
115
        get_node_children(child, nodes)
116
    return nodes
117
118
119
def get_node_subtypes(node, nodes=None):
120
    if nodes is None:
121
        nodes = [node]
122
    for child in node.get_children(refs=ua.ObjectIds.HasSubtype):
123
        nodes.append(child)
124
        get_node_subtypes(child, nodes)
125
    return nodes
126
127
128
def get_node_supertypes(node, includeitself = False, skipbase = True):
129
    """
130
    return get all subtype parents of node recursive
131
    :param server: used in case node is nodeid         
132
    :param node: can be a ua.Node or ua.NodeId
133
    :param includeitself: include also node to the list
134
    :param skipbase don't include the toplevel one
135
    :returns list of ua.Node, top parent first 
136
    """
137
    parents =[]      
138
    if includeitself:
139
        parents.append(node)
140
    parents.extend(_get_node_supertypes(node))
141
    if skipbase and len(parents) > 1:
142
        parents = parents [:-1]
143
        
144
    return parents
145
146
147
def _get_node_supertypes(node):
148
    """
149
    recursive implementation of get_node_derived_from_types
150
    """
151
    basetypes = []
152
    parents = node.get_referenced_nodes(refs=ua.ObjectIds.HasSubtype, direction=ua.BrowseDirection.Inverse, includesubtypes=True)
153
    if len(parents) != 0:  
154
        #TODO: Is it possible to have multiple subtypes ? If so extended support for it
155
       basetypes.append(parents[0])  
156
       basetypes.extend( _get_node_supertypes(parents[0]) )
157
       
158
    return basetypes
159
160
def is_child_present(node, browsename):
161
    """
162
    return if a browsename is present a child from the provide node
163
    :param node: node wherein to find the browsename
164
    :param browsename: browsename to search
165
    :returns returne True if the browsename is present else False 
166
    """
167
    child_descs = node.get_children_descriptions()
168
    for child_desc in child_descs:
169
        if child_desc.BrowseName == browsename:
170
            return True
171
172
    return False
173