generate_builtin.py 13 KB

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