generate_builtin.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. from __future__ import print_function
  2. import sys
  3. import time
  4. import platform
  5. import getpass
  6. from collections import OrderedDict
  7. import re
  8. from lxml import etree
  9. import inspect
  10. if len(sys.argv) != 3:
  11. print("Usage: python generate_builtin.py <path/to/Opc.Ua.Types.bsd> <outfile w/o extension>", file=sys.stdout)
  12. exit(0)
  13. ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
  14. tree = etree.parse(sys.argv[1])
  15. types = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
  16. fh = open(sys.argv[2] + ".h",'w');
  17. fc = open(sys.argv[2] + ".c",'w');
  18. # dirty hack. we go up the call frames to access local variables of the calling
  19. # function. this allows to shorten code and get %()s replaces with less clutter.
  20. def printh(string):
  21. print(string % inspect.currentframe().f_back.f_locals, end='\n', file=fh)
  22. def printc(string):
  23. print(string % inspect.currentframe().f_back.f_locals, end='\n', file=fc)
  24. # types that are coded manually
  25. existing_types = set(["Boolean", "SByte", "Byte", "Int16", "UInt16", "Int32", "UInt32",
  26. "Int64", "UInt64", "Float", "Double", "String", "DateTime", "Guid",
  27. "ByteString", "XmlElement", "NodeId", "ExpandedNodeId", "StatusCode",
  28. "QualifiedName", "LocalizedText", "ExtensionObject", "DataValue",
  29. "Variant", "DiagnosticInfo"])
  30. fixed_size = set(["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16",
  31. "UA_Int32", "UA_UInt32", "UA_Int64", "UA_UInt64", "UA_Float",
  32. "UA_Double", "UA_DateTime", "UA_Guid", "UA_StatusCode"])
  33. # types we do not want to autogenerate
  34. def skipType(name):
  35. if name in existing_types:
  36. return True
  37. if re.search("NodeId$", name) != None:
  38. return True
  39. return False
  40. def stripTypename(tn):
  41. return tn[tn.find(":")+1:]
  42. def camlCase2CCase(item):
  43. if item in ["Float","Double"]:
  44. return "my" + item
  45. return item[:1].lower() + item[1:] if item else ''
  46. # are the types we need already in place? if not, postpone.
  47. def printableStructuredType(element):
  48. for child in element:
  49. if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  50. typename = stripTypename(child.get("TypeName"))
  51. if typename not in existing_types:
  52. return False
  53. return True
  54. def createEnumerated(element):
  55. valuemap = OrderedDict()
  56. name = "UA_" + element.get("Name")
  57. fixed_size.add(name)
  58. for child in element:
  59. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  60. printh("/** @brief " + child.text + " */")
  61. if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
  62. valuemap[name + "_" + child.get("Name")] = child.get("Value")
  63. valuemap = OrderedDict(sorted(valuemap.iteritems(), key=lambda (k,v): int(v)))
  64. printh("typedef UA_UInt32 " + name + ";")
  65. printh("enum " + name + "_enum { \n\t" +
  66. ",\n\t".join(map(lambda (key, value) : key.upper() + " = " + value, valuemap.iteritems())) +
  67. "\n};")
  68. printh("UA_TYPE_PROTOTYPES (" + name + ")")
  69. printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
  70. printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
  71. printc("UA_TYPE_AS(" + name + ", UA_UInt32)")
  72. printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_UInt32)")
  73. printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
  74. UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
  75. UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s\n)''')
  76. def createOpaque(element):
  77. name = "UA_" + element.get("Name")
  78. for child in element:
  79. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  80. printh("/** @brief " + child.text + " */")
  81. printh("typedef UA_ByteString %(name)s;")
  82. printh("UA_TYPE_PROTOTYPES(%(name)s)")
  83. printh("UA_TYPE_BINARY_ENCODING(%(name)s)")
  84. printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
  85. printc("UA_TYPE_AS(%(name)s, UA_ByteString)")
  86. printc("UA_TYPE_BINARY_ENCODING_AS(%(name)s, UA_ByteString)")
  87. printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
  88. UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
  89. UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)\n''')
  90. def createStructured(element):
  91. name = "UA_" + element.get("Name")
  92. # 1) Are there arrays in the type?
  93. lengthfields = set()
  94. for child in element:
  95. if child.get("LengthField"):
  96. lengthfields.add(child.get("LengthField"))
  97. # 2) Store members in membermap (name->type).
  98. membermap = OrderedDict()
  99. for child in element:
  100. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  101. printh("/** @brief " + child.text + " */")
  102. elif child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  103. if child.get("Name") in lengthfields:
  104. continue
  105. childname = camlCase2CCase(child.get("Name"))
  106. typename = stripTypename(child.get("TypeName"))
  107. if child.get("LengthField"):
  108. membermap[childname] = "UA_" + typename + "*"
  109. else:
  110. membermap[childname] = "UA_" + typename
  111. # 3) Print structure
  112. if len(membermap) > 0:
  113. printh("typedef struct %(name)s {")
  114. for n,t in membermap.iteritems():
  115. if t.find("*") != -1:
  116. printh("\t" + "UA_Int32 " + n + "Size;")
  117. printh("\t%(t)s %(n)s;")
  118. printh("} %(name)s;")
  119. else:
  120. printh("typedef void* %(name)s;")
  121. # 3) function prototypes
  122. printh("UA_TYPE_PROTOTYPES(" + name + ")")
  123. printh("UA_TYPE_BINARY_ENCODING(" + name + ")")
  124. printh("UA_TYPE_XML_ENCODING(" + name + ")\n")
  125. # 4) CalcSizeBinary
  126. printc('''UA_Int32 %(name)s_calcSizeBinary(%(name)s const * ptr) {
  127. \tif(ptr==UA_NULL) return sizeof(%(name)s);
  128. \treturn 0''')
  129. has_fixed_size = True
  130. for n,t in membermap.iteritems():
  131. if t in fixed_size:
  132. printc('\t + sizeof(%(t)s) // %(n)s')
  133. elif t.find("*") != -1:
  134. printc('\t + UA_Array_calcSizeBinary(ptr->%(n)sSize,'+ t[0:t.find("*")].upper() + ",ptr->%(n)s)")
  135. has_fixed_size = False
  136. else:
  137. printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
  138. has_fixed_size = False
  139. printc("\t;\n}\n")
  140. if has_fixed_size:
  141. fixed_size.add(name)
  142. # 5) EncodeBinary
  143. printc('''UA_Int32 %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {
  144. \tUA_Int32 retval = UA_SUCCESS;''')
  145. for n,t in membermap.iteritems():
  146. if t.find("*") != -1:
  147. printc("\tretval |= UA_Array_encodeBinary(src->%(n)s,src->%(n)sSize," +
  148. t[0:t.find("*")].upper() + ",dst,offset);")
  149. else:
  150. printc('\tretval |= %(t)s_encodeBinary(&src->%(n)s,dst,offset);')
  151. printc("\treturn retval;\n}\n")
  152. # 6) DecodeBinary
  153. printc('''UA_Int32 %(name)s_decodeBinary(UA_ByteString const * src, UA_UInt32 *offset, %(name)s * dst) {
  154. \tUA_Int32 retval = UA_SUCCESS;''')
  155. printc('\t'+name+'_init(dst);')
  156. for n,t in membermap.iteritems():
  157. if t.find("*") != -1:
  158. printc('\tCHECKED_DECODE(UA_Int32_decodeBinary(src,offset,&dst->%(n)sSize),' +
  159. '%(name)s_deleteMembers(dst));')
  160. printc('\tCHECKED_DECODE(UA_Array_decodeBinary(src,offset,dst->%(n)sSize,' + t[0:t.find("*")].upper() +
  161. ',(void**)&dst->%(n)s), %(name)s_deleteMembers(dst));')
  162. else:
  163. printc('\tCHECKED_DECODE(%(t)s_decodeBinary(src,offset,&dst->%(n)s), %(name)s_deleteMembers(dst));')
  164. printc("\treturn retval;\n}\n")
  165. # 7) Xml
  166. printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
  167. UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
  168. UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
  169. # 8) Delete
  170. printc('''UA_Int32 %(name)s_delete(%(name)s *p) {
  171. UA_Int32 retval = UA_SUCCESS;
  172. retval |= %(name)s_deleteMembers(p);
  173. retval |= UA_free(p);
  174. return retval;\n}\n''')
  175. # 9) DeleteMembers
  176. printc('''UA_Int32 %(name)s_deleteMembers(%(name)s *p) {
  177. \tUA_Int32 retval = UA_SUCCESS;''')
  178. for n,t in membermap.iteritems():
  179. if not t in fixed_size: # dynamic size on the wire
  180. if t.find("*") != -1:
  181. printc("\tretval |= UA_Array_delete((void*)p->%(n)s,p->%(n)sSize," +
  182. t[0:t.find("*")].upper()+");")
  183. else:
  184. printc('\tretval |= %(t)s_deleteMembers(&p->%(n)s);')
  185. printc("\treturn retval;\n}\n")
  186. # 10) Init
  187. printc('''UA_Int32 %(name)s_init(%(name)s *p) {
  188. \tUA_Int32 retval = UA_SUCCESS;''')
  189. for n,t in membermap.iteritems():
  190. if t.find("*") != -1:
  191. printc('\tp->%(n)sSize = 0;')
  192. printc('\tp->%(n)s = UA_NULL;')
  193. else:
  194. printc('\tretval |= %(t)s_init(&p->%(n)s);')
  195. printc("\treturn retval;\n}\n")
  196. # 11) New
  197. printc("UA_TYPE_NEW_DEFAULT(%(name)s)")
  198. # 12) Copy
  199. printc('''UA_Int32 %(name)s_copy(const %(name)s *src,%(name)s *dst) {
  200. \tif(src == UA_NULL || dst == UA_NULL) return UA_ERROR;
  201. \tUA_Int32 retval = UA_SUCCESS;''')
  202. printc("\tmemcpy(dst, src, sizeof(%(name)s));")
  203. for n,t in membermap.iteritems():
  204. if t.find("*") != -1:
  205. printc('\tdst->%(n)s = src->%(n)s;')
  206. printc("\tretval |= UA_Array_copy(src->%(n)s, src->%(n)sSize," +
  207. t[0:t.find("*")].upper()+",(void**)&dst->%(n)s);")
  208. continue
  209. if not t in fixed_size: # there are members of variable size
  210. printc('\tretval |= %(t)s_copy(&src->%(n)s,&dst->%(n)s);')
  211. printc("\treturn retval;\n}\n")
  212. printh('''/**
  213. * @file '''+sys.argv[2]+'''.h
  214. *
  215. * @brief Autogenerated data types defined in the UA standard
  216. *
  217. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  218. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  219. */
  220. #ifndef ''' + sys.argv[2].upper() + '''_H_
  221. #define ''' + sys.argv[2].upper() + '''_H_
  222. #include "ua_types.h"
  223. #include "ua_types_encoding_binary.h"
  224. #include "ua_types_encoding_xml.h"
  225. #include "ua_namespace_0.h"\n''')
  226. printc('''/**
  227. * @file '''+sys.argv[2]+'''.c
  228. *
  229. * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  230. *
  231. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  232. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  233. */
  234. #include "''' + sys.argv[2] + '.h"\n')
  235. #include "ua_types_encoding_binary.h"
  236. #include "ua_util.h"
  237. # types for which we create a vector type
  238. arraytypes = set()
  239. fields = tree.xpath("//opc:Field", namespaces=ns)
  240. for field in fields:
  241. if field.get("LengthField"):
  242. arraytypes.add(stripTypename(field.get("TypeName")))
  243. deferred_types = OrderedDict()
  244. #plugin handling
  245. import os
  246. files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
  247. plugin_types = []
  248. packageForType = OrderedDict()
  249. for f in files:
  250. package = f[:-3]
  251. exec "import " + package
  252. exec "pluginSetup = " + package + ".setup()"
  253. if pluginSetup["pluginType"] == "structuredObject":
  254. plugin_types.append(pluginSetup["tagName"])
  255. packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
  256. print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
  257. #end plugin handling
  258. for element in types:
  259. name = element.get("Name")
  260. if skipType(name):
  261. continue
  262. if element.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  263. createEnumerated(element)
  264. existing_types.add(name)
  265. elif element.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  266. if printableStructuredType(element):
  267. createStructured(element)
  268. existing_types.add(name)
  269. else: # the record contains types that were not yet detailed
  270. deferred_types[name] = element
  271. continue
  272. elif element.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  273. createOpaque(element)
  274. existing_types.add(name)
  275. for name, element in deferred_types.iteritems():
  276. if name in plugin_types:
  277. #execute plugin if registered
  278. exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
  279. if ret == "default":
  280. createStructured(element)
  281. existing_types.add(name)
  282. else:
  283. createStructured(element)
  284. existing_types.add(name)
  285. printh('#endif')
  286. fh.close()
  287. fc.close()