generate_datatypes.py 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  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 itertools
  10. import argparse
  11. from pprint import pprint
  12. fixed_size = {"UA_Boolean": 1, "UA_SByte": 1, "UA_Byte": 1, "UA_Int16": 2, "UA_UInt16": 2,
  13. "UA_Int32": 4, "UA_UInt32": 4, "UA_Int64": 8, "UA_UInt64": 8, "UA_Float": 4,
  14. "UA_Double": 8, "UA_DateTime": 8, "UA_Guid": 16, "UA_StatusCode": 4}
  15. zero_copy = ["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16", "UA_Int32", "UA_UInt32",
  16. "UA_Int64", "UA_UInt64", "UA_Float", "UA_Double", "UA_DateTime", "UA_StatusCode"]
  17. # The order of the builtin-types is not as in the standard. We put all the
  18. # fixed_size types in the front, so they can be distinguished by a simple geq
  19. # comparison. That's ok, since we use the type-index only internally!!
  20. builtin_types = ["UA_Boolean", "UA_SByte", "UA_Byte", "UA_Int16", "UA_UInt16",
  21. "UA_Int32", "UA_UInt32", "UA_Int64", "UA_UInt64", "UA_Float",
  22. "UA_Double", "UA_String", "UA_DateTime", "UA_Guid", "UA_ByteString",
  23. "UA_XmlElement", "UA_NodeId", "UA_ExpandedNodeId", "UA_StatusCode",
  24. "UA_QualifiedName", "UA_LocalizedText", "UA_ExtensionObject", "UA_DataValue",
  25. "UA_Variant", "UA_DiagnosticInfo"]
  26. excluded_types = ["UA_NodeIdType", "UA_InstanceNode", "UA_TypeNode", "UA_Node", "UA_ObjectNode",
  27. "UA_ObjectTypeNode", "UA_VariableNode", "UA_VariableTypeNode", "UA_ReferenceTypeNode",
  28. "UA_MethodNode", "UA_ViewNode", "UA_DataTypeNode", "UA_ServerDiagnosticsSummaryDataType",
  29. "UA_SamplingIntervalDiagnosticsDataType", "UA_SessionSecurityDiagnosticsDataType",
  30. "UA_SubscriptionDiagnosticsDataType", "UA_SessionDiagnosticsDataType"]
  31. minimal_types = ["InvalidType", "Node", "NodeClass", "ReferenceNode", "ApplicationDescription", "ApplicationType",
  32. "ChannelSecurityToken", "OpenSecureChannelRequest", "OpenSecureChannelResponse",
  33. "CloseSecureChannelRequest", "CloseSecureChannelResponse", "RequestHeader", "ResponseHeader",
  34. "SecurityTokenRequestType", "MessageSecurityMode", "CloseSessionResponse", "CloseSessionRquest",
  35. "ActivateSessionRequest", "ActivateSessionResponse", "SignatureData", "SignedSoftwareCertificate",
  36. "CreateSessionResponse", "CreateSessionRequest", "EndpointDescription", "UserTokenPolicy", "UserTokenType",
  37. "GetEndpointsRequest", "GetEndpointsResponse", "PublishRequest", "PublishResponse", "FindServersRequest", "FindServersResponse",
  38. "SetPublishingModeResponse", "SubscriptionAcknowledgement", "NotificationMessage", "ExtensionObject",
  39. "Structure", "ReadRequest", "ReadResponse", "ReadValueId", "TimestampsToReturn", "WriteRequest",
  40. "WriteResponse", "WriteValue", "SetPublishingModeRequest", "CreateMonitoredItemsResponse",
  41. "MonitoredItemCreateResult", "CreateMonitoredItemsRequest", "MonitoredItemCreateRequest",
  42. "MonitoringMode", "MonitoringParameters", "TranslateBrowsePathsToNodeIdsRequest",
  43. "TranslateBrowsePathsToNodeIdsResponse", "BrowsePath", "BrowsePathResult", "RelativePath",
  44. "BrowsePathTarget", "RelativePathElement", "CreateSubscriptionRequest", "CreateSubscriptionResponse",
  45. "BrowseResponse", "BrowseResult", "ReferenceDescription", "BrowseRequest", "ViewDescription",
  46. "BrowseNextRequest", "BrowseNextResponse", "DeleteSubscriptionsRequest", "DeleteSubscriptionsResponse",
  47. "BrowseDescription", "BrowseDirection", "CloseSessionRequest", "AddNodesRequest", "AddNodesResponse",
  48. "AddNodesItem", "AddNodesResult", "DeleteNodesItem","AddReferencesRequest", "AddReferencesResponse",
  49. "AddReferencesItem","DeleteReferencesItem", "VariableNode", "MethodNode", "VariableTypeNode",
  50. "ViewNode", "ReferenceTypeNode", "BrowseResultMask", "ServerState", "ServerStatusDataType", "BuildInfo",
  51. "ObjectNode", "DataTypeNode", "ObjectTypeNode", "IdType", "NodeAttributes",
  52. "VariableAttributes", "ObjectAttributes", "ReferenceTypeAttributes", "ViewAttributes", "MethodAttributes",
  53. "ObjectTypeAttributes", "VariableTypeAttributes", "DataTypeAttributes", "NodeAttributesMask",
  54. "DeleteNodesItem", "DeleteNodesRequest", "DeleteNodesResponse",
  55. "DeleteReferencesItem", "DeleteReferencesRequest", "DeleteReferencesResponse",
  56. "RegisterNodesRequest", "RegisterNodesResponse", "UnregisterNodesRequest", "UnregisterNodesResponse",
  57. "UserIdentityToken", "UserNameIdentityToken", "AnonymousIdentityToken", "ServiceFault",
  58. "CallMethodRequest", "CallMethodResult", "CallResponse", "CallRequest", "Argument"]
  59. subscription_types = ["DeleteMonitoredItemsRequest", "DeleteMonitoredItemsResponse", "NotificationMessage",
  60. "MonitoredItemNotification", "DataChangeNotification", "ModifySubscriptionRequest",
  61. "ModifySubscriptionResponse"]
  62. class TypeDescription(object):
  63. def __init__(self, name, nodeid, namespaceid):
  64. self.name = name # without the UA_ prefix
  65. self.nodeid = nodeid
  66. self.namespaceid = namespaceid
  67. def parseTypeDescriptions(filename, namespaceid):
  68. definitions = {}
  69. f = open(filename[0])
  70. input_str = f.read()
  71. f.close()
  72. input_str = input_str.replace('\r','')
  73. rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
  74. for index, row in enumerate(rows):
  75. if len(row) < 3:
  76. continue
  77. if row[2] != "DataType":
  78. continue
  79. if row[0] == "BaseDataType":
  80. definitions["UA_Variant"] = TypeDescription(row[0], row[1], namespaceid)
  81. elif row[0] == "Structure":
  82. definitions["UA_ExtensionObject"] = TypeDescription(row[0], row[1], namespaceid)
  83. else:
  84. definitions["UA_" + row[0]] = TypeDescription(row[0], row[1], namespaceid)
  85. return definitions
  86. class BuiltinType(object):
  87. "Generic type without members. Used for builtin types."
  88. def __init__(self, name, description = ""):
  89. self.name = name
  90. self.description = description
  91. def fixed_size(self):
  92. return self.name in fixed_size
  93. def mem_size(self):
  94. return fixed_size[self.name]
  95. def zero_copy(self):
  96. return self.name in zero_copy
  97. def typedef_c(self):
  98. pass
  99. def typelayout_c(self, namespace_0, description, outname):
  100. if description == None:
  101. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  102. else:
  103. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % \
  104. (description.namespaceid, description.nodeid)
  105. if self.name in ["UA_String", "UA_ByteString", "UA_XmlElement"]:
  106. return "{.typeId = " + typeid + \
  107. ".memSize = sizeof(" + self.name + "), " + \
  108. ".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  109. ".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTE, .namespaceZero = UA_TRUE, " + \
  110. ".padding = offsetof(UA_String, data) - sizeof(UA_Int32), .isArray = UA_TRUE }}, " + \
  111. ".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
  112. if self.name == "UA_QualifiedName":
  113. return "{.typeId = " + typeid + \
  114. ".memSize = sizeof(UA_QualifiedName), " + \
  115. ".namespaceZero = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  116. ".membersSize = 2, .members = {" + \
  117. "\n\t{.memberTypeIndex = UA_TYPES_UINT16, .namespaceZero = UA_TRUE, " + \
  118. ".padding = 0, .isArray = UA_FALSE }," + \
  119. "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
  120. ".padding = offsetof(UA_QualifiedName, name) - sizeof(UA_UInt16), .isArray = UA_FALSE }},\n" + \
  121. ".typeIndex = UA_TYPES_QUALIFIEDNAME }"
  122. return "{.typeId = " + typeid + \
  123. ".memSize = sizeof(" + self.name + "), " + \
  124. ".namespaceZero = UA_TRUE, " + \
  125. ".fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
  126. ", .zeroCopyable = " + ("UA_TRUE" if self.zero_copy() else "UA_FALSE") + \
  127. ", .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_" + self.name[3:].upper() + "," + \
  128. ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, " + \
  129. ".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
  130. class EnumerationType(object):
  131. def __init__(self, name, description = "", elements = OrderedDict()):
  132. self.name = name
  133. self.description = description
  134. self.elements = elements # maps a name to an integer value
  135. def append_enum(name, value):
  136. self.elements[name] = value
  137. def fixed_size(self):
  138. return True
  139. def mem_size(self):
  140. return 4
  141. def zero_copy(self):
  142. return True
  143. def typedef_c(self):
  144. if sys.version_info[0] < 3:
  145. values = self.elements.iteritems()
  146. else:
  147. values = self.elements.items()
  148. return "typedef enum { \n " + \
  149. ",\n ".join(map(lambda kv : kv[0].upper() + " = " + kv[1], values)) + \
  150. "\n} " + self.name + ";"
  151. def typelayout_c(self, namespace_0, description, outname):
  152. if description == None:
  153. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  154. else:
  155. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  156. return "{.typeId = " + typeid + \
  157. ".memSize = sizeof(" + self.name + "), " +\
  158. ".namespaceZero = UA_TRUE, " + \
  159. ".fixedSize = UA_TRUE, .zeroCopyable = UA_TRUE, " + \
  160. ".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_INT32," + \
  161. ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, .typeIndex = UA_TYPES_INT32 }"
  162. def functions_c(self, typeTableName):
  163. return '''static UA_INLINE %s * %s_new(void) { %s *v = (%s*)UA_Int32_new(); return v; }
  164. static UA_INLINE void %s_init(%s *p) { UA_Int32_init((UA_Int32*)p); }
  165. static UA_INLINE void %s_delete(%s *p) { UA_Int32_delete((UA_Int32*)p); }
  166. static UA_INLINE void %s_deleteMembers(%s *p) { }
  167. static UA_INLINE UA_StatusCode %s_copy(const %s *src, %s *dst) { return UA_Int32_copy((const UA_Int32*)src, (UA_Int32*)dst); }''' % tuple(itertools.repeat(self.name, 13))
  168. def encoding_h(self, typeTableName):
  169. return '''static UA_INLINE UA_StatusCode %s_encodeBinary(const %s *src, UA_ByteString *dst, size_t *offset) { return UA_Int32_encodeBinary((const UA_Int32*)src, dst, offset); }
  170. static UA_INLINE UA_StatusCode %s_decodeBinary(const UA_ByteString *src, size_t *offset, %s *dst) { return UA_Int32_decodeBinary(src, offset, (UA_Int32*)dst); }''' % tuple(itertools.repeat(self.name, 4))
  171. class OpaqueType(object):
  172. def __init__(self, name, description = ""):
  173. self.name = name
  174. self.description = description
  175. def fixed_size(self):
  176. return False
  177. def zero_copy(self):
  178. return False
  179. def typedef_c(self):
  180. return "typedef UA_ByteString " + self.name + ";"
  181. def typelayout_c(self, namespace_0, description, outname):
  182. if description == None:
  183. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  184. else:
  185. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  186. return "{.typeId = " + typeid + \
  187. ".memSize = sizeof(" + self.name + "), .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  188. ".namespaceZero = UA_TRUE, .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTESTRING," + \
  189. ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, .typeIndex = UA_TYPES_BYTESTRING }"
  190. def functions_c(self, typeTableName):
  191. return '''static UA_INLINE %s * %s_new(void) { %s *v = (%s*)UA_ByteString_new(); return v; }
  192. static UA_INLINE void %s_init(%s *p) { UA_ByteString_init((UA_ByteString*)p); }
  193. static UA_INLINE void %s_delete(%s *p) { UA_ByteString_delete((UA_ByteString*)p); }
  194. static UA_INLINE void %s_deleteMembers(%s *p) { }
  195. static UA_INLINE UA_StatusCode %s_copy(const %s *src, %s *dst) { return UA_ByteString_copy((const UA_ByteString*)src, (UA_ByteString*)dst); }''' % tuple(itertools.repeat(self.name, 13))
  196. def encoding_h(self, typeTableName):
  197. return '''static UA_INLINE UA_StatusCode %s_encodeBinary(const %s *src, UA_ByteString *dst, size_t *offset) { return UA_ByteString_encodeBinary((UA_ByteString*)src, dst, offset); }
  198. static UA_INLINE UA_StatusCode %s_decodeBinary(const UA_ByteString *src, size_t *offset, %s *dst) { return UA_ByteString_decodeBinary(src, offset, (UA_ByteString*)dst); }''' % tuple(itertools.repeat(self.name, 4))
  199. class StructMember(object):
  200. def __init__(self, name, memberType, isArray):
  201. self.name = name
  202. self.memberType = memberType
  203. self.isArray = isArray
  204. class StructType(object):
  205. def __init__(self, name, description, members = OrderedDict()):
  206. self.name = name
  207. self.description = description
  208. self.members = members # maps a name to a member definition
  209. def fixed_size(self):
  210. for m in self.members.values():
  211. if m.isArray or not m.memberType.fixed_size():
  212. return False
  213. return True
  214. def mem_size(self):
  215. total = 0
  216. for m in self.members.values():
  217. if m.isArray:
  218. raise Exception("Arrays have no fixed size!")
  219. else:
  220. total += m.memberType.mem_size()
  221. return total
  222. def zero_copy(self):
  223. for m in self.members.values():
  224. if m.isArray or not m.memberType.zero_copy():
  225. return False
  226. return True
  227. def typedef_c(self):
  228. if len(self.members) == 0:
  229. return "typedef void * " + self.name + ";"
  230. returnstr = "typedef struct {\n"
  231. if sys.version_info[0] < 3:
  232. values = self.members.iteritems()
  233. else:
  234. values = self.members.items()
  235. for name, member in values:
  236. if member.isArray:
  237. returnstr += " UA_Int32 " + name + "Size;\n"
  238. returnstr += " " + member.memberType.name + " *" +name + ";\n"
  239. else:
  240. returnstr += " " + member.memberType.name + " " +name + ";\n"
  241. return returnstr + "} " + self.name + ";"
  242. def typelayout_c(self, namespace_0, description, outname):
  243. if description == None:
  244. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  245. else:
  246. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  247. layout = "{.typeId = "+ typeid + \
  248. ".memSize = sizeof(" + self.name + "), "+ \
  249. ".namespaceZero = " + ("UA_TRUE" if namespace_0 else "UA_FALSE") + \
  250. ", .fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
  251. ", .zeroCopyable = " + ("sizeof(" + self.name + ") == " + str(self.mem_size()) if self.zero_copy() \
  252. else "UA_FALSE") + \
  253. ", .typeIndex = " + outname.upper() + "_" + self.name[3:].upper() + \
  254. ", .membersSize = " + str(len(self.members)) + ","
  255. if len(self.members) > 0:
  256. layout += "\n\t.members={"
  257. for index, member in enumerate(self.members.values()):
  258. layout += "\n\t{" + \
  259. ".memberTypeIndex = " + ("UA_TYPES_" + member.memberType.name[3:].upper() if args.namespace_id == 0 or member.memberType.name in existing_types else \
  260. outname.upper() + "_" + member.memberType.name[3:].upper()) + ", " + \
  261. ".namespaceZero = "+ \
  262. ("UA_TRUE, " if args.namespace_id == 0 or member.memberType.name in existing_types else "UA_FALSE, ") + \
  263. ".padding = "
  264. before_endpos = "0"
  265. thispos = "offsetof(%s, %s)" % (self.name, member.name)
  266. if index > 0:
  267. if sys.version_info[0] < 3:
  268. before = self.members.values()[index-1]
  269. else:
  270. before = list(self.members.values())[index-1]
  271. before_endpos = "(offsetof(%s, %s)" % (self.name, before.name)
  272. if before.isArray:
  273. before_endpos += " + sizeof(void*))"
  274. else:
  275. before_endpos += " + sizeof(%s))" % before.memberType.name
  276. if member.isArray:
  277. # the first two bytes are padding for the length index, the last three for the pointer
  278. length_pos = "offsetof(%s, %sSize)" % (self.name, member.name)
  279. if index != 0:
  280. layout += "((%s - %s) << 3) + " % (length_pos, before_endpos)
  281. layout += "(%s - sizeof(UA_Int32) - %s)" % (thispos, length_pos)
  282. else:
  283. layout += "%s - %s" % (thispos, before_endpos)
  284. layout += ", .isArray = " + ("UA_TRUE" if member.isArray else "UA_FALSE") + " }, "
  285. layout += "}"
  286. return layout + "}"
  287. def functions_c(self, typeTableName):
  288. return '''static UA_INLINE void %s_init(%s *p) { UA_init(p, %s); }
  289. static UA_INLINE void %s_delete(%s *p) { UA_delete(p, %s); }
  290. static UA_INLINE void %s_deleteMembers(%s *p) { UA_deleteMembers(p, %s); }
  291. static UA_INLINE %s * %s_new(void) { return (%s *) UA_new(%s); }
  292. static UA_INLINE UA_StatusCode %s_copy(const %s *src, %s *dst) { return UA_copy(src, dst, %s); }''' % \
  293. tuple( list(itertools.chain(*itertools.repeat([self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 3)))
  294. + list(itertools.chain(*itertools.repeat([self.name, self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 2))) )
  295. def encoding_h(self, typeTableName):
  296. return '''static UA_INLINE UA_StatusCode %s_encodeBinary(const %s *src, UA_ByteString *dst, size_t *offset) { return UA_encodeBinary(src, %s, dst, offset); }
  297. static UA_INLINE UA_StatusCode %s_decodeBinary(const UA_ByteString *src, size_t *offset, %s *dst) { return UA_decodeBinary(src, offset, dst, %s); }''' % \
  298. tuple(list(itertools.chain(*itertools.repeat([self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 2))))
  299. def parseTypeDefinitions(xmlDescription, existing_types = OrderedDict()):
  300. '''Returns an ordered dict that maps names to types. The order is such that
  301. every type depends only on known types. '''
  302. ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
  303. tree = etree.parse(xmlDescription)
  304. typeSnippets = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
  305. types = OrderedDict(existing_types.items())
  306. # types we do not want to autogenerate
  307. def skipType(name):
  308. if name in builtin_types:
  309. return True
  310. if name in excluded_types:
  311. return True
  312. if outname == "ua_types" and not name[3:] in minimal_types:
  313. return True
  314. if "Test" in name: # skip all test types
  315. return True
  316. if re.search("NodeId$", name) != None:
  317. return True
  318. return False
  319. def stripTypename(tn):
  320. return tn[tn.find(":")+1:]
  321. def camlCase2CCase(item):
  322. "Member names begin with a lower case character"
  323. return item[:1].lower() + item[1:] if item else ''
  324. def typeReady(element):
  325. "Do we have the member types yet?"
  326. for child in element:
  327. if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  328. if stripTypename(child.get("TypeName")) not in types:
  329. return False
  330. return True
  331. def parseEnumeration(typeXml):
  332. name = "UA_" + typeXml.get("Name")
  333. description = ""
  334. elements = OrderedDict()
  335. for child in typeXml:
  336. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  337. description = child.text
  338. if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
  339. elements[name + "_" + child.get("Name")] = child.get("Value")
  340. return EnumerationType(name, description, elements)
  341. def parseOpaque(typeXml):
  342. name = "UA_" + typeXml.get("Name")
  343. description = ""
  344. for child in typeXml:
  345. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  346. description = child.text
  347. return OpaqueType(name, description)
  348. def parseStructured(typeXml):
  349. "Returns None if we miss member descriptions"
  350. name = "UA_" + typeXml.get("Name")
  351. description = ""
  352. for child in typeXml:
  353. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  354. description = child.text
  355. # ignore lengthfields, just tag the array-members as an array
  356. lengthfields = []
  357. for child in typeXml:
  358. if child.get("LengthField"):
  359. lengthfields.append(child.get("LengthField"))
  360. members = OrderedDict()
  361. for child in typeXml:
  362. if not child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  363. continue
  364. if child.get("Name") in lengthfields:
  365. continue
  366. memberTypeName = "UA_" + stripTypename(child.get("TypeName"))
  367. if not memberTypeName in types:
  368. return None
  369. memberType = types[memberTypeName]
  370. memberName = camlCase2CCase(child.get("Name"))
  371. isArray = True if child.get("LengthField") else False
  372. members[memberName] = StructMember(memberName, memberType, isArray)
  373. return StructType(name, description, members)
  374. finished = False
  375. while(not finished):
  376. finished = True
  377. for typeXml in typeSnippets:
  378. name = "UA_" + typeXml.get("Name")
  379. if name in types or skipType(name):
  380. continue
  381. if typeXml.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  382. t = parseEnumeration(typeXml)
  383. types[t.name] = t
  384. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  385. t = parseOpaque(typeXml)
  386. types[t.name] = t
  387. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  388. t = parseStructured(typeXml)
  389. if t == None:
  390. finished = False
  391. else:
  392. types[t.name] = t
  393. # remove the existing types
  394. for k in existing_types.keys():
  395. types.pop(k)
  396. return types
  397. parser = argparse.ArgumentParser()
  398. parser.add_argument('--ns0-types-xml', nargs=1, help='xml-definition of the ns0 types that are assumed to already exist')
  399. parser.add_argument('--enable-subscription-types', nargs=1, help='Generate datatypes necessary for Montoring and Subscriptions.')
  400. parser.add_argument('--typedescriptions', nargs=1, help='csv file with type descriptions')
  401. parser.add_argument('namespace_id', type=int, help='the id of the target namespace')
  402. parser.add_argument('types_xml', help='path/to/Opc.Ua.Types.bsd')
  403. parser.add_argument('outfile', help='output file w/o extension')
  404. args = parser.parse_args()
  405. outname = args.outfile.split("/")[-1]
  406. inname = args.types_xml.split("/")[-1]
  407. existing_types = OrderedDict()
  408. if args.enable_subscription_types:
  409. minimal_types = minimal_types + subscription_types
  410. if args.namespace_id == 0 or args.ns0_types_xml:
  411. existing_types = OrderedDict([(t, BuiltinType(t)) for t in builtin_types])
  412. if args.ns0_types_xml:
  413. if sys.version_info[0] < 3:
  414. OrderedDict(existing_types.items() + parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items())
  415. else:
  416. OrderedDict(list(existing_types.items()) + list(parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items()))
  417. types = parseTypeDefinitions(args.types_xml, existing_types)
  418. if args.namespace_id == 0:
  419. if sys.version_info[0] < 3:
  420. types = OrderedDict(existing_types.items() + types.items())
  421. else:
  422. types = OrderedDict(list(existing_types.items()) + list(types.items()))
  423. typedescriptions = {}
  424. if args.typedescriptions:
  425. typedescriptions = parseTypeDescriptions(args.typedescriptions, args.namespace_id)
  426. fh = open(args.outfile + "_generated.h",'w')
  427. fe = open(args.outfile + "_generated_encoding_binary.h",'w')
  428. fc = open(args.outfile + "_generated.c",'w')
  429. def printh(string):
  430. print(string, end='\n', file=fh)
  431. def printe(string):
  432. print(string, end='\n', file=fe)
  433. def printc(string):
  434. print(string, end='\n', file=fc)
  435. printh('''/**
  436. * @file ''' + outname + '''_generated.h
  437. *
  438. * @brief Autogenerated data types
  439. *
  440. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  441. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  442. */
  443. #ifndef ''' + outname.upper() + '''_GENERATED_H_
  444. #define ''' + outname.upper() + '''_GENERATED_H_
  445. #ifdef __cplusplus
  446. extern "C" {
  447. #endif
  448. #include "ua_types.h"
  449. #ifdef UA_INTERNAL
  450. #include "ua_types_encoding_binary.h"
  451. #endif'''
  452. + ('\n#include "ua_types_generated.h"\n' if args.namespace_id != 0 else '') + '''
  453. /**
  454. * @ingroup types
  455. *
  456. * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
  457. *
  458. * @brief Data structures that are autogenerated from an XML-Schema.
  459. *
  460. * @{
  461. */
  462. ''')
  463. printh("#define " + outname.upper() + "_COUNT %s\n" % (str(len(types))))
  464. printh("extern UA_EXPORT const UA_DataType *" + outname.upper() + ";\n")
  465. i = 0
  466. if sys.version_info[0] < 3:
  467. values = types.itervalues()
  468. else:
  469. values = types.values()
  470. for t in values:
  471. if type(t) != BuiltinType:
  472. printh("")
  473. if t.description != "":
  474. printh("/** @brief " + t.description + " */")
  475. printh(t.typedef_c())
  476. printh("#define " + outname.upper() + "_" + t.name[3:].upper() + " " + str(i))
  477. if type(t) != BuiltinType:
  478. printh(t.functions_c(outname.upper()))
  479. i += 1
  480. printh('''
  481. /// @} /* end of group */\n
  482. #ifdef __cplusplus
  483. } // extern "C"
  484. #endif\n
  485. #endif /* %s_GENERATED_H_ */''' % outname.upper())
  486. printe('''/**
  487. * @file ''' + outname + '''_generated_encoding_binary.h
  488. *
  489. * @brief Binary encoding for autogenerated data types
  490. *
  491. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  492. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  493. */\n
  494. #include "ua_types_encoding_binary.h"
  495. #include "''' + outname + '''_generated.h"''')
  496. printc('''/**
  497. * @file ''' + outname + '''_generated.c
  498. *
  499. * @brief Autogenerated data types
  500. *
  501. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  502. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  503. */\n
  504. #include "stddef.h"
  505. #include "ua_types.h"
  506. #include "''' + outname + '''_generated.h"\n
  507. const UA_DataType *''' + outname.upper() + ''' = (UA_DataType[]){''')
  508. if sys.version_info[0] < 3:
  509. values = types.itervalues()
  510. else:
  511. values = types.values()
  512. for t in values:
  513. printc("")
  514. printc("/* " + t.name + " */")
  515. if args.typedescriptions:
  516. td = typedescriptions[t.name]
  517. else:
  518. td = None
  519. printc(t.typelayout_c(args.namespace_id == 0, td, outname) + ",")
  520. if not type(t) == BuiltinType:
  521. printe("")
  522. printe("/* " + t.name + " */")
  523. printe(t.encoding_h(outname.upper()))
  524. printc("};\n")
  525. # if args.typedescriptions:
  526. # printc('const UA_UInt32 *' + outname.upper() + '_IDS = (UA_UInt32[]){')
  527. # for t in types.itervalues():
  528. # print(str(typedescriptions[t.name].nodeid) + ", ", end='', file=fc)
  529. # printc("};")
  530. fh.close()
  531. fe.close()
  532. fc.close()