Completed
Pull Request — master (#388)
by Olivier
05:13
created

Field.__init__()   A

Complexity

Conditions 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 2
Metric Value
cc 1
c 2
b 0
f 2
dl 0
loc 5
ccs 5
cts 5
cp 1
crap 1
rs 9.4285
1
"""
2
parse simple structures from an xml tree
3
We only support a subset of features but should be enough
4
for custom structures
5
"""
6
7 1
from lxml import etree
8 1
from lxml import objectify
9
10
11
12 1
from opcua.ua.ua_binary import Primitives
13
14
15 1
def get_default_value(uatype):
16 1
    if uatype in ("String"):
17 1
        return "None" 
18 1
    elif uatype == "Guid":
19 1
        return "uuid.uuid4()" 
20 1
    elif uatype in ("ByteString", "CharArray", "Char"):
21 1
        return None 
22 1
    elif uatype in ("Boolean"):
23 1
        return "True"
24 1
    elif uatype in ("DateTime"):
25 1
        return "datetime.utcnow()"
26 1
    elif uatype in ("Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Double", "Float", "Byte", "SByte"):
27 1
        return 0
28
    else:
29 1
        return "ua." + uatype + "()"
30
31
32 1
class Struct(object):
33 1
    def __init__(self, name):
34 1
        self.name = name
35 1
        self.fields = []
36 1
        self.code = ""
37
38 1
    def get_code(self):
39 1
        self._make_constructor()
40 1
        self._make_from_binary()
41 1
        self._make_to_binary()
42 1
        return self.code
43
44 1
    def _make_constructor(self):
45 1
        self.code = """
46
47
class {0}(object):
48
    '''
49
    {0} autogenerated from xml
50
    '''
51
    def __init__(self, data=None):
52
        if data is not None:
53
            self._binary_init(data)
54
            return
55
""".format(self.name)
56 1
        for field in self.fields:
57 1
            self.code += "        self.{} = {}\n".format(field.name, field.value)
58
59 1
    def _make_from_binary(self):
60 1
        self.code += '''
61
    @staticmethod 
62
    def from_binary(data):
63
        return {}(data=data)
64
65
    def _binary_init(self, data):
66
'''.format(self.name)
67 1
        for field in self.fields:
68 1
            if hasattr(Primitives, field.uatype):
69 1
                if field.array:
70 1
                    self.code += '        self.{} = ua.ua_binary.Primitives.{}.unpack_array(data)\n'.format(field.name, field.uatype)
71
                else:
72 1
                    self.code += '        self.{} = ua.ua_binary.Primitives.{}.unpack(data)\n'.format(field.name, field.uatype)
73
            else:
74 1
                if field.array:
75 1
                    self.code += '''
76
        length = ua.ua_binary.Primitives.Int32.unpack(data)
77
        if length != -1:
78
            self.{0} = [ua.{1}.from_binary(data) for _ in range(length)]
79
'''.format(field.name, field.uatype)
80
                else:
81 1
                    self.code += "        self.{} = ua.{}.from_binary(data)\n".format(field.name, field.uatype)
82
83 1
    def _make_to_binary(self):
84 1
        self.code += '''
85
    def to_binary(self):
86
        packet = []
87
'''
88 1
        for field in self.fields:
89 1
            if hasattr(Primitives, field.uatype):
90 1
                if field.array:
91 1
                    self.code += '        packet.append(ua.ua_binary.Primitives.{}.pack_array(self.{}))\n'.format(field.uatype, field.name)
92
                else:
93 1
                    self.code += '        packet.append(ua.ua_binary.Primitives.{}.pack(self.{}))\n'.format(field.uatype, field.name)
94
            else:
95 1
                if field.array:
96 1
                    self.code += '''
97
        if self.{0} is None:
98
            packet.append(ua.ua_binary.Primitives.Int32.pack(-1))
99
        else:
100
            packet.append(ua.ua_binary.Primitives.Int32.pack(len(self.{0})))
101
            for element in self.{0}:
102
                packet.append(element.to_binary())
103
'''.format(field.name, field.uatype)
104
                else:
105 1
                    self.code += "        packet.append(self.{}.to_binary())\n".format(field.name)
106 1
        self.code += '        return b"".join(packet)'
107
108
109 1
class Field(object):
110 1
    def __init__(self, name):
111 1
        self.name = name
112 1
        self.uatype = None
113 1
        self.value = None
114 1
        self.array = False
115
116
117 1
class StructGenerator(object):
118 1
    def __init__(self, path, output):
119 1
        self.path = path
120 1
        self.output = output
121 1
        self.model = []
122
123 1
    def _make_model(self):
124 1
        obj = objectify.parse(self.path)
125 1
        root = obj.getroot()
126 1
        for child in root.iter("{*}StructuredType"):
127 1
            struct = Struct(child.get("Name"))
128 1
            array = False
129 1
            for xmlfield in child.iter("{*}Field"):
130 1
                name = xmlfield.get("Name")
131 1
                if name.startswith("NoOf"):
132 1
                    array = True
133 1
                    continue
134 1
                field = Field(name)
135 1
                uatype = xmlfield.get("TypeName")
136 1
                if uatype.startswith("opc"):
137 1
                    field.uatype = uatype[4:]
138
                else:
139 1
                    field.uatype = uatype[3:]
140 1
                field.value = get_default_value(field.uatype)
141 1
                if array:
142 1
                    field.array = True
143 1
                    field.value = []
144 1
                    array = False
145 1
                struct.fields.append(field)
146 1
            self.model.append(struct)
147
148 1
    def run(self):
149 1
        self._make_model()
150 1
        self._file = open(self.output, "wt")
151 1
        self._make_header()
152 1
        for struct in self.model:
153 1
            self._file.write(struct.get_code())
154 1
        self._file.close()
155
156 1
    def get_structures(self):
157
        self._make_model()
158
        ld = {}
159
        for struct in self.model:
160
            exec(struct.get_code(), ld)
161
        return ld
162
163
164
165 1
    def _make_header(self):
166 1
        self._file.write("""
167
'''
168
THIS FILE IS AUTOGENERATED, DO NOT EDIT!!!
169
'''
170
171
from datetime import datetime
172
import uuid
173
174
from opcua import ua
175
176
177
""")
178
179
180
181
182 1
if __name__ == "__main__":
183
    import sys
184
    sys.path.insert(0, ".") # necessary for import in current dir
185
186
    xmlpath = "/home/olivier/python-opcua/schemas/example.bsd"
187
    c = StructGenerator(xmlpath, "structures.py")
188
    c.run()
189
    import structures as s
190
191
192
    #sts = c.get_structures()
193
    embed()
194