generate_builtin.py 14 KB

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