generate_datatypes.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660
  1. #!/usr/bin/env python
  2. # This Source Code Form is subject to the terms of the Mozilla Public
  3. # License, v. 2.0. If a copy of the MPL was not distributed with this
  4. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  5. from __future__ import print_function
  6. import sys
  7. import time
  8. import platform
  9. import getpass
  10. from collections import OrderedDict
  11. import re
  12. import xml.etree.ElementTree as etree
  13. import itertools
  14. import argparse
  15. import csv
  16. from nodeset_compiler.opaque_type_mapping import get_base_type_for_opaque
  17. types = OrderedDict() # contains types that were already parsed
  18. typedescriptions = {} # contains type nodeids
  19. excluded_types = ["NodeIdType", "InstanceNode", "TypeNode", "Node", "ObjectNode",
  20. "ObjectTypeNode", "VariableNode", "VariableTypeNode", "ReferenceTypeNode",
  21. "MethodNode", "ViewNode", "DataTypeNode",
  22. "NumericRange", "NumericRangeDimensions",
  23. "UA_ServerDiagnosticsSummaryDataType", "UA_SamplingIntervalDiagnosticsDataType",
  24. "UA_SessionSecurityDiagnosticsDataType", "UA_SubscriptionDiagnosticsDataType",
  25. "UA_SessionDiagnosticsDataType"]
  26. builtin_types = ["Boolean", "SByte", "Byte", "Int16", "UInt16", "Int32", "UInt32",
  27. "Int64", "UInt64", "Float", "Double", "String", "DateTime", "Guid",
  28. "ByteString", "XmlElement", "NodeId", "ExpandedNodeId", "StatusCode",
  29. "QualifiedName", "LocalizedText", "ExtensionObject", "DataValue",
  30. "Variant", "DiagnosticInfo"]
  31. # Some types can be memcpy'd off the binary stream. That's especially important
  32. # for arrays. But we need to check if they contain padding and whether the
  33. # endianness is correct. This dict gives the C-statement that must be true for the
  34. # type to be overlayable. Parsed types are added if they apply.
  35. builtin_overlayable = {"Boolean": "true",
  36. "SByte": "true", "Byte": "true",
  37. "Int16": "UA_BINARY_OVERLAYABLE_INTEGER",
  38. "UInt16": "UA_BINARY_OVERLAYABLE_INTEGER",
  39. "Int32": "UA_BINARY_OVERLAYABLE_INTEGER",
  40. "UInt32": "UA_BINARY_OVERLAYABLE_INTEGER",
  41. "Int64": "UA_BINARY_OVERLAYABLE_INTEGER",
  42. "UInt64": "UA_BINARY_OVERLAYABLE_INTEGER",
  43. "Float": "UA_BINARY_OVERLAYABLE_FLOAT",
  44. "Double": "UA_BINARY_OVERLAYABLE_FLOAT",
  45. "DateTime": "UA_BINARY_OVERLAYABLE_INTEGER",
  46. "StatusCode": "UA_BINARY_OVERLAYABLE_INTEGER",
  47. "Guid": "(UA_BINARY_OVERLAYABLE_INTEGER && " + \
  48. "offsetof(UA_Guid, data2) == sizeof(UA_UInt32) && " + \
  49. "offsetof(UA_Guid, data3) == (sizeof(UA_UInt16) + sizeof(UA_UInt32)) && " + \
  50. "offsetof(UA_Guid, data4) == (2*sizeof(UA_UInt32)))"}
  51. whitelistFuncAttrWarnUnusedResult = [ ] # for instances [ "String", "ByteString", "LocalizedText" ]
  52. # Type aliases
  53. type_aliases = { "CharArray" : "String" }
  54. def getTypeName(xmlTypeName):
  55. typeName = xmlTypeName[xmlTypeName.find(":")+1:]
  56. return type_aliases.get(typeName, typeName);
  57. # Escape C strings:
  58. def makeCLiteral(value):
  59. return re.sub(r'(?<!\\)"', r'\\"', value.replace('\\', r'\\\\').replace('\n', r'\\n').replace('\r', r''))
  60. # Strip invalid characters to create valid C identifiers (variable names etc):
  61. def makeCIdentifier(value):
  62. return re.sub(r'[^\w]', '', value)
  63. ################
  64. # Type Classes #
  65. ################
  66. class StructMember(object):
  67. def __init__(self, name, memberType, isArray):
  68. self.name = name
  69. self.memberType = memberType
  70. self.isArray = isArray
  71. def getNodeidTypeAndId(nodeId):
  72. if not '=' in nodeId:
  73. return "UA_NODEIDTYPE_NUMERIC, {{{0}}}".format(nodeId)
  74. if nodeId.startswith("i="):
  75. return "UA_NODEIDTYPE_NUMERIC, {{{0}}}".format(nodeId[2:])
  76. if nodeId.startswith("s="):
  77. strId = nodeId[2:]
  78. return "UA_NODEIDTYPE_STRING, {{ .string = UA_STRING_STATIC(\"{id}\") }}".format(id=strId.replace("\"", "\\\""))
  79. class Type(object):
  80. def __init__(self, outname, xml, namespace):
  81. self.name = None
  82. if xml is not None:
  83. self.name = xml.get("Name")
  84. self.typeIndex = makeCIdentifier(outname.upper() + "_" + self.name.upper())
  85. else:
  86. self.typeIndex = makeCIdentifier(outname.upper())
  87. self.ns0 = ("true" if namespace == 0 else "false")
  88. self.outname = outname
  89. self.description = ""
  90. self.pointerfree = "false"
  91. self.overlayable = "false"
  92. if self.name in builtin_types:
  93. self.builtin = "true"
  94. else:
  95. self.builtin = "false"
  96. self.members = [StructMember("", self, False)] # Returns one member: itself. Overwritten by some types.
  97. if xml is not None:
  98. for child in xml:
  99. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  100. self.description = child.text
  101. break
  102. def datatype_c(self):
  103. # xmlEncodingId = "0"
  104. binaryEncodingId = "0"
  105. if self.name in typedescriptions:
  106. description = typedescriptions[self.name]
  107. typeid = "{%s, %s}" % (description.namespaceid, getNodeidTypeAndId(description.nodeid))
  108. # xmlEncodingId = description.xmlEncodingId
  109. binaryEncodingId = description.binaryEncodingId
  110. else:
  111. typeid = "{0, UA_NODEIDTYPE_NUMERIC, {0}}"
  112. idName = makeCIdentifier(self.name)
  113. return "{\n UA_TYPENAME(\"%s\") /* .typeName */\n" % idName + \
  114. " " + typeid + ", /* .typeId */\n" + \
  115. " sizeof(UA_" + idName + "), /* .memSize */\n" + \
  116. " " + self.typeIndex + ", /* .typeIndex */\n" + \
  117. " " + str(len(self.members)) + ", /* .membersSize */\n" + \
  118. " " + self.builtin + ", /* .builtin */\n" + \
  119. " " + self.pointerfree + ", /* .pointerFree */\n" + \
  120. " " + self.overlayable + ", /* .overlayable */\n" + \
  121. " " + binaryEncodingId + ", /* .binaryEncodingId */\n" + \
  122. " %s_members" % idName + " /* .members */\n}"
  123. def members_c(self):
  124. idName = makeCIdentifier(self.name)
  125. if len(self.members)==0:
  126. return "#define %s_members NULL" % (idName)
  127. members = "static UA_DataTypeMember %s_members[%s] = {" % (idName, len(self.members))
  128. before = None
  129. i = 0
  130. size = len(self.members)
  131. for index, member in enumerate(self.members):
  132. i += 1
  133. memberName = makeCIdentifier(member.name)
  134. memberNameCapital = memberName
  135. if len(memberName) > 0:
  136. memberNameCapital = memberName[0].upper() + memberName[1:]
  137. m = "\n{\n UA_TYPENAME(\"%s\") /* .memberName */\n" % memberNameCapital
  138. m += " %s_%s, /* .memberTypeIndex */\n" % (member.memberType.outname.upper(), makeCIdentifier(member.memberType.name.upper()))
  139. m += " "
  140. if not before:
  141. m += "0,"
  142. else:
  143. if member.isArray:
  144. m += "offsetof(UA_%s, %sSize)" % (idName, memberName)
  145. else:
  146. m += "offsetof(UA_%s, %s)" % (idName, memberName)
  147. m += " - offsetof(UA_%s, %s)" % (idName, makeCIdentifier(before.name))
  148. if before.isArray:
  149. m += " - sizeof(void*),"
  150. else:
  151. m += " - sizeof(UA_%s)," % makeCIdentifier(before.memberType.name)
  152. m += " /* .padding */\n"
  153. m += " %s, /* .namespaceZero */\n" % member.memberType.ns0
  154. m += (" true" if member.isArray else " false") + " /* .isArray */\n}"
  155. if i != size:
  156. m += ","
  157. members += m
  158. before = member
  159. return members + "};"
  160. def datatype_ptr(self):
  161. return "&" + self.outname.upper() + "[" + makeCIdentifier(self.outname.upper() + "_" + self.name.upper()) + "]"
  162. def functions_c(self):
  163. idName = makeCIdentifier(self.name)
  164. funcs = "static UA_INLINE void\nUA_%s_init(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (idName, idName, idName)
  165. funcs += "static UA_INLINE UA_%s *\nUA_%s_new(void) {\n return (UA_%s*)UA_new(%s);\n}\n\n" % (idName, idName, idName, self.datatype_ptr())
  166. if self.pointerfree == "true":
  167. 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" % (idName, idName, idName)
  168. funcs += "static UA_INLINE void\nUA_%s_deleteMembers(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (idName, idName, idName)
  169. funcs += "static UA_INLINE void\nUA_%s_clear(UA_%s *p) {\n memset(p, 0, sizeof(UA_%s));\n}\n\n" % (idName, idName, idName)
  170. else:
  171. for entry in whitelistFuncAttrWarnUnusedResult:
  172. if idName == entry:
  173. funcs += "UA_INTERNAL_FUNC_ATTR_WARN_UNUSED_RESULT "
  174. break;
  175. 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" % (idName, idName, idName, self.datatype_ptr())
  176. funcs += "static UA_INLINE void\nUA_%s_deleteMembers(UA_%s *p) {\n UA_clear(p, %s);\n}\n\n" % (idName, idName, self.datatype_ptr())
  177. funcs += "static UA_INLINE void\nUA_%s_clear(UA_%s *p) {\n UA_clear(p, %s);\n}\n\n" % (idName, idName, self.datatype_ptr())
  178. funcs += "static UA_INLINE void\nUA_%s_delete(UA_%s *p) {\n UA_delete(p, %s);\n}" % (idName, idName, self.datatype_ptr())
  179. return funcs
  180. def encoding_h(self):
  181. idName = makeCIdentifier(self.name)
  182. enc = "static UA_INLINE size_t\nUA_%s_calcSizeBinary(const UA_%s *src) {\n return UA_calcSizeBinary(src, %s);\n}\n"
  183. 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"
  184. 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}"
  185. return enc % tuple(list(itertools.chain(*itertools.repeat([idName, idName, self.datatype_ptr()], 3))))
  186. class BuiltinType(Type):
  187. def __init__(self, name):
  188. Type.__init__(self, name, None, 0)
  189. self.name = name
  190. self.ns0 = "true"
  191. self.typeIndex = makeCIdentifier("UA_TYPES_" + self.name.upper())
  192. self.outname = "ua_types"
  193. self.description = ""
  194. self.pointerfree = "false"
  195. if self.name in builtin_overlayable.keys():
  196. self.pointerfree = "true"
  197. self.overlayable = "false"
  198. if name in builtin_overlayable:
  199. self.overlayable = builtin_overlayable[name]
  200. self.builtin = "true"
  201. self.members = [StructMember("", self, False)] # builtin types contain only one member: themselves (drops into the jumptable during processing)
  202. class EnumerationType(Type):
  203. def __init__(self, outname, xml, namespace):
  204. Type.__init__(self, outname, xml, namespace)
  205. self.pointerfree = "true"
  206. self.overlayable = "UA_BINARY_OVERLAYABLE_INTEGER"
  207. self.members = [StructMember("", types["Int32"], False)] # encoded as uint32
  208. self.builtin = "true"
  209. self.typeIndex = "UA_TYPES_INT32"
  210. self.elements = OrderedDict()
  211. for child in xml:
  212. if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
  213. self.elements[child.get("Name")] = child.get("Value")
  214. def typedef_h(self):
  215. if sys.version_info[0] < 3:
  216. values = self.elements.iteritems()
  217. else:
  218. values = self.elements.items()
  219. return "typedef enum {\n " + ",\n ".join(map(lambda kv : makeCIdentifier("UA_" + self.name.upper() + "_" + kv[0].upper()) + \
  220. " = " + kv[1], values)) + \
  221. ",\n __UA_{0}_FORCE32BIT = 0x7fffffff\n".format(makeCIdentifier(self.name.upper())) + "} " + \
  222. "UA_{0};\nUA_STATIC_ASSERT(sizeof(UA_{0}) == sizeof(UA_Int32), enum_must_be_32bit);".format(makeCIdentifier(self.name))
  223. class OpaqueType(Type):
  224. def __init__(self, outname, xml, namespace, baseType):
  225. Type.__init__(self, outname, xml, namespace)
  226. self.baseType = baseType
  227. self.members = [StructMember("", types[baseType], False)] # encoded as string
  228. def typedef_h(self):
  229. return "typedef UA_" + self.baseType + " UA_%s;" % self.name
  230. class StructType(Type):
  231. def __init__(self, outname, xml, namespace):
  232. Type.__init__(self, outname, xml, namespace)
  233. self.members = []
  234. lengthfields = [] # lengthfields of arrays are not included as members
  235. for child in xml:
  236. if child.get("LengthField"):
  237. lengthfields.append(child.get("LengthField"))
  238. for child in xml:
  239. if not child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  240. continue
  241. if child.get("Name") in lengthfields:
  242. continue
  243. memberName = child.get("Name")
  244. memberName = memberName[:1].lower() + memberName[1:]
  245. memberTypeName = getTypeName(child.get("TypeName"))
  246. memberType = types[memberTypeName]
  247. isArray = True if child.get("LengthField") else False
  248. self.members.append(StructMember(memberName, memberType, isArray))
  249. self.pointerfree = "true"
  250. self.overlayable = "true"
  251. before = None
  252. for m in self.members:
  253. if m.isArray or m.memberType.pointerfree != "true":
  254. self.pointerfree = "false"
  255. self.overlayable = "false"
  256. else:
  257. self.overlayable += "\n\t\t && " + m.memberType.overlayable
  258. if before:
  259. self.overlayable += "\n\t\t && offsetof(UA_%s, %s) == (offsetof(UA_%s, %s) + sizeof(UA_%s))" % \
  260. (makeCIdentifier(self.name), makeCIdentifier(m.name), makeCIdentifier(self.name), makeCIdentifier(before.name), makeCIdentifier(before.memberType.name))
  261. if "false" in self.overlayable:
  262. self.overlayable = "false"
  263. before = m
  264. def typedef_h(self):
  265. if len(self.members) == 0:
  266. return "typedef void * UA_%s;" % makeCIdentifier(self.name)
  267. returnstr = "typedef struct {\n"
  268. for member in self.members:
  269. if member.isArray:
  270. returnstr += " size_t %sSize;\n" % makeCIdentifier(member.name)
  271. returnstr += " UA_%s *%s;\n" % (makeCIdentifier(member.memberType.name), makeCIdentifier(member.name))
  272. else:
  273. returnstr += " UA_%s %s;\n" % (makeCIdentifier(member.memberType.name), makeCIdentifier(member.name))
  274. return returnstr + "} UA_%s;" % makeCIdentifier(self.name)
  275. #########################
  276. # Parse Typedefinitions #
  277. #########################
  278. def parseTypeDefinitions(outname, xmlDescription, namespace):
  279. def typeReady(element):
  280. "Are all member types defined?"
  281. for child in element:
  282. if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  283. childname = getTypeName(child.get("TypeName"))
  284. if childname not in types:
  285. return False
  286. return True
  287. def unknownTypes(element):
  288. "Return all unknown types"
  289. unknowns = []
  290. for child in element:
  291. if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  292. childname = getTypeName(child.get("TypeName"))
  293. if childname not in types:
  294. unknowns.append(childname)
  295. return unknowns
  296. def skipType(name):
  297. if name in excluded_types:
  298. return True
  299. if "Test" in name: # skip all test types
  300. return True
  301. if re.search("NodeId$", name) != None:
  302. return True
  303. return False
  304. snippets = {}
  305. for typeXml in etree.parse(xmlDescription).getroot():
  306. if not typeXml.get("Name"):
  307. continue
  308. name = typeXml.get("Name")
  309. snippets[name] = typeXml
  310. detectLoop = len(snippets)+1
  311. while(len(snippets) > 0):
  312. if detectLoop == len(snippets):
  313. name, typeXml = (snippets.items())[0]
  314. raise RuntimeError("Infinite loop detected trying to processing types " + name + ": unknonwn subtype " + str(unknownTypes(typeXml)))
  315. detectLoop = len(snippets)
  316. for name, typeXml in list(snippets.items()):
  317. if name in types or skipType(name):
  318. del snippets[name]
  319. continue
  320. if not typeReady(typeXml):
  321. continue
  322. if name in builtin_types:
  323. types[name] = BuiltinType(name)
  324. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  325. types[name] = EnumerationType(outname, typeXml, namespace)
  326. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  327. types[name] = OpaqueType(outname, typeXml, namespace, get_base_type_for_opaque(name)['name'])
  328. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  329. types[name] = StructType(outname, typeXml, namespace)
  330. else:
  331. raise Exception("Type not known")
  332. del snippets[name]
  333. ##########################
  334. # Parse TypeDescriptions #
  335. ##########################
  336. class TypeDescription(object):
  337. def __init__(self, name, nodeid, namespaceid):
  338. self.name = name
  339. self.nodeid = nodeid
  340. self.namespaceid = namespaceid
  341. self.xmlEncodingId = "0"
  342. self.binaryEncodingId = "0"
  343. def parseTypeDescriptions(f, namespaceid):
  344. definitions = {}
  345. csvreader = csv.reader(f, delimiter=',')
  346. delay_init = []
  347. for index, row in enumerate(csvreader):
  348. if len(row) < 3:
  349. continue
  350. if row[2] == "Object":
  351. # Check if node name ends with _Encoding_(DefaultXml|DefaultBinary) and store the node id in the corresponding DataType
  352. m = re.match('(.*?)_Encoding_Default(Xml|Binary)$',row[0])
  353. if (m):
  354. baseType = m.group(1)
  355. if baseType not in types:
  356. continue
  357. delay_init.append({
  358. "baseType": baseType,
  359. "encoding": m.group(2),
  360. "id": row[1]
  361. })
  362. continue
  363. if row[2] != "DataType":
  364. continue
  365. if row[0] == "BaseDataType":
  366. definitions["Variant"] = TypeDescription(row[0], row[1], namespaceid)
  367. elif row[0] == "Structure":
  368. definitions["ExtensionObject"] = TypeDescription(row[0], row[1], namespaceid)
  369. elif row[0] not in types:
  370. continue
  371. else:
  372. definitions[row[0]] = TypeDescription(row[0], row[1], namespaceid)
  373. for i in delay_init:
  374. if i["baseType"] not in definitions:
  375. raise Exception("Type {} not found in definitions file.".format(i["baseType"]))
  376. if i["encoding"] == "Xml":
  377. definitions[i["baseType"]].xmlEncodingId = i["id"]
  378. else:
  379. definitions[i["baseType"]].binaryEncodingId = i["id"]
  380. return definitions
  381. def merge_dicts(*dict_args):
  382. """
  383. Given any number of dicts, shallow copy and merge into a new dict,
  384. precedence goes to key value pairs in latter dicts.
  385. """
  386. result = {}
  387. for dictionary in dict_args:
  388. result.update(dictionary)
  389. return result
  390. ###############################
  391. # Parse the Command Line Input#
  392. ###############################
  393. parser = argparse.ArgumentParser()
  394. parser.add_argument('-c', '--type-csv',
  395. metavar="<typeDescriptions>",
  396. type=argparse.FileType('r'),
  397. dest="type_csv",
  398. action='append',
  399. default=[],
  400. help='csv file with type descriptions')
  401. parser.add_argument('--namespace',
  402. type=int,
  403. dest="namespace",
  404. default=0,
  405. help='namespace id of the generated type nodeids (defaults to 0)')
  406. parser.add_argument('-s', '--selected-types',
  407. metavar="<selectedTypes>",
  408. type=argparse.FileType('r'),
  409. dest="selected_types",
  410. action='append',
  411. default=[],
  412. help='file with list of types (among those parsed) to be generated. If not given, all types are generated')
  413. parser.add_argument('--no-builtin',
  414. action='store_true',
  415. dest="no_builtin",
  416. help='Do not generate builtin types')
  417. parser.add_argument('-t', '--type-bsd',
  418. metavar="<typeBsds>",
  419. type=argparse.FileType('r'),
  420. dest="type_bsd",
  421. action='append',
  422. default=[],
  423. help='bsd file with type definitions')
  424. parser.add_argument('outfile',
  425. metavar='<outputFile>',
  426. help='output file w/o extension')
  427. args = parser.parse_args()
  428. outname = args.outfile.split("/")[-1]
  429. inname = ', '.join(list(map(lambda x:x.name.split("/")[-1], args.type_bsd)))
  430. ################
  431. # Create Types #
  432. ################
  433. for builtin in builtin_types:
  434. types[builtin] = BuiltinType(builtin)
  435. for f in args.type_bsd:
  436. parseTypeDefinitions(outname, f, args.namespace)
  437. typedescriptions = {}
  438. for f in args.type_csv:
  439. typedescriptions = merge_dicts(typedescriptions, parseTypeDescriptions(f, args.namespace))
  440. # Read the selected data types
  441. selected_types = []
  442. for f in args.selected_types:
  443. selected_types += list(filter(len, [line.strip() for line in f]))
  444. # Use all types if none are selected
  445. if len(selected_types) == 0:
  446. selected_types = types.keys()
  447. #############################
  448. # Write out the Definitions #
  449. #############################
  450. fh = open(args.outfile + "_generated.h",'w')
  451. ff = open(args.outfile + "_generated_handling.h",'w')
  452. fe = open(args.outfile + "_generated_encoding_binary.h",'w')
  453. fc = open(args.outfile + "_generated.c",'w')
  454. def printh(string):
  455. print(string, end='\n', file=fh)
  456. def printf(string):
  457. print(string, end='\n', file=ff)
  458. def printe(string):
  459. print(string, end='\n', file=fe)
  460. def printc(string):
  461. print(string, end='\n', file=fc)
  462. def iter_types(v):
  463. l = None
  464. if sys.version_info[0] < 3:
  465. l = list(v.itervalues())
  466. else:
  467. l = list(v.values())
  468. if len(selected_types) > 0:
  469. l = list(filter(lambda t: t.name in selected_types, l))
  470. if args.no_builtin:
  471. l = list(filter(lambda t: type(t) != BuiltinType, l))
  472. return l
  473. ################
  474. # Print Header #
  475. ################
  476. printh('''/* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  477. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + \
  478. ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + ''' */
  479. #ifndef ''' + outname.upper() + '''_GENERATED_H_
  480. #define ''' + outname.upper() + '''_GENERATED_H_
  481. #ifdef UA_NO_AMALGAMATION
  482. #include "ua_types.h"
  483. ''' + ('#include "ua_types_generated.h"\n' if outname != "ua_types" else '') + '''
  484. #else
  485. #include "open62541.h"
  486. #endif
  487. _UA_BEGIN_DECLS
  488. ''')
  489. filtered_types = iter_types(types)
  490. printh('''/**
  491. * Every type is assigned an index in an array containing the type descriptions.
  492. * These descriptions are used during type handling (copying, deletion,
  493. * binary encoding, ...). */''')
  494. printh("#define " + outname.upper() + "_COUNT %s" % (str(len(filtered_types))))
  495. printh("extern UA_EXPORT const UA_DataType " + outname.upper() + "[" + outname.upper() + "_COUNT];")
  496. i = 0
  497. for t in filtered_types:
  498. printh("\n/**\n * " + t.name)
  499. printh(" * " + "^" * len(t.name))
  500. if t.description == "":
  501. printh(" */")
  502. else:
  503. printh(" * " + t.description + " */")
  504. if type(t) != BuiltinType:
  505. printh(t.typedef_h() + "\n")
  506. printh("#define " + makeCIdentifier(outname.upper() + "_" + t.name.upper()) + " " + str(i))
  507. i += 1
  508. printh('''
  509. _UA_END_DECLS
  510. #endif /* %s_GENERATED_H_ */''' % outname.upper())
  511. ##################
  512. # Print Handling #
  513. ##################
  514. printf('''/* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  515. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + \
  516. ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + ''' */
  517. #ifndef ''' + outname.upper() + '''_GENERATED_HANDLING_H_
  518. #define ''' + outname.upper() + '''_GENERATED_HANDLING_H_
  519. #include "''' + outname + '''_generated.h"
  520. _UA_BEGIN_DECLS
  521. #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
  522. # pragma GCC diagnostic push
  523. # pragma GCC diagnostic ignored "-Wmissing-field-initializers"
  524. # pragma GCC diagnostic ignored "-Wmissing-braces"
  525. #endif
  526. ''')
  527. for t in filtered_types:
  528. printf("\n/* " + t.name + " */")
  529. printf(t.functions_c())
  530. printf('''
  531. #if defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 6
  532. # pragma GCC diagnostic pop
  533. #endif
  534. _UA_END_DECLS
  535. #endif /* %s_GENERATED_HANDLING_H_ */''' % outname.upper())
  536. ###########################
  537. # Print Description Array #
  538. ###########################
  539. printc('''/* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  540. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + \
  541. ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + ''' */
  542. #include "''' + outname + '''_generated.h"''')
  543. for t in filtered_types:
  544. printc("")
  545. printc("/* " + t.name + " */")
  546. printc(t.members_c())
  547. printc("const UA_DataType %s[%s_COUNT] = {" % (outname.upper(), outname.upper()))
  548. for t in filtered_types:
  549. # printc("")
  550. printc("/* " + t.name + " */")
  551. printc(t.datatype_c() + ",")
  552. printc("};\n")
  553. ##################
  554. # Print Encoding #
  555. ##################
  556. printe('''/* Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  557. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + \
  558. ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + ''' */
  559. #include "ua_types_encoding_binary.h"
  560. #include "''' + outname + '''_generated.h"''')
  561. for t in filtered_types:
  562. printe("\n/* " + t.name + " */")
  563. printe(t.encoding_h())
  564. fh.close()
  565. ff.close()
  566. fc.close()
  567. fe.close()