generate_model.py
Go to the documentation of this file.
1 """
2 Generate address space c++ code from xml file specification
3 """
4 import sys
5 from copy import copy
6 
7 import xml.etree.ElementTree as ET
8 
9 # from IPython import embed
10 
11 NeedOverride = []
12 NeedConstructor = []#["RelativePathElement", "ReadValueId", "OpenSecureChannelParameters", "UserIdentityToken", "RequestHeader", "ResponseHeader", "ReadParameters", "UserIdentityToken", "BrowseDescription", "ReferenceDescription", "CreateSubscriptionParameters", "PublishResult", "NotificationMessage", "SetPublishingModeParameters"]
13 IgnoredEnums = []#["IdType", "NodeIdType"]
14 #we want to implement som struct by hand, to make better interface or simply because they are too complicated
15 IgnoredStructs = []#["NodeId", "ExpandedNodeId", "Variant", "QualifiedName", "DataValue", "LocalizedText"]#, "ExtensionObject"]
16 #by default we split requests and respons in header and parameters, but some are so simple we do not split them
17 NoSplitStruct = ["GetEndpointsResponse", "CloseSessionRequest", "AddNodesResponse", "DeleteNodesResponse", "BrowseResponse", "HistoryReadResponse", "HistoryUpdateResponse", "RegisterServerResponse", "CloseSecureChannelRequest", "CloseSecureChannelResponse", "CloseSessionRequest", "CloseSessionResponse", "UnregisterNodesResponse", "MonitoredItemModifyRequest", "MonitoredItemsCreateRequest", "ReadResponse", "WriteResponse", "TranslateBrowsePathsToNodeIdsResponse", "DeleteSubscriptionsResponse", "DeleteMonitoredItemsResponse", "CreateMonitoredItemsResponse", "ServiceFault", "AddReferencesRequest", "AddReferencesResponse", "ModifyMonitoredItemsResponse", "RepublishResponse", "CallResponse", "FindServersResponse", "RegisterServerRequest", "RegisterServer2Response"]
18 #structs that end with Request or Response but are not
19 NotRequest = ["MonitoredItemCreateRequest", "MonitoredItemModifyRequest", "CallMethodRequest"]
20 OverrideTypes = {}#AttributeId": "AttributeID", "ResultMask": "BrowseResultMask", "NodeClassMask": "NodeClass", "AccessLevel": "VariableAccessLevel", "UserAccessLevel": "VariableAccessLevel", "NotificationData": "NotificationData"}
21 OverrideNames = {}#{"RequestHeader": "Header", "ResponseHeader": "Header", "StatusCode": "Status", "NodesToRead": "AttributesToRead"} # "MonitoringMode": "Mode",, "NotificationMessage": "Notification", "NodeIdType": "Type"}
22 
23 #some object are defined in extensionobjects in spec but seems not to be in reality
24 #in addition to this list all request and response and descriptions will not inherit
25 #NoInherit = ["RequestHeader", "ResponseHeader", "ChannelSecurityToken", "UserTokenPolicy", "SignatureData", "BrowseResult", "ReadValueId", "WriteValue", "BrowsePath", "BrowsePathTarget", "RelativePath", "RelativePathElement", "BrowsePathResult"]#, "ApplicationDescription", "EndpointDescription"
26 
27 
28 class Bit(object):
29  def __init__(self):
30  self.name = None
31  self.idx = None
32  self.container = None
33  self.length = 1
34 
35  def __str__(self):
36  return "(Bit: {0}, container:{1}, idx:{2})".format(self.name, self.container, self.idx)
37  __repr__ = __str__
38 
39 class Struct(object):
40  def __init__(self):
41  self.name = None
42  self.basetype = None
43  self.doc = ""
44  self.fields = []
45  self.bits = {}
46  self.needconstructor = None
47  self.needoverride = False
48  self.children = []
49  self.parents = []
50  self.extensionobject = False #used for struct which are not pure extension objects
51 
52  def get_field(self, name):
53  for f in self.fields:
54  if f.name == name:
55  return f
56  raise Exception("field not found: " + name)
57 
58  def __str__(self):
59  return "Struct {0}:{1}".format(self.name, self.basetype)
60 
61  __repr__ = __str__
62 
63 
64 class Field(object):
65  def __init__(self):
66  self.name = None
67  self.uatype = None
68  self.length = None
69  self.sourcetype = None
70  self.switchfield = None
71  self.switchvalue = None
72  self.bitlength = 1
73 
74  def __str__(self):
75  return "Field {0}({1})".format(self.name, self.uatype)
76 
77  __repr__ = __str__
78 
79  def is_native_type(self):
80  if self.uatype in ("Char", "SByte", "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "Boolean", "Double", "Float", "Byte", "String", "CharArray", "ByteString", "DateTime"):
81  return True
82  return False
83 
84  def get_ctype(self):
85  if self.uatype == "String":
86  ty = "std::string"
87  elif self.uatype == "CharArray":
88  ty = "std::string"
89  elif self.uatype == "Char":
90  ty = "char"
91  elif self.uatype == "SByte":
92  ty = "char"
93  elif self.uatype == "Int8":
94  ty = "int8_t"
95  elif self.uatype == "Int16":
96  ty = "int16_t"
97  elif self.uatype == "Int32":
98  ty = "int32_t"
99  elif self.uatype == "Int64":
100  ty = "int64_t"
101  elif self.uatype == "UInt8":
102  ty = "uint8_t"
103  elif self.uatype == "UInt16":
104  ty = "uint16_t"
105  elif self.uatype == "UInt32":
106  ty = "uint32_t"
107  elif self.uatype == "UInt64":
108  ty = "uint64_t"
109  elif self.uatype == "DateTime":
110  ty = "OpcUa::DateTime"
111  elif self.uatype == "Boolean":
112  ty = "bool"
113  elif self.uatype == "Double":
114  ty = "double"
115  elif self.uatype == "Float":
116  ty = "float"
117  elif self.uatype == "ByteString":
118  ty = "OpcUa::ByteString"
119  elif self.uatype == "Byte":
120  ty = "uint8_t"
121  else:
122  ty = "OpcUa::" + self.uatype
123  if self.length:
124  ty = "std::vector<{0}>".format(ty)
125  return ty
126 
127 class Enum(object):
128  def __init__(self):
129  self.name = None
130  self.uatype = None
131  self.values = []
132  self.doc = ""
133 
134  def get_ctype(self):
135  return "uint{0}_t".format(self.uatype)
136 
137 class EnumValue(object):
138  def __init__(self):
139  self.name = None
140  self.value = None
141 
142 class Model(object):
143  def __init__(self):
144  self.structs = []
145  self.enums = []
146  self.struct_list = []
147  self.enum_list = []
148 
149  def get_struct(self, name):
150  for struct in self.structs:
151  if name == struct.name:
152  return struct
153  raise Exception("No struct named: " + str(name))
154 
155  def get_enum(self, name):
156  for s in self.enums:
157  if name == s.name:
158  return s
159  raise Exception("No enum named: " + str(name))
160 
161 
162 
163 
164 def reorder_structs(model):
165  types = IgnoredStructs + IgnoredEnums + ["Bit", "Char", "CharArray", "Guid", "SByte", "Int8", "Int16", "Int32", "Int64", "UInt8", "UInt16", "UInt32", "UInt64", "DateTime", "Boolean", "Double", "Float", "ByteString", "Byte", "StatusCode", "DiagnosticInfo", "String", "AttributeID"] + [enum.name for enum in model.enums] + ["VariableAccessLevel"]
166  waiting = {}
167  newstructs = []
168  for s in model.structs:
169  types.append(s.name)
170  s.waitingfor = []
171  ok = True
172  for f in s.fields:
173  if f.uatype not in types:
174  if f.uatype in waiting.keys():
175  waiting[f.uatype].append(s)
176  s.waitingfor.append(f.uatype)
177  else:
178  waiting[f.uatype] = [s]
179  s.waitingfor.append(f.uatype)
180  ok = False
181  if ok:
182  newstructs.append(s)
183  waitings = waiting.pop(s.name, None)
184  if waitings:
185  for s2 in waitings:
186  s2.waitingfor.remove(s.name)
187  if not s2.waitingfor:
188  newstructs.append(s2)
189  if len(model.structs) != len(newstructs):
190  print("Error while reordering structs, some structs could not be reinserted, had {0} structs, we now have {1} structs".format(len(model.structs), len(newstructs)))
191  s1 = set(model.structs)
192  s2 = set(newstructs)
193  rest = s1 -s2
194  print("Variant" in types)
195  for s in s1-s2:
196  print("{0} is waiting for: {1}".format(s, s.waitingfor))
197  #print(s1 -s2)
198  #print(waiting)
199  model.structs = newstructs
200 
201 def override_types(model):
202  for struct in model.structs:
203  for field in struct.fields:
204  if field.name in OverrideTypes.keys():
205  field.uatype = OverrideTypes[field.name]
206 
207 def remove_duplicates(model):
208  for struct in model.structs:
209  fields = []
210  names = []
211  for field in struct.fields:
212  if field.name not in names:
213  names.append(field.name)
214  fields.append(field)
215  struct.fields = fields
216 
218  for struct in model.structs:
219  newfields = []
220  container = None
221  idx = 0
222  for field in struct.fields:
223  if field.uatype in ("UInt6", "NodeIdType"):
224  container = field.name
225  b = Bit()
226  b.name = field.name
227  b.idx = 0
228  b.container = container
229  b.length = 6
230  idx = b.length
231  struct.bits[b.name] = b
232 
233  if field.uatype == "Bit":
234  if not container or idx > 7:
235  container = "Encoding"
236  idx = 0
237  f = Field()
238  f.sourcetype = field.sourcetype
239  f.name = "Encoding"
240  f.uatype = "UInt8"
241  newfields.append(f)
242 
243  b = Bit()
244  b.name = field.name
245  b.idx = idx
246  b.container = container
247  b.length = field.bitlength
248  idx += field.bitlength
249  struct.bits[b.name] = b
250  else:
251  newfields.append(field)
252  struct.fields = newfields
253 
255  for struct in model.structs:
256  new = []
257  for field in struct.fields:
258  if not field.name.startswith("NoOf"):
259  new.append(field)
260  struct.fields = new
261 
263  for struct in model.structs:
264  new = []
265  for field in struct.fields:
266  if not field.name == "BodyLength":
267  new.append(field)
268  struct.fields = new
269 
270 #def remove_extensionobject_fields(model):
271  #for obj in model.structs:
272  #if obj.name.endswith("Request") or obj.name.endswith("Response"):
273  #obj.fields = [el for el in obj.fields if el.name not in ("TypeId", "Body", "Encoding")]
274 
275 def split_requests(model):
276  structs = []
277  for struct in model.structs:
278  structtype = None
279  if struct.name.endswith("Request") and not struct.name in NotRequest:
280  structtype = "Request"
281  elif struct.name.endswith("Response") or struct.name == "ServiceFault":
282  structtype = "Response"
283  if structtype:
284  #for field in struct.fields:
285  #if field.name == "Encoding":
286  #struct.fields.remove(field)
287  #break
288  #for field in struct.fields:
289  #if field.name == "BodyLength":
290  #struct.fields.remove(field)
291  #break
292  struct.needconstructor = True
293  field = Field()
294  field.name = "TypeId"
295  field.uatype = "NodeId"
296  struct.fields.insert(0, field)
297 
298  if structtype and not struct.name in NoSplitStruct:
299  paramstruct = Struct()
300  if structtype == "Request":
301  basename = struct.name.replace("Request", "") + "Parameters"
302  paramstruct.name = basename
303  else:
304  basename = struct.name.replace("Response", "") + "Result"
305  paramstruct.name = basename
306  paramstruct.fields = struct.fields[2:]
307  paramstruct.bits = struct.bits
308 
309  struct.fields = struct.fields[:2]
310  #struct.bits = {}
311  structs.append(paramstruct)
312 
313  typeid = Field()
314  typeid.name = "Parameters"
315  typeid.uatype = paramstruct.name
316  struct.fields.append(typeid)
317  structs.append(struct)
318  model.structs = structs
319 
320 
321 class Parser(object):
322  def __init__(self, path):
323  self.path = path
324  self.model = None
325 
326  def parse(self):
327  print("Parsing: ", self.path)
328  self.model = Model()
329  tree = ET.parse(self.path)
330  root = tree.getroot()
331  self.add_extension_object()
332  for child in root:
333  tag = child.tag[40:]
334  if tag == "StructuredType":
335  struct = self.parse_struct(child)
336  if struct.name != "ExtensionObject":
337  self.model.structs.append(struct)
338  self.model.struct_list.append(struct.name)
339  elif tag == "EnumeratedType":
340  enum = self.parse_enum(child)
341  self.model.enums.append(enum)
342  self.model.enum_list.append(enum.name)
343  #else:
344  #print("Not implemented node type: " + tag + "\n")
345  return self.model
346 
348  obj = Struct()
349  obj.name = "ExtensionObject"
350  f = Field()
351  f.name = "TypeId"
352  f.uatype = "NodeId"
353  obj.fields.append(f)
354  f = Field()
355  f.name = "BinaryBody"
356  f.uatype = "Bit"
357  obj.fields.append(f)
358  f = Field()
359  f.name = "XmlBody"
360  f.uatype = "Bit"
361  obj.fields.append(f)
362  f = Field()
363  f.name = "Body"
364  f.uatype = "ByteString"
365  f.switchfield = "BinaryBody"
366  obj.fields.append(f)
367  self.model.struct_list.append(obj.name)
368 
369  self.model.structs.append(obj)
370 
371  def parse_struct(self, child):
372  tag = child.tag[40:]
373  struct = Struct()
374  for key, val in child.attrib.items():
375  if key == "Name":
376  struct.name = val
377  elif key == "BaseType":
378  if ":" in val:
379  prefix, val = val.split(":")
380  struct.basetype = val
381  tmp = struct
382  while tmp.basetype:
383  struct.parents.append(tmp.basetype)
384  tmp = self.model.get_struct(tmp.basetype)
385  else:
386  print("Error unknown key: ", key)
387  for el in child:
388  tag = el.tag[40:]
389  if tag == "Field":
390  field = Field()
391  for key, val in el.attrib.items():
392  if key == "Name":
393  field.name = val
394  elif key == "TypeName":
395  field.uatype = val.split(":")[1]
396  elif key == "LengthField":
397  field.length = val
398  elif key == "SourceType":
399  field.sourcetype = val
400  elif key == "SwitchField":
401  field.switchfield = val
402  elif key == "SwitchValue":
403  field.switchvalue = val
404  elif key == "Length":
405  field.bitlength = int(val)
406  else:
407  print("Unknown field item: ", struct.name, key)
408 
409  struct.fields.append(field)
410  elif tag == "Documentation":
411  struct.doc = el.text
412  else:
413  print("Unknown tag: ", tag)
414 
415  return struct
416 
417  def parse_enum(self, child):
418  tag = child.tag[40:]
419  enum = Enum()
420  for k, v in child.items():
421  if k == "Name":
422  enum.name = v
423  elif k == "LengthInBits":
424  enum.uatype = "UInt" + v
425  else:
426  print("Unknown attr for enum: ", k)
427  for el in child:
428  tag = el.tag[40:]
429  if tag == "EnumeratedValue":
430  ev = EnumValue()
431  for k, v in el.attrib.items():
432  if k == "Name":
433  ev.name = v
434  elif k == "Value":
435  ev.value = v
436  else:
437  print("Unknown field attrib: ", k)
438  enum.values.append(ev)
439  elif tag == "Documentation":
440  enum.doc = el.text
441  else:
442  print("Unknown enum tag: ", tag)
443  return enum
444 
445 
446 #"def reorder_extobjects(model):
447  #ext = model.get_struct("ExtensionObject")
448  #print(ext)
449  #typeid = ext.fields[4]
450  #ext.fields.remove(typeid)
451  #ext.fields.insert(0, typeid)
452 
454  for struct in model.structs:
455  if not struct.basetype:
456  continue
457  emptystruct = False
458  if len(struct.fields) == 0:
459  emptystruct = True
460  if struct.basetype in ("ExtensionObject"):
461  struct.basetype = None
462  continue
463  base = model.get_struct(struct.basetype)
464  #if struct.basetype == "ExtensionObject" and len(struct.fields) != 0:
465  #if struct.basetype == "ExtensionObject" and len(struct.fields) != 0:
466  #if struc
467  #for f in base.fields:
468  #if f.name == "TypeId":
469  #f2 = copy(f)
470  #f2.switchfield = None
471  #struct.fields.insert(0, f2)
472  #break
473  #continue
474  for name, bit in base.bits.items():
475  struct.bits[name] = bit
476  for idx, field in enumerate(base.fields):
477  field = copy(field)
478  if field.name == "Body" and not emptystruct:
479  #print("Field is names Body", struct.name, field.name)
480  struct.extensionobject = True
481  field.name = "BodyLength"
482  field.uatype = "Int32"
483  field.length = None
484  field.switchfield = None
485  #print("Field is names Body 2", struct.name, field.name)
486  #HACK EXTENSIONOBJECT
487  #if base.name == "ExtensionObject":
488  #continue
489  #if field.uatype == "Bit":
490  #continue
491  #if field.name == "Body":
492  #continue
493  #if field.name == "TypeId":
494  #field.switchfield = None
495  #END HACK
496  if not field.sourcetype:
497  field.sourcetype = base.name
498  struct.fields.insert(idx, field)
499 
500 
501 
502 
503 
def get_enum(self, name)
def split_requests(model)
def override_types(model)
def parse_enum(self, child)
def remove_duplicates(model)
def add_basetype_members(model)
def get_struct(self, name)
def remove_body_length(model)
def add_encoding_field(model)
def remove_vector_length(model)
def __init__(self, path)
def parse_struct(self, child)
def add_extension_object(self)
def get_field(self, name)
def reorder_structs(model)


ros_opcua_impl_python_opcua
Author(s): Denis Štogl , Daniel Draper
autogenerated on Tue Jan 19 2021 03:12:43