backend_open62541_typedefinitions.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. from __future__ import print_function
  2. import re
  3. import itertools
  4. import sys
  5. import time
  6. import getpass
  7. import platform
  8. if sys.version_info[0] >= 3:
  9. from nodeset_compiler.type_parser import BuiltinType, EnumerationType, OpaqueType, StructType
  10. else:
  11. from type_parser import BuiltinType, EnumerationType, OpaqueType, StructType
  12. # Some types can be memcpy'd off the binary stream. That's especially important
  13. # for arrays. But we need to check if they contain padding and whether the
  14. # endianness is correct. This dict gives the C-statement that must be true for the
  15. # type to be overlayable. Parsed types are added if they apply.
  16. builtin_overlayable = {"Boolean": "true",
  17. "SByte": "true", "Byte": "true",
  18. "Int16": "UA_BINARY_OVERLAYABLE_INTEGER",
  19. "UInt16": "UA_BINARY_OVERLAYABLE_INTEGER",
  20. "Int32": "UA_BINARY_OVERLAYABLE_INTEGER",
  21. "UInt32": "UA_BINARY_OVERLAYABLE_INTEGER",
  22. "Int64": "UA_BINARY_OVERLAYABLE_INTEGER",
  23. "UInt64": "UA_BINARY_OVERLAYABLE_INTEGER",
  24. "Float": "UA_BINARY_OVERLAYABLE_FLOAT",
  25. "Double": "UA_BINARY_OVERLAYABLE_FLOAT",
  26. "DateTime": "UA_BINARY_OVERLAYABLE_INTEGER",
  27. "StatusCode": "UA_BINARY_OVERLAYABLE_INTEGER",
  28. "Guid": "(UA_BINARY_OVERLAYABLE_INTEGER && " +
  29. "offsetof(UA_Guid, data2) == sizeof(UA_UInt32) && " +
  30. "offsetof(UA_Guid, data3) == (sizeof(UA_UInt16) + sizeof(UA_UInt32)) && " +
  31. "offsetof(UA_Guid, data4) == (2*sizeof(UA_UInt32)))"}
  32. whitelistFuncAttrWarnUnusedResult = [] # for instances [ "String", "ByteString", "LocalizedText" ]
  33. # Escape C strings:
  34. def makeCLiteral(value):
  35. return re.sub(r'(?<!\\)"', r'\\"', value.replace('\\', r'\\\\').replace('\n', r'\\n').replace('\r', r''))
  36. # Strip invalid characters to create valid C identifiers (variable names etc):
  37. def makeCIdentifier(value):
  38. return re.sub(r'[^\w]', '', value)
  39. def getNodeidTypeAndId(nodeId):
  40. if '=' not in nodeId:
  41. return "UA_NODEIDTYPE_NUMERIC, {{{0}}}".format(nodeId)
  42. if nodeId.startswith("i="):
  43. return "UA_NODEIDTYPE_NUMERIC, {{{0}}}".format(nodeId[2:])
  44. if nodeId.startswith("s="):
  45. strId = nodeId[2:]
  46. return "UA_NODEIDTYPE_STRING, {{ .string = UA_STRING_STATIC(\"{id}\") }}".format(id=strId.replace("\"", "\\\""))
  47. class CGenerator(object):
  48. def __init__(self, parser, inname, outfile, is_internal_types):
  49. self.parser = parser
  50. self.inname = inname
  51. self.outfile = outfile
  52. self.is_internal_types = is_internal_types
  53. self.filtered_types = None
  54. self.fh = None
  55. self.ff = None
  56. self.fc = None
  57. self.fe = None
  58. @staticmethod
  59. def get_type_index(datatype):
  60. if isinstance(datatype, BuiltinType):
  61. return makeCIdentifier("UA_TYPES_" + datatype.name.upper())
  62. if isinstance(datatype, EnumerationType):
  63. return datatype.strTypeIndex;
  64. if datatype.name is not None:
  65. return "UA_" + makeCIdentifier(datatype.outname.upper() + "_" + datatype.name.upper())
  66. return makeCIdentifier(datatype.outname.upper())
  67. @staticmethod
  68. def get_type_kind(datatype):
  69. if isinstance(datatype, BuiltinType):
  70. return "UA_DATATYPEKIND_" + datatype.name.upper()
  71. if isinstance(datatype, EnumerationType):
  72. return datatype.strTypeKind
  73. if isinstance(datatype, OpaqueType):
  74. return "UA_DATATYPEKIND_" + datatype.base_type.upper()
  75. if isinstance(datatype, StructType):
  76. return "UA_DATATYPEKIND_STRUCTURE"
  77. raise RuntimeError("Unknown type")
  78. @staticmethod
  79. def get_struct_overlayable(struct):
  80. if not struct.pointerfree == "false":
  81. return "false"
  82. before = None
  83. overlayable = ""
  84. for m in struct.members:
  85. if m.is_array or not m.member_type.pointerfree:
  86. return "false"
  87. overlayable += "\n\t\t && " + m.member_type.overlayable
  88. if before:
  89. overlayable += "\n\t\t && offsetof(UA_%s, %s) == (offsetof(UA_%s, %s) + sizeof(UA_%s))" % \
  90. (makeCIdentifier(struct.name), makeCIdentifier(m.name), makeCIdentifier(struct.name),
  91. makeCIdentifier(before.name), makeCIdentifier(before.member_type.name))
  92. before = m
  93. return overlayable
  94. def get_type_overlayable(self, datatype):
  95. if isinstance(datatype, BuiltinType) or isinstance(datatype, OpaqueType):
  96. return builtin_overlayable[datatype.name] if datatype.name in builtin_overlayable else "false"
  97. if isinstance(datatype, EnumerationType):
  98. return "UA_BINARY_OVERLAYABLE_INTEGER"
  99. if isinstance(datatype, StructType):
  100. return self.get_struct_overlayable(datatype)
  101. raise RuntimeError("Unknown datatype")
  102. def print_datatype(self, datatype):
  103. binaryEncodingId = "0"
  104. if datatype.name in self.parser.typedescriptions:
  105. description = self.parser.typedescriptions[datatype.name]
  106. typeid = "{%s, %s}" % (description.namespaceid, getNodeidTypeAndId(description.nodeid))
  107. # xmlEncodingId = description.xmlEncodingId
  108. binaryEncodingId = description.binaryEncodingId
  109. else:
  110. if not self.is_internal_types:
  111. raise RuntimeError("NodeId for " + datatype.name + " not found in .csv file")
  112. else:
  113. typeid = "{0, UA_NODEIDTYPE_NUMERIC, {0}}"
  114. idName = makeCIdentifier(datatype.name)
  115. pointerfree = "true" if datatype.pointerfree else "false"
  116. return "{\n UA_TYPENAME(\"%s\") /* .typeName */\n" % idName + \
  117. " " + typeid + ", /* .typeId */\n" + \
  118. " sizeof(UA_" + idName + "), /* .memSize */\n" + \
  119. " " + self.get_type_index(datatype) + ", /* .typeIndex */\n" + \
  120. " " + self.get_type_kind(datatype) + ", /* .typeKind */\n" + \
  121. " " + pointerfree + ", /* .pointerFree */\n" + \
  122. " " + self.get_type_overlayable(datatype) + ", /* .overlayable */\n" + \
  123. " " + str(len(datatype.members)) + ", /* .membersSize */\n" + \
  124. " " + binaryEncodingId + ", /* .binaryEncodingId */\n" + \
  125. " %s_members" % idName + " /* .members */\n}"
  126. @staticmethod
  127. def print_members(datatype):
  128. idName = makeCIdentifier(datatype.name)
  129. if len(datatype.members) == 0:
  130. return "#define %s_members NULL" % (idName)
  131. members = "static UA_DataTypeMember %s_members[%s] = {" % (idName, len(datatype.members))
  132. before = None
  133. size = len(datatype.members)
  134. for i, member in enumerate(datatype.members):
  135. member_name = makeCIdentifier(member.name)
  136. member_name_capital = member_name
  137. if len(member_name) > 0:
  138. member_name_capital = member_name[0].upper() + member_name[1:]
  139. m = "\n{\n UA_TYPENAME(\"%s\") /* .memberName */\n" % member_name_capital
  140. m += " UA_%s_%s, /* .memberTypeIndex */\n" % (
  141. member.member_type.outname.upper(), makeCIdentifier(member.member_type.name.upper()))
  142. m += " "
  143. if not before:
  144. m += "0,"
  145. else:
  146. if member.is_array:
  147. m += "offsetof(UA_%s, %sSize)" % (idName, member_name)
  148. else:
  149. m += "offsetof(UA_%s, %s)" % (idName, member_name)
  150. m += " - offsetof(UA_%s, %s)" % (idName, makeCIdentifier(before.name))
  151. if before.is_array:
  152. m += " - sizeof(void*),"
  153. else:
  154. m += " - sizeof(UA_%s)," % makeCIdentifier(before.member_type.name)
  155. m += " /* .padding */\n"
  156. m += " %s, /* .namespaceZero */\n" % ("true" if member.member_type.ns0 else "false")
  157. m += (" true" if member.is_array else " false") + " /* .isArray */\n}"
  158. if i != size:
  159. m += ","
  160. members += m
  161. before = member
  162. return members + "};"
  163. @staticmethod
  164. def print_datatype_ptr(datatype):
  165. return "&UA_" + datatype.outname.upper() + "[UA_" + makeCIdentifier(
  166. datatype.outname.upper() + "_" + datatype.name.upper()) + "]"
  167. def print_functions(self, datatype):
  168. idName = makeCIdentifier(datatype.name)
  169. funcs = "static UA_INLINE void\nUA_%s_init(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (
  170. idName, idName, idName)
  171. funcs += "static UA_INLINE UA_%s *\nUA_%s_new(void) {\n return (UA_%s*)UA_new(%s);\n}\n\n" % (
  172. idName, idName, idName, CGenerator.print_datatype_ptr(datatype))
  173. if datatype.pointerfree == "true":
  174. funcs += "static UA_INLINE UA_StatusCode\nUA_%s_copy(const UA_%s *src, UA_%s *dst) {\n *dst = *src;\n return UA_STATUSCODE_GOOD;\n}\n\n" % (
  175. idName, idName, idName)
  176. funcs += "static UA_INLINE void\nUA_%s_deleteMembers(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (
  177. idName, idName, idName)
  178. funcs += "static UA_INLINE void\nUA_%s_clear(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (
  179. idName, idName, idName)
  180. else:
  181. for entry in whitelistFuncAttrWarnUnusedResult:
  182. if idName == entry:
  183. funcs += "UA_INTERNAL_FUNC_ATTR_WARN_UNUSED_RESULT "
  184. break
  185. funcs += "static UA_INLINE UA_StatusCode\nUA_%s_copy(const UA_%s *src, UA_%s *dst) {\n return UA_copy(src, dst, %s);\n}\n\n" % (
  186. idName, idName, idName, self.print_datatype_ptr(datatype))
  187. funcs += "static UA_INLINE void\nUA_%s_deleteMembers(UA_%s *p) {\n UA_clear(p, %s);\n}\n\n" % (
  188. idName, idName, self.print_datatype_ptr(datatype))
  189. funcs += "static UA_INLINE void\nUA_%s_clear(UA_%s *p) {\n UA_clear(p, %s);\n}\n\n" % (
  190. idName, idName, self.print_datatype_ptr(datatype))
  191. funcs += "static UA_INLINE void\nUA_%s_delete(UA_%s *p) {\n UA_delete(p, %s);\n}" % (
  192. idName, idName, self.print_datatype_ptr(datatype))
  193. return funcs
  194. def print_datatype_encoding(self, datatype):
  195. idName = makeCIdentifier(datatype.name)
  196. enc = "static UA_INLINE size_t\nUA_%s_calcSizeBinary(const UA_%s *src) {\n return UA_calcSizeBinary(src, %s);\n}\n"
  197. enc += "static UA_INLINE UA_StatusCode\nUA_%s_encodeBinary(const UA_%s *src, UA_Byte **bufPos, const UA_Byte *bufEnd) {\n return UA_encodeBinary(src, %s, bufPos, &bufEnd, NULL, NULL);\n}\n"
  198. enc += "static UA_INLINE UA_StatusCode\nUA_%s_decodeBinary(const UA_ByteString *src, size_t *offset, UA_%s *dst) {\n return UA_decodeBinary(src, offset, dst, %s, NULL);\n}"
  199. return enc % tuple(
  200. list(itertools.chain(*itertools.repeat([idName, idName, self.print_datatype_ptr(datatype)], 3))))
  201. @staticmethod
  202. def print_enum_typedef(enum):
  203. if sys.version_info[0] < 3:
  204. values = enum.elements.iteritems()
  205. else:
  206. values = enum.elements.items()
  207. if enum.isOptionSet == True:
  208. return "typedef " + enum.strDataType + " " + makeCIdentifier("UA_" + enum.name) + ";\n\n" + "\n".join(
  209. map(lambda kv: "#define " + makeCIdentifier("UA_" + enum.name.upper() + "_" + kv[0].upper()) +
  210. " " + kv[1], values))
  211. else:
  212. return "typedef enum {\n " + ",\n ".join(
  213. map(lambda kv: makeCIdentifier("UA_" + enum.name.upper() + "_" + kv[0].upper()) +
  214. " = " + kv[1], values)) + \
  215. ",\n __UA_{0}_FORCE32BIT = 0x7fffffff\n".format(makeCIdentifier(enum.name.upper())) + "} " + \
  216. "UA_{0};\nUA_STATIC_ASSERT(sizeof(UA_{0}) == sizeof(UA_Int32), enum_must_be_32bit);".format(
  217. makeCIdentifier(enum.name))
  218. @staticmethod
  219. def print_struct_typedef(struct):
  220. if len(struct.members) == 0:
  221. return "typedef void * UA_%s;" % makeCIdentifier(struct.name)
  222. returnstr = "typedef struct {\n"
  223. for member in struct.members:
  224. if member.is_array:
  225. returnstr += " size_t %sSize;\n" % makeCIdentifier(member.name)
  226. returnstr += " UA_%s *%s;\n" % (
  227. makeCIdentifier(member.member_type.name), makeCIdentifier(member.name))
  228. else:
  229. returnstr += " UA_%s %s;\n" % (
  230. makeCIdentifier(member.member_type.name), makeCIdentifier(member.name))
  231. return returnstr + "} UA_%s;" % makeCIdentifier(struct.name)
  232. @staticmethod
  233. def print_datatype_typedef(datatype):
  234. if isinstance(datatype, EnumerationType):
  235. return CGenerator.print_enum_typedef(datatype)
  236. if isinstance(datatype, OpaqueType):
  237. return "typedef UA_" + datatype.base_type + " UA_%s;" % datatype.name
  238. if isinstance(datatype, StructType):
  239. return CGenerator.print_struct_typedef(datatype)
  240. raise RuntimeError("Type does not have an associated typedef")
  241. def write_definitions(self):
  242. self.fh = open(self.outfile + "_generated.h", 'w')
  243. self.ff = open(self.outfile + "_generated_handling.h", 'w')
  244. self.fe = open(self.outfile + "_generated_encoding_binary.h", 'w')
  245. self.fc = open(self.outfile + "_generated.c", 'w')
  246. self.filtered_types = self.iter_types(self.parser.types)
  247. self.print_header()
  248. self.print_handling()
  249. self.print_description_array()
  250. self.print_encoding()
  251. self.fh.close()
  252. self.ff.close()
  253. self.fc.close()
  254. self.fe.close()
  255. def printh(self, string):
  256. print(string, end='\n', file=self.fh)
  257. def printf(self, string):
  258. print(string, end='\n', file=self.ff)
  259. def printe(self, string):
  260. print(string, end='\n', file=self.fe)
  261. def printc(self, string):
  262. print(string, end='\n', file=self.fc)
  263. def iter_types(self, v):
  264. l = None
  265. if sys.version_info[0] < 3:
  266. l = list(v.itervalues())
  267. else:
  268. l = list(v.values())
  269. if len(self.parser.selected_types) > 0:
  270. l = list(filter(lambda t: t.name in self.parser.selected_types, l))
  271. if self.parser.no_builtin:
  272. l = list(filter(lambda t: not isinstance(t, BuiltinType), l))
  273. l = list(filter(lambda t: t.name not in self.parser.types_imported, l))
  274. return l
  275. def print_header(self):
  276. self.printh('''/* Generated from ''' + self.inname + ''' with script ''' + sys.argv[0] + '''
  277. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime(
  278. "%Y-%m-%d %I:%M:%S") + ''' */
  279. #ifndef ''' + self.parser.outname.upper() + '''_GENERATED_H_
  280. #define ''' + self.parser.outname.upper() + '''_GENERATED_H_
  281. #ifdef UA_ENABLE_AMALGAMATION
  282. #include "open62541.h"
  283. #else
  284. #include <open62541/types.h>
  285. ''' + ('#include <open62541/types_generated.h>\n' if self.parser.outname != "types" else '') + '''
  286. #endif
  287. _UA_BEGIN_DECLS
  288. ''')
  289. self.printh('''/**
  290. * Every type is assigned an index in an array containing the type descriptions.
  291. * These descriptions are used during type handling (copying, deletion,
  292. * binary encoding, ...). */''')
  293. self.printh("#define UA_" + self.parser.outname.upper() + "_COUNT %s" % (str(len(self.filtered_types))))
  294. if len(self.filtered_types) > 0:
  295. self.printh(
  296. "extern UA_EXPORT const UA_DataType UA_" + self.parser.outname.upper() + "[UA_" + self.parser.outname.upper() + "_COUNT];")
  297. for i, t in enumerate(self.filtered_types):
  298. self.printh("\n/**\n * " + t.name)
  299. self.printh(" * " + "^" * len(t.name))
  300. if t.description == "":
  301. self.printh(" */")
  302. else:
  303. self.printh(" * " + t.description + " */")
  304. if not isinstance(t, BuiltinType):
  305. self.printh(self.print_datatype_typedef(t) + "\n")
  306. self.printh(
  307. "#define UA_" + makeCIdentifier(self.parser.outname.upper() + "_" + t.name.upper()) + " " + str(i))
  308. self.printh('''
  309. _UA_END_DECLS
  310. #endif /* %s_GENERATED_H_ */''' % self.parser.outname.upper())
  311. def print_handling(self):
  312. self.printf('''/* Generated from ''' + self.inname + ''' with script ''' + sys.argv[0] + '''
  313. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime(
  314. "%Y-%m-%d %I:%M:%S") + ''' */
  315. #ifndef ''' + self.parser.outname.upper() + '''_GENERATED_HANDLING_H_
  316. #define ''' + self.parser.outname.upper() + '''_GENERATED_HANDLING_H_
  317. #include "''' + self.parser.outname + '''_generated.h"
  318. _UA_BEGIN_DECLS
  319. #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
  320. # pragma GCC diagnostic push
  321. # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  322. # pragma GCC diagnostic ignored "-Wmissing-braces"
  323. #endif
  324. ''')
  325. for t in self.filtered_types:
  326. self.printf("\n/* " + t.name + " */")
  327. self.printf(self.print_functions(t))
  328. self.printf('''
  329. #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
  330. # pragma GCC diagnostic pop
  331. #endif
  332. _UA_END_DECLS
  333. #endif /* %s_GENERATED_HANDLING_H_ */''' % self.parser.outname.upper())
  334. def print_description_array(self):
  335. self.printc('''/* Generated from ''' + self.inname + ''' with script ''' + sys.argv[0] + '''
  336. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime(
  337. "%Y-%m-%d %I:%M:%S") + ''' */
  338. #include "''' + self.parser.outname + '''_generated.h"''')
  339. for t in self.filtered_types:
  340. self.printc("")
  341. self.printc("/* " + t.name + " */")
  342. self.printc(CGenerator.print_members(t))
  343. if len(self.filtered_types) > 0:
  344. self.printc(
  345. "const UA_DataType UA_%s[UA_%s_COUNT] = {" % (self.parser.outname.upper(), self.parser.outname.upper()))
  346. for t in self.filtered_types:
  347. self.printc("/* " + t.name + " */")
  348. self.printc(self.print_datatype(t) + ",")
  349. self.printc("};\n")
  350. def print_encoding(self):
  351. self.printe('''/* Generated from ''' + self.inname + ''' with script ''' + sys.argv[0] + '''
  352. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime(
  353. "%Y-%m-%d %I:%M:%S") + ''' */
  354. #ifdef UA_ENABLE_AMALGAMATION
  355. # include "open62541.h"
  356. #else
  357. # include "ua_types_encoding_binary.h"
  358. # include "''' + self.parser.outname + '''_generated.h"
  359. #endif
  360. ''')
  361. for t in self.filtered_types:
  362. self.printe("\n/* " + t.name + " */")
  363. self.printe(self.print_datatype_encoding(t))