generate_builtin.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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_Int32 " + 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_Int32)")
  72. printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_Int32)")
  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. if(ptr==UA_NULL) return sizeof(%(name)s);
  128. return 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,&UA_.types['+ t[0:t.find("*")].upper() +
  135. "],ptr->%(n)s)")
  136. has_fixed_size = False
  137. else:
  138. printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
  139. has_fixed_size = False
  140. printc("\t;\n}\n")
  141. if has_fixed_size:
  142. fixed_size.add(name)
  143. # 5) EncodeBinary
  144. printc('''UA_Int32 %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {
  145. UA_Int32 retval = UA_SUCCESS;''')
  146. for n,t in membermap.iteritems():
  147. if t.find("*") != -1:
  148. printc("\tretval |= UA_Array_encodeBinary(src->%(n)s,src->%(n)sSize,&UA_.types[" +
  149. t[0:t.find("*")].upper() + "],dst,offset);")
  150. else:
  151. printc('\tretval |= %(t)s_encodeBinary(&src->%(n)s,dst,offset);')
  152. printc("\treturn retval;\n}\n")
  153. # 6) DecodeBinary
  154. printc('''UA_Int32 %(name)s_decodeBinary(UA_ByteString const * src, UA_UInt32 *offset, %(name)s * dst) {
  155. UA_Int32 retval = UA_SUCCESS;''')
  156. printc('\t'+name+'_init(dst);')
  157. for n,t in membermap.iteritems():
  158. if t.find("*") != -1:
  159. printc('\tCHECKED_DECODE(UA_Int32_decodeBinary(src,offset,&dst->%(n)sSize),' +
  160. '%(name)s_deleteMembers(dst));')
  161. printc('\tCHECKED_DECODE(UA_Array_decodeBinary(src,offset,dst->%(n)sSize,&UA_.types[' +
  162. t[0:t.find("*")].upper() + '],(void**)&dst->%(n)s), %(name)s_deleteMembers(dst));')
  163. else:
  164. printc('\tCHECKED_DECODE(%(t)s_decodeBinary(src,offset,&dst->%(n)s), %(name)s_deleteMembers(dst));')
  165. printc("\treturn retval;\n}\n")
  166. # 7) Xml
  167. printc('''UA_TYPE_METHOD_CALCSIZEXML_NOTIMPL(%(name)s)
  168. UA_TYPE_METHOD_ENCODEXML_NOTIMPL(%(name)s)
  169. UA_TYPE_METHOD_DECODEXML_NOTIMPL(%(name)s)''')
  170. # 8) Delete
  171. printc('''UA_Int32 %(name)s_delete(%(name)s *p) {
  172. UA_Int32 retval = UA_SUCCESS;
  173. retval |= %(name)s_deleteMembers(p);
  174. retval |= UA_free(p);
  175. return retval;\n}\n''')
  176. # 9) DeleteMembers
  177. printc('''UA_Int32 %(name)s_deleteMembers(%(name)s *p) {
  178. UA_Int32 retval = UA_SUCCESS;''')
  179. for n,t in membermap.iteritems():
  180. if not t in fixed_size: # dynamic size on the wire
  181. if t.find("*") != -1:
  182. printc("\tretval |= UA_Array_delete((void*)p->%(n)s,p->%(n)sSize,&UA_.types[" +
  183. t[0:t.find("*")].upper()+"]);")
  184. else:
  185. printc('\tretval |= %(t)s_deleteMembers(&p->%(n)s);')
  186. printc("\treturn retval;\n}\n")
  187. # 10) Init
  188. printc('''UA_Int32 %(name)s_init(%(name)s *p) {
  189. UA_Int32 retval = UA_SUCCESS;''')
  190. for n,t in membermap.iteritems():
  191. if t.find("*") != -1:
  192. printc('\tp->%(n)sSize = 0;')
  193. printc('\tp->%(n)s = UA_NULL;')
  194. else:
  195. printc('\tretval |= %(t)s_init(&p->%(n)s);')
  196. printc("\treturn retval;\n}\n")
  197. # 11) New
  198. printc("UA_TYPE_NEW_DEFAULT(%(name)s)")
  199. # 12) Copy
  200. printc('''UA_Int32 %(name)s_copy(const %(name)s *src,%(name)s *dst) {
  201. if(src == UA_NULL || dst == UA_NULL) return UA_ERROR;
  202. UA_Int32 retval = UA_SUCCESS;''')
  203. printc("\tmemcpy(dst, src, sizeof(%(name)s));")
  204. for n,t in membermap.iteritems():
  205. if t.find("*") != -1:
  206. printc('\tdst->%(n)s = src->%(n)s;')
  207. printc("\tretval |= UA_Array_copy(src->%(n)s, src->%(n)sSize,&UA_.types[" +
  208. t[0:t.find("*")].upper()+"],(void**)&dst->%(n)s);")
  209. continue
  210. if not t in fixed_size: # there are members of variable size
  211. printc('\tretval |= %(t)s_copy(&src->%(n)s,&dst->%(n)s);')
  212. printc("\treturn retval;\n}\n")
  213. # 13) Print
  214. printc('''#ifdef DEBUG''')
  215. printc('''void %(name)s_print(const %(name)s *p, FILE *stream) {
  216. fprintf(stream, "(%(name)s){");''')
  217. for i,(n,t) in enumerate(membermap.iteritems()):
  218. if t.find("*") != -1:
  219. printc('\tUA_Int32_print(&p->%(n)sSize, stream);')
  220. printc("\tUA_Array_print(p->%(n)s, p->%(n)sSize, &UA_.types[" + t[0:t.find("*")].upper()+"], stream);")
  221. else:
  222. printc('\t%(t)s_print(&p->%(n)s,stream);')
  223. if i == len(membermap)-1:
  224. continue
  225. printc('\tfprintf(stream, ",");')
  226. printc('''\tfprintf(stream, "}");\n}''')
  227. printc('#endif');
  228. printc('''\n''')
  229. printh('''/**
  230. * @file '''+sys.argv[2]+'''.h
  231. *
  232. * @brief Autogenerated data types defined in the UA standard
  233. *
  234. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  235. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  236. */
  237. #ifndef ''' + sys.argv[2].upper() + '''_H_
  238. #define ''' + sys.argv[2].upper() + '''_H_
  239. #include "ua_types.h"
  240. #include "ua_types_encoding_binary.h"
  241. #include "ua_types_encoding_xml.h"
  242. #include "ua_namespace_0.h"\n''')
  243. printc('''/**
  244. * @file '''+sys.argv[2]+'''.c
  245. *
  246. * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  247. *
  248. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  249. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  250. */
  251. #include "''' + sys.argv[2] + '.h"\n')
  252. #include "ua_types_encoding_binary.h"
  253. #include "util/ua_util.h"
  254. # types for which we create a vector type
  255. arraytypes = set()
  256. fields = tree.xpath("//opc:Field", namespaces=ns)
  257. for field in fields:
  258. if field.get("LengthField"):
  259. arraytypes.add(stripTypename(field.get("TypeName")))
  260. deferred_types = OrderedDict()
  261. #plugin handling
  262. import os
  263. files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
  264. plugin_types = []
  265. packageForType = OrderedDict()
  266. for f in files:
  267. package = f[:-3]
  268. exec "import " + package
  269. exec "pluginSetup = " + package + ".setup()"
  270. if pluginSetup["pluginType"] == "structuredObject":
  271. plugin_types.append(pluginSetup["tagName"])
  272. packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
  273. print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
  274. #end plugin handling
  275. for element in types:
  276. name = element.get("Name")
  277. if skipType(name):
  278. continue
  279. if element.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  280. createEnumerated(element)
  281. existing_types.add(name)
  282. elif element.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  283. if printableStructuredType(element):
  284. createStructured(element)
  285. existing_types.add(name)
  286. else: # the record contains types that were not yet detailed
  287. deferred_types[name] = element
  288. continue
  289. elif element.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  290. createOpaque(element)
  291. existing_types.add(name)
  292. for name, element in deferred_types.iteritems():
  293. if name in plugin_types:
  294. #execute plugin if registered
  295. exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
  296. if ret == "default":
  297. createStructured(element)
  298. existing_types.add(name)
  299. else:
  300. createStructured(element)
  301. existing_types.add(name)
  302. printh('#endif')
  303. fh.close()
  304. fc.close()