generate_builtin.py 14 KB

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