generate_builtin.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  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. \tif(ptr==UA_NULL) return sizeof(%(name)s);
  128. \treturn 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,'+ t[0:t.find("*")].upper() + ",ptr->%(n)s)")
  135. has_fixed_size = False
  136. else:
  137. printc('\t + %(t)s_calcSizeBinary(&ptr->%(n)s)')
  138. has_fixed_size = False
  139. printc("\t;\n}\n")
  140. if has_fixed_size:
  141. fixed_size.add(name)
  142. # 5) EncodeBinary
  143. printc('''UA_Int32 %(name)s_encodeBinary(%(name)s const * src, UA_ByteString* dst, UA_UInt32 *offset) {
  144. \tUA_Int32 retval = UA_SUCCESS;''')
  145. for n,t in membermap.iteritems():
  146. if t.find("*") != -1:
  147. printc('\tretval |= UA_Int32_encodeBinary(&src->%(n)sSize,dst, offset);')
  148. printc("\tretval |= UA_Array_encodeBinary(&src->%(n)s,src->%(n)sSize," +
  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. \tUA_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,' + t[0:t.find("*")].upper() +
  162. ',(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. \tUA_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," +
  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. \tUA_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. \tif(src == UA_NULL || dst == UA_NULL) return UA_ERROR;
  202. \tUA_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," +
  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. printh('''/**
  214. * @file '''+sys.argv[2]+'''.h
  215. *
  216. * @brief Autogenerated data types defined in the UA standard
  217. *
  218. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  219. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  220. */
  221. #ifndef ''' + sys.argv[2].upper() + '''_H_
  222. #define ''' + sys.argv[2].upper() + '''_H_
  223. #include "ua_types.h"
  224. #include "ua_types_encoding_binary.h"
  225. #include "ua_types_encoding_xml.h"
  226. #include "ua_namespace_0.h"\n''')
  227. printc('''/**
  228. * @file '''+sys.argv[2]+'''.c
  229. *
  230. * @brief Autogenerated function implementations to manage the data types defined in the UA standard
  231. *
  232. * Generated from '''+sys.argv[1]+''' with script '''+sys.argv[0]+'''
  233. * on host '''+platform.uname()[1]+''' by user '''+getpass.getuser()+''' at '''+ time.strftime("%Y-%m-%d %I:%M:%S")+'''
  234. */
  235. #include "''' + sys.argv[2] + '.h"\n')
  236. #include "ua_types_encoding_binary.h"
  237. #include "ua_util.h"
  238. # types for which we create a vector type
  239. arraytypes = set()
  240. fields = tree.xpath("//opc:Field", namespaces=ns)
  241. for field in fields:
  242. if field.get("LengthField"):
  243. arraytypes.add(stripTypename(field.get("TypeName")))
  244. deferred_types = OrderedDict()
  245. #plugin handling
  246. import os
  247. files = [f for f in os.listdir('.') if os.path.isfile(f) and f[-3:] == ".py" and f[:7] == "plugin_"]
  248. plugin_types = []
  249. packageForType = OrderedDict()
  250. for f in files:
  251. package = f[:-3]
  252. exec "import " + package
  253. exec "pluginSetup = " + package + ".setup()"
  254. if pluginSetup["pluginType"] == "structuredObject":
  255. plugin_types.append(pluginSetup["tagName"])
  256. packageForType[pluginSetup["tagName"]] = [package,pluginSetup]
  257. print("Custom object creation for tag " + pluginSetup["tagName"] + " imported from package " + package)
  258. #end plugin handling
  259. for element in types:
  260. name = element.get("Name")
  261. if skipType(name):
  262. continue
  263. if element.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  264. createEnumerated(element)
  265. existing_types.add(name)
  266. elif element.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  267. if printableStructuredType(element):
  268. createStructured(element)
  269. existing_types.add(name)
  270. else: # the record contains types that were not yet detailed
  271. deferred_types[name] = element
  272. continue
  273. elif element.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  274. createOpaque(element)
  275. existing_types.add(name)
  276. for name, element in deferred_types.iteritems():
  277. if name in plugin_types:
  278. #execute plugin if registered
  279. exec "ret = " + packageForType[name][0]+"."+packageForType[name][1]["functionCall"]
  280. if ret == "default":
  281. createStructured(element)
  282. existing_types.add(name)
  283. else:
  284. createStructured(element)
  285. existing_types.add(name)
  286. printh('#endif')
  287. fh.close()
  288. fc.close()