generate_builtin.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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_UInt32 " + 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_UInt32)")
  72. printc("UA_TYPE_BINARY_ENCODING_AS(" + name + ", UA_UInt32)")
  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('''void %(name)s_print(const %(name)s *p, FILE *stream) {
  215. fprintf(stream, "(%(name)s){");''')
  216. for i,(n,t) in enumerate(membermap.iteritems()):
  217. if t.find("*") != -1:
  218. printc('\tUA_Int32_print(&p->%(n)sSize, stream);')
  219. printc("\tUA_Array_print(p->%(n)s, p->%(n)sSize, &UA_.types[" + t[0:t.find("*")].upper()+"], stream);")
  220. else:
  221. printc('\t%(t)s_print(&p->%(n)s,stream);')
  222. if i == len(membermap)-1:
  223. continue
  224. printc('\tfprintf(stream, ",");')
  225. printc('''\tfprintf(stream, "}");\n}\n''')
  226. printh('''/**
  227. * @file '''+sys.argv[2]+'''.h
  228. *
  229. * @brief Autogenerated data types defined in the UA standard
  230. *
  231. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  232. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  233. */
  234. #ifndef ''' + sys.argv[2].upper() + '''_H_
  235. #define ''' + sys.argv[2].upper() + '''_H_
  236. #include "ua_types.h"
  237. #include "ua_types_encoding_binary.h"
  238. #include "ua_types_encoding_xml.h"
  239. #include "ua_namespace_0.h"\n''')
  240. printc('''/**
  241. * @file '''+sys.argv[2]+'''.c
  242. *
  243. * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  244. *
  245. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  246. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  247. */
  248. #include "''' + sys.argv[2] + '.h"\n')
  249. #include "ua_types_encoding_binary.h"
  250. #include "util/ua_util.h"
  251. # types for which we create a vector type
  252. arraytypes = set()
  253. fields = tree.xpath("//opc:Field", namespaces=ns)
  254. for field in fields:
  255. if field.get("LengthField"):
  256. arraytypes.add(stripTypename(field.get("TypeName")))
  257. deferred_types = OrderedDict()
  258. #plugin handling
  259. import os
  260. files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
  261. plugin_types = []
  262. packageForType = OrderedDict()
  263. for f in files:
  264. package = f[:-3]
  265. exec "import " + package
  266. exec "pluginSetup = " + package + ".setup()"
  267. if pluginSetup["pluginType"] == "structuredObject":
  268. plugin_types.append(pluginSetup["tagName"])
  269. packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
  270. print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
  271. #end plugin handling
  272. for element in types:
  273. name = element.get("Name")
  274. if skipType(name):
  275. continue
  276. if element.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  277. createEnumerated(element)
  278. existing_types.add(name)
  279. elif element.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  280. if printableStructuredType(element):
  281. createStructured(element)
  282. existing_types.add(name)
  283. else: # the record contains types that were not yet detailed
  284. deferred_types[name] = element
  285. continue
  286. elif element.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  287. createOpaque(element)
  288. existing_types.add(name)
  289. for name, element in deferred_types.iteritems():
  290. if name in plugin_types:
  291. #execute plugin if registered
  292. exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
  293. if ret == "default":
  294. createStructured(element)
  295. existing_types.add(name)
  296. else:
  297. createStructured(element)
  298. existing_types.add(name)
  299. printh('#endif')
  300. fh.close()
  301. fc.close()