generate_builtin.py 13 KB

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