generate_datatypes.py 28 KB

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