Skip to content

Commit 283c3cb

Browse files
committed
add new generator for structures
1 parent d7ff6ce commit 283c3cb

2 files changed

Lines changed: 201 additions & 0 deletions

File tree

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
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+
import sys
8+
from lxml import etree
9+
from lxml import objectify
10+
11+
from IPython import embed
12+
13+
14+
from opcua.ua.ua_binary import Primitives1, Primitives
15+
#from opcua.common.ua_utils import get_default_value
16+
17+
18+
def get_default_value(uatype):
19+
if uatype in ("String"):
20+
return None
21+
elif uatype in ("ByteString", "CharArray", "Char"):
22+
return None
23+
elif uatype in ("Boolean"):
24+
return "True"
25+
elif uatype in ("DateTime"):
26+
return "datetime.utcnow()"
27+
elif uatype in ("Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Double", "Float", "Byte", "SByte"):
28+
return 0
29+
elif uatype in ("ExtensionObject"):
30+
return "None"
31+
else:
32+
return "ua." + uatype + "()"
33+
34+
35+
class Struct(object):
36+
def __init__(self, name):
37+
self.name = name
38+
self.fields = []
39+
self.code = ""
40+
41+
def get_code(self):
42+
self._make_constructor()
43+
self._make_from_binary()
44+
self._make_to_binary()
45+
return self.code
46+
47+
def _make_constructor(self):
48+
self.code = """
49+
50+
class {0}(object):
51+
'''
52+
{0} autogenerated from xml
53+
'''
54+
def __init__(self):
55+
""".format(self.name)
56+
for field in self.fields:
57+
self.code += " self.{} = {}\n".format(field.name, field.value)
58+
59+
def _make_from_binary(self):
60+
self.code += '\n def from_binary(self, data):\n'
61+
for field in self.fields:
62+
if hasattr(Primitives, field.uatype):
63+
if field.array:
64+
self.code += ' self.{} = ua.ua_binary.Primitives.{}.unpack_array(data)\n'.format(field.name, field.uatype)
65+
else:
66+
self.code += ' self.{} = ua.ua_binary.Primitives.{}.unpack(data)\n'.format(field.name, field.uatype)
67+
else:
68+
if field.array:
69+
self.code += '''
70+
length = ua.ua_binary.Primitives.Int32.unpack(data)
71+
if length != -1:
72+
self.{0} = [ua.{1}.from_binary(data) for _ in range(length)]
73+
'''.format(field.name, field.uatype)
74+
else:
75+
self.code += " self.{} = ua.{}.from_binary(data)\n".format(field.name, field.uatype)
76+
77+
def _make_to_binary(self):
78+
self.code += '''
79+
def to_binary(self):
80+
packet = []
81+
'''
82+
for field in self.fields:
83+
if hasattr(Primitives, field.uatype):
84+
if field.array:
85+
self.code += ' packet.append(ua.ua_binary.Primitives.{}.pack_array(self.{}))\n'.format(field.uatype, field.name)
86+
else:
87+
self.code += ' packet.append(ua.ua_binary.Primitives.{}.pack(self.{}))\n'.format(field.uatype, field.name)
88+
else:
89+
if field.array:
90+
self.code += '''
91+
if self.{0} is None:
92+
packet.append(ua.ua_binary.Primitives.Int32.pack(-1))
93+
else:
94+
packet.append(ua.ua_binary.Primitives.Int32.pack(len(self.{0})))
95+
for element in self.{0}:
96+
packet.append(element.to_binary())
97+
'''.format(field.name, field.uatype)
98+
else:
99+
self.code += " packet.append(ua.{}.to_binary())\n".format(field.name)
100+
self.code += ' return b"".join(packet)'
101+
102+
103+
104+
105+
106+
class Field(object):
107+
def __init__(self, name):
108+
self.name = name
109+
self.uatype = None
110+
self.value = None
111+
self.array = False
112+
113+
114+
class CodeGenerator(object):
115+
def __init__(self, path, output):
116+
self.path = path
117+
self.output = output
118+
self.model = []
119+
120+
def generate(self):
121+
parser = etree.XMLParser(remove_blank_text=True, ns_clean=True)
122+
root = etree.parse(self.path, parser)
123+
embed()
124+
for child in root.iter():
125+
embed()
126+
127+
def _make_model(self):
128+
obj = objectify.parse(self.path)
129+
root = obj.getroot()
130+
for child in root.iter("{*}StructuredType"):
131+
struct = Struct(child.get("Name"))
132+
array = False
133+
for xmlfield in child.iter("{*}Field"):
134+
name = xmlfield.get("Name")
135+
if name.startswith("NoOf"):
136+
array = True
137+
continue
138+
field = Field(name)
139+
uatype = xmlfield.get("TypeName")
140+
if uatype.startswith("opc"):
141+
field.uatype = uatype[4:]
142+
else:
143+
field.uatype = uatype[3:]
144+
field.value = get_default_value(field.uatype)
145+
if array:
146+
field.array = True
147+
field.value = []
148+
array = False
149+
struct.fields.append(field)
150+
self.model.append(struct)
151+
152+
def run(self):
153+
self._make_model()
154+
self._file = open(self.output, "wt")
155+
self._make_header()
156+
for struct in self.model:
157+
self._file.write(struct.get_code())
158+
self._file.close()
159+
160+
def _make_header(self):
161+
self._file.write("""
162+
'''
163+
THIS FILE IS AUTOGENERATED, DO NOT EDIT!!!
164+
'''
165+
166+
from datetime import datetime
167+
168+
from opcua import ua
169+
170+
171+
""")
172+
173+
174+
175+
176+
if __name__ == "__main__":
177+
178+
xmlpath = "/home/olivier/python-opcua/schemas/example.bsd"
179+
#p = gm.Parser(xmlpath)
180+
#model = p.parse()
181+
#gm.add_basetype_members(model)
182+
#gm.add_encoding_field(model)
183+
#gm.remove_duplicates(model)
184+
#gm.remove_vector_length(model)
185+
#gm.split_requests(model)
186+
#gm.fix_names(model)
187+
c = CodeGenerator(xmlpath, "structures.py")
188+
#c.generate()
189+
c.run()
190+
#embed()

opcua/common/ua_utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,3 +258,14 @@ def get_nodes_of_namespace(server, namespaces=None):
258258
nodes = [server.get_node(nodeid) for nodeid in server.iserver.aspace.keys()
259259
if nodeid.NamespaceIndex != 0 and nodeid.NamespaceIndex in namespace_indexes]
260260
return nodes
261+
262+
263+
def get_default_value(uatype):
264+
if isinstance(uatype, ua.VariantType):
265+
return ua.get_default_values(uatype)
266+
elif hasattr(ua.VariantType, uatype):
267+
return ua.get_default_value(getattr(ua.VariantType, uatype))
268+
else:
269+
return getattr(ua, uatype)()
270+
271+

0 commit comments

Comments
 (0)