generate_datatypes.py 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611
  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",
  38. "FindServersResponse", "SetPublishingModeResponse", "SubscriptionAcknowledgement", "NotificationMessage",
  39. "ExtensionObject", "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. "FilterOperator", "ContentFilterElement", "ContentFilter", "QueryDataDescription",
  60. "NodeTypeDescription", "QueryFirstRequest", "QueryDataSet", "ParsingResult",
  61. "ContentFilterElementResult", "ContentFilterResult", "QueryFirstResponse",
  62. "QueryNextRequest", "QueryNextResponse"]
  63. subscription_types = ["CreateSubscriptionRequest", "CreateSubscriptionResponse",
  64. "DeleteMonitoredItemsRequest", "DeleteMonitoredItemsResponse", "NotificationMessage",
  65. "MonitoredItemNotification", "DataChangeNotification", "ModifySubscriptionRequest",
  66. "ModifySubscriptionResponse", "RepublishRequest", "RepublishResponse"]
  67. class TypeDescription(object):
  68. def __init__(self, name, nodeid, namespaceid):
  69. self.name = name # without the UA_ prefix
  70. self.nodeid = nodeid
  71. self.namespaceid = namespaceid
  72. def parseTypeDescriptions(filename, namespaceid):
  73. definitions = {}
  74. f = open(filename[0])
  75. input_str = f.read()
  76. f.close()
  77. input_str = input_str.replace('\r','')
  78. rows = map(lambda x:tuple(x.split(',')), input_str.split('\n'))
  79. for index, row in enumerate(rows):
  80. if len(row) < 3:
  81. continue
  82. if row[2] != "DataType":
  83. continue
  84. if row[0] == "BaseDataType":
  85. definitions["UA_Variant"] = TypeDescription(row[0], row[1], namespaceid)
  86. elif row[0] == "Structure":
  87. definitions["UA_ExtensionObject"] = TypeDescription(row[0], row[1], namespaceid)
  88. else:
  89. definitions["UA_" + row[0]] = TypeDescription(row[0], row[1], namespaceid)
  90. return definitions
  91. class Type(object):
  92. def __init__(self, name, description = ""):
  93. self.name = name
  94. self.description = description
  95. def fixed_size(self):
  96. return self.name in fixed_size
  97. def mem_size(self):
  98. return fixed_size[self.name]
  99. def zero_copy(self):
  100. return self.name in zero_copy
  101. def typedef_c(self):
  102. pass
  103. def functions_c(self, typeTableName):
  104. return ('''static UA_INLINE void %s_init(%s *p) { memset(p, 0, sizeof(%s)); }
  105. static UA_INLINE void %s_delete(%s *p) { UA_delete(p, %s); }
  106. static UA_INLINE void %s_deleteMembers(%s *p) { ''' + ("UA_deleteMembers(p, &"+typeTableName+"["+typeTableName+"_"+self.name[3:].upper()+"]);" if not self.fixed_size() else "") + ''' }
  107. static UA_INLINE %s * %s_new(void) { return (%s*) UA_new(%s); }
  108. static UA_INLINE UA_StatusCode %s_copy(const %s *src, %s *dst) { ''' + \
  109. ("*dst = *src; return UA_STATUSCODE_GOOD;" if self.fixed_size() else "return UA_copy(src, dst, &" + typeTableName+"["+typeTableName+"_"+self.name[3:].upper() + "]);") +" }") % \
  110. tuple([self.name, self.name, self.name] +
  111. [self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"] +
  112. [self.name, self.name] +
  113. [self.name, self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"] +
  114. [self.name, self.name, self.name])
  115. def encoding_h(self, typeTableName):
  116. return '''static UA_INLINE UA_StatusCode %s_encodeBinary(const %s *src, UA_ByteString *dst, size_t *offset) { return UA_encodeBinary(src, %s, dst, offset); }
  117. static UA_INLINE UA_StatusCode %s_decodeBinary(const UA_ByteString *src, size_t *offset, %s *dst) { return UA_decodeBinary(src, offset, dst, %s); }''' % \
  118. tuple(list(itertools.chain(*itertools.repeat([self.name, self.name, "&"+typeTableName+"[" + typeTableName + "_" + self.name[3:].upper()+"]"], 2))))
  119. class BuiltinType(Type):
  120. "Generic type without members. Used for builtin types."
  121. def typelayout_c(self, namespace_0, description, outname):
  122. if description == None:
  123. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  124. else:
  125. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % \
  126. (description.namespaceid, description.nodeid)
  127. if self.name in ["UA_String", "UA_ByteString", "UA_XmlElement"]:
  128. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  129. ".memSize = sizeof(" + self.name + "), " + \
  130. ".builtin = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  131. ".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTE, .namespaceZero = UA_TRUE, " + \
  132. (".memberName = \"\", " if typeintrospection else "") + \
  133. ".padding = 0, .isArray = UA_TRUE }}, " + \
  134. ".typeIndex = %s }" % (outname.upper() + "_" + self.name[3:].upper())
  135. if self.name == "UA_ExpandedNodeId":
  136. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  137. ".memSize = sizeof(UA_ExpandedNodeId), " + \
  138. ".builtin = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  139. ".membersSize = 3, .members = {" + \
  140. "\n\t{.memberTypeIndex = UA_TYPES_NODEID, .namespaceZero = UA_TRUE, " + \
  141. (".memberName = \"nodeId\", " if typeintrospection else "") + \
  142. ".padding = 0, .isArray = UA_FALSE }," + \
  143. "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
  144. (".memberName = \"namespaceUri\", " if typeintrospection else "") + \
  145. ".padding = offsetof(UA_ExpandedNodeId, namespaceUri) - sizeof(UA_NodeId), .isArray = UA_FALSE }," + \
  146. "\n\t{.memberTypeIndex = UA_TYPES_UINT32, .namespaceZero = UA_TRUE, " + \
  147. (".memberName = \"serverIndex\", " if typeintrospection else "") + \
  148. ".padding = offsetof(UA_ExpandedNodeId, serverIndex) - offsetof(UA_ExpandedNodeId, namespaceUri) - sizeof(UA_String), .isArray = UA_FALSE }},\n" + \
  149. ".typeIndex = UA_TYPES_EXPANDEDNODEID }"
  150. if self.name == "UA_QualifiedName":
  151. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  152. ".memSize = sizeof(UA_QualifiedName), " + \
  153. ".builtin = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  154. ".membersSize = 2, .members = {" + \
  155. "\n\t{.memberTypeIndex = UA_TYPES_UINT16, .namespaceZero = UA_TRUE, " + \
  156. (".memberName = \"namespaceIndex\", " if typeintrospection else "") + \
  157. ".padding = 0, .isArray = UA_FALSE }," + \
  158. "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
  159. (".memberName = \"name\", " if typeintrospection else "") + \
  160. ".padding = offsetof(UA_QualifiedName, name)-sizeof(UA_UInt16), .isArray = UA_FALSE }},\n" + \
  161. ".typeIndex = UA_TYPES_QUALIFIEDNAME }"
  162. if self.name == "UA_LocalizedText":
  163. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  164. ".memSize = sizeof(UA_LocalizedText), " + \
  165. ".builtin = UA_TRUE, .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  166. ".membersSize = 2, .members = {" + \
  167. "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
  168. (".memberName = \"locale\", " if typeintrospection else "") + \
  169. ".padding = 0, .isArray = UA_FALSE }," + \
  170. "\n\t{.memberTypeIndex = UA_TYPES_STRING, .namespaceZero = UA_TRUE, " + \
  171. (".memberName = \"text\", " if typeintrospection else "") + \
  172. ".padding = offsetof(UA_LocalizedText, text)-sizeof(UA_String), .isArray = UA_FALSE }},\n" + \
  173. ".typeIndex = UA_TYPES_LOCALIZEDTEXT }"
  174. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  175. ".memSize = sizeof(" + self.name + "), " + \
  176. ".builtin = UA_TRUE, .fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
  177. ", .zeroCopyable = " + ("UA_TRUE" if self.zero_copy() else "UA_FALSE") + \
  178. ", .membersSize = 1, .members = {" + \
  179. "\n\t{.memberTypeIndex = UA_TYPES_" + self.name[3:].upper() + " , .namespaceZero = UA_TRUE, " + \
  180. (".memberName = \"\", " if typeintrospection else "") + \
  181. ".padding = 0, .isArray = UA_FALSE }},\n" + \
  182. ".typeIndex = UA_TYPES_" + self.name[3:].upper() + " }"
  183. class EnumerationType(Type):
  184. def __init__(self, name, description = "", elements = OrderedDict()):
  185. self.name = name
  186. self.description = description
  187. self.elements = elements # maps a name to an integer value
  188. def append_enum(name, value):
  189. self.elements[name] = value
  190. def fixed_size(self):
  191. return True
  192. def mem_size(self):
  193. return 4
  194. def zero_copy(self):
  195. return True
  196. def typedef_c(self):
  197. if sys.version_info[0] < 3:
  198. values = self.elements.iteritems()
  199. else:
  200. values = self.elements.items()
  201. return "typedef enum { \n " + \
  202. ",\n ".join(map(lambda kv : kv[0].upper() + " = " + kv[1], values)) + \
  203. "\n} " + self.name + ";"
  204. def typelayout_c(self, namespace_0, description, outname):
  205. if description == None:
  206. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  207. else:
  208. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  209. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  210. ".memSize = sizeof(" + self.name + "), .builtin = UA_TRUE, " + \
  211. ".fixedSize = UA_TRUE, .zeroCopyable = UA_TRUE, " + \
  212. ".membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_INT32, " + \
  213. (".memberName = \"\", " if typeintrospection else "") + \
  214. ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_FALSE }}, .typeIndex = UA_TYPES_INT32 }"
  215. class OpaqueType(Type):
  216. def fixed_size(self):
  217. return False
  218. def zero_copy(self):
  219. return False
  220. def typedef_c(self):
  221. return "typedef UA_ByteString " + self.name + ";"
  222. def typelayout_c(self, namespace_0, description, outname):
  223. if description == None:
  224. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  225. else:
  226. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  227. return (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  228. ".memSize = sizeof(" + self.name + "), .fixedSize = UA_FALSE, .zeroCopyable = UA_FALSE, " + \
  229. ".builtin = UA_FALSE, .membersSize = 1,\n\t.members = {{.memberTypeIndex = UA_TYPES_BYTE," + \
  230. (".memberName = \"\", " if typeintrospection else "") + \
  231. ".namespaceZero = UA_TRUE, .padding = 0, .isArray = UA_TRUE }}, .typeIndex = %s}" % (outname.upper() + "_" + self.name[3:].upper())
  232. class StructMember(object):
  233. def __init__(self, name, memberType, isArray):
  234. self.name = name
  235. self.memberType = memberType
  236. self.isArray = isArray
  237. class StructType(Type):
  238. def __init__(self, name, description, members = OrderedDict()):
  239. self.name = name
  240. self.description = description
  241. self.members = members # maps a name to a member definition
  242. def fixed_size(self):
  243. for m in self.members.values():
  244. if m.isArray or not m.memberType.fixed_size():
  245. return False
  246. return True
  247. def mem_size(self):
  248. total = 0
  249. for m in self.members.values():
  250. if m.isArray:
  251. raise Exception("Arrays have no fixed size!")
  252. else:
  253. total += m.memberType.mem_size()
  254. return total
  255. def zero_copy(self):
  256. for m in self.members.values():
  257. if m.isArray or not m.memberType.zero_copy():
  258. return False
  259. return True
  260. def typedef_c(self):
  261. if len(self.members) == 0:
  262. return "typedef void * " + self.name + ";"
  263. returnstr = "typedef struct {\n"
  264. if sys.version_info[0] < 3:
  265. values = self.members.iteritems()
  266. else:
  267. values = self.members.items()
  268. for name, member in values:
  269. if member.isArray:
  270. returnstr += " size_t " + name + "Size;\n"
  271. returnstr += " " + member.memberType.name + " *" +name + ";\n"
  272. else:
  273. returnstr += " " + member.memberType.name + " " +name + ";\n"
  274. return returnstr + "} " + self.name + ";"
  275. def typelayout_c(self, namespace_0, description, outname):
  276. if description == None:
  277. typeid = "{.namespaceIndex = 0, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = 0}, "
  278. else:
  279. typeid = "{.namespaceIndex = %s, .identifierType = UA_NODEIDTYPE_NUMERIC, .identifier.numeric = %s}, " % (description.namespaceid, description.nodeid)
  280. layout = (("{.typeName = \"" + self.name[3:] + "\", ") if typeintrospection else "{") + ".typeId = " + typeid + \
  281. ".memSize = sizeof(" + self.name + "), "+ \
  282. ".builtin = UA_FALSE" + \
  283. ", .fixedSize = " + ("UA_TRUE" if self.fixed_size() else "UA_FALSE") + \
  284. ", .zeroCopyable = " + ("sizeof(" + self.name + ") == " + str(self.mem_size()) if self.zero_copy() \
  285. else "UA_FALSE") + \
  286. ", .typeIndex = " + outname.upper() + "_" + self.name[3:].upper() + \
  287. ", .membersSize = " + str(len(self.members)) + ","
  288. if len(self.members) > 0:
  289. layout += "\n\t.members={"
  290. for index, member in enumerate(self.members.values()):
  291. layout += "\n\t{" + \
  292. ((".memberName = \"" + member.name[0].upper() + member.name[1:] + "\", ") if typeintrospection else "") + \
  293. ".memberTypeIndex = " + ("UA_TYPES_" + member.memberType.name[3:].upper() if args.namespace_id == 0 or member.memberType.name in existing_types else \
  294. outname.upper() + "_" + member.memberType.name[3:].upper()) + ", " + \
  295. ".namespaceZero = "+ \
  296. ("UA_TRUE, " if args.namespace_id == 0 or member.memberType.name in existing_types else "UA_FALSE, ") + \
  297. ".padding = "
  298. if not member.isArray:
  299. thispos = "offsetof(%s, %s)" % (self.name, member.name)
  300. else:
  301. thispos = "offsetof(%s, %sSize)" % (self.name, member.name)
  302. before_endpos = "0"
  303. if index > 0:
  304. if sys.version_info[0] < 3:
  305. before = self.members.values()[index-1]
  306. else:
  307. before = list(self.members.values())[index-1]
  308. before_endpos = "(offsetof(%s, %s)" % (self.name, before.name)
  309. if before.isArray:
  310. before_endpos += " + sizeof(void*))"
  311. else:
  312. before_endpos += " + sizeof(%s))" % before.memberType.name
  313. layout += "%s - %s" % (thispos, before_endpos)
  314. layout += ", .isArray = " + ("UA_TRUE" if member.isArray else "UA_FALSE") + " }, "
  315. layout += "}"
  316. return layout + "}"
  317. def parseTypeDefinitions(xmlDescription, existing_types = OrderedDict()):
  318. '''Returns an ordered dict that maps names to types. The order is such that
  319. every type depends only on known types. '''
  320. ns = {"opc": "http://opcfoundation.org/BinarySchema/"}
  321. tree = etree.parse(xmlDescription)
  322. typeSnippets = tree.xpath("/opc:TypeDictionary/*[not(self::opc:Import)]", namespaces=ns)
  323. types = OrderedDict(existing_types.items())
  324. # types we do not want to autogenerate
  325. def skipType(name):
  326. if name in builtin_types:
  327. return True
  328. if name in excluded_types:
  329. return True
  330. if outname == "ua_types" and not name[3:] in minimal_types:
  331. return True
  332. if "Test" in name: # skip all test types
  333. return True
  334. if re.search("NodeId$", name) != None:
  335. return True
  336. return False
  337. def stripTypename(tn):
  338. return tn[tn.find(":")+1:]
  339. def camlCase2CCase(item):
  340. "Member names begin with a lower case character"
  341. return item[:1].lower() + item[1:] if item else ''
  342. def typeReady(element):
  343. "Do we have the member types yet?"
  344. for child in element:
  345. if child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  346. if stripTypename(child.get("TypeName")) not in types:
  347. return False
  348. return True
  349. def parseEnumeration(typeXml):
  350. name = "UA_" + typeXml.get("Name")
  351. description = ""
  352. elements = OrderedDict()
  353. for child in typeXml:
  354. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  355. description = child.text
  356. if child.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedValue":
  357. elements[name + "_" + child.get("Name")] = child.get("Value")
  358. return EnumerationType(name, description, elements)
  359. def parseOpaque(typeXml):
  360. name = "UA_" + typeXml.get("Name")
  361. description = ""
  362. for child in typeXml:
  363. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  364. description = child.text
  365. return OpaqueType(name, description)
  366. def parseStructured(typeXml):
  367. "Returns None if we miss member descriptions"
  368. name = "UA_" + typeXml.get("Name")
  369. description = ""
  370. for child in typeXml:
  371. if child.tag == "{http://opcfoundation.org/BinarySchema/}Documentation":
  372. description = child.text
  373. # ignore lengthfields, just tag the array-members as an array
  374. lengthfields = []
  375. for child in typeXml:
  376. if child.get("LengthField"):
  377. lengthfields.append(child.get("LengthField"))
  378. members = OrderedDict()
  379. for child in typeXml:
  380. if not child.tag == "{http://opcfoundation.org/BinarySchema/}Field":
  381. continue
  382. if child.get("Name") in lengthfields:
  383. continue
  384. memberTypeName = "UA_" + stripTypename(child.get("TypeName"))
  385. if not memberTypeName in types:
  386. return None
  387. memberType = types[memberTypeName]
  388. memberName = camlCase2CCase(child.get("Name"))
  389. isArray = True if child.get("LengthField") else False
  390. members[memberName] = StructMember(memberName, memberType, isArray)
  391. return StructType(name, description, members)
  392. finished = False
  393. while(not finished):
  394. finished = True
  395. for typeXml in typeSnippets:
  396. name = "UA_" + typeXml.get("Name")
  397. if name in types or skipType(name):
  398. continue
  399. if typeXml.tag == "{http://opcfoundation.org/BinarySchema/}EnumeratedType":
  400. t = parseEnumeration(typeXml)
  401. types[t.name] = t
  402. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}OpaqueType":
  403. t = parseOpaque(typeXml)
  404. types[t.name] = t
  405. elif typeXml.tag == "{http://opcfoundation.org/BinarySchema/}StructuredType":
  406. t = parseStructured(typeXml)
  407. if t == None:
  408. finished = False
  409. else:
  410. types[t.name] = t
  411. # remove the existing types
  412. for k in existing_types.keys():
  413. types.pop(k)
  414. return types
  415. parser = argparse.ArgumentParser()
  416. parser.add_argument('--ns0-types-xml', nargs=1, help='xml-definition of the ns0 types that are assumed to already exist')
  417. parser.add_argument('--enable-subscription-types', nargs=1, help='Generate datatypes necessary for Montoring and Subscriptions.')
  418. parser.add_argument('--typedescriptions', nargs=1, help='csv file with type descriptions')
  419. parser.add_argument('--typeintrospection', help='add the type and member names to the idatatype structures', action='store_true')
  420. parser.add_argument('namespace_id', type=int, help='the id of the target namespace')
  421. parser.add_argument('types_xml', help='path/to/Opc.Ua.Types.bsd')
  422. parser.add_argument('outfile', help='output file w/o extension')
  423. args = parser.parse_args()
  424. outname = args.outfile.split("/")[-1]
  425. inname = args.types_xml.split("/")[-1]
  426. typeintrospection = args.typeintrospection
  427. existing_types = OrderedDict()
  428. if args.enable_subscription_types:
  429. minimal_types = minimal_types + subscription_types
  430. if args.namespace_id == 0 or args.ns0_types_xml:
  431. existing_types = OrderedDict([(t, BuiltinType(t)) for t in builtin_types])
  432. if args.ns0_types_xml:
  433. if sys.version_info[0] < 3:
  434. OrderedDict(existing_types.items() + parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items())
  435. else:
  436. OrderedDict(list(existing_types.items()) + list(parseTypeDefinitions(args.ns0_types_xml[0], existing_types).items()))
  437. types = parseTypeDefinitions(args.types_xml, existing_types)
  438. if args.namespace_id == 0:
  439. if sys.version_info[0] < 3:
  440. types = OrderedDict(existing_types.items() + types.items())
  441. else:
  442. types = OrderedDict(list(existing_types.items()) + list(types.items()))
  443. typedescriptions = {}
  444. if args.typedescriptions:
  445. typedescriptions = parseTypeDescriptions(args.typedescriptions, args.namespace_id)
  446. fh = open(args.outfile + "_generated.h",'w')
  447. fe = open(args.outfile + "_generated_encoding_binary.h",'w')
  448. fc = open(args.outfile + "_generated.c",'w')
  449. def printh(string):
  450. print(string, end='\n', file=fh)
  451. def printe(string):
  452. print(string, end='\n', file=fe)
  453. def printc(string):
  454. print(string, end='\n', file=fc)
  455. printh('''/**
  456. * @file ''' + outname + '''_generated.h
  457. *
  458. * @brief Autogenerated data types
  459. *
  460. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  461. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  462. */
  463. #ifndef ''' + outname.upper() + '''_GENERATED_H_
  464. #define ''' + outname.upper() + '''_GENERATED_H_
  465. #ifdef __cplusplus
  466. extern "C" {
  467. #endif
  468. #include "ua_types.h"
  469. #ifdef UA_INTERNAL
  470. #include "ua_types_encoding_binary.h"
  471. #endif'''
  472. + ('\n#include "ua_types_generated.h"\n' if args.namespace_id != 0 else '') + '''
  473. /**
  474. * @ingroup types
  475. *
  476. * @defgroup ''' + outname + '''_generated Autogenerated ''' + outname + ''' Types
  477. *
  478. * @brief Data structures that are autogenerated from an XML-Schema.
  479. *
  480. * @{
  481. */
  482. ''')
  483. printh("#define " + outname.upper() + "_COUNT %s\n" % (str(len(types))))
  484. printh("extern UA_EXPORT const UA_DataType *" + outname.upper() + ";\n")
  485. i = 0
  486. if sys.version_info[0] < 3:
  487. values = types.itervalues()
  488. else:
  489. values = types.values()
  490. for t in values:
  491. printh("")
  492. if type(t) != BuiltinType:
  493. if t.description != "":
  494. printh("/** @brief " + t.description + " */")
  495. printh(t.typedef_c())
  496. printh("#define " + outname.upper() + "_" + t.name[3:].upper() + " " + str(i))
  497. printh(t.functions_c(outname.upper()))
  498. i += 1
  499. printh('''
  500. /// @} /* end of group */\n
  501. #ifdef __cplusplus
  502. } // extern "C"
  503. #endif\n
  504. #endif /* %s_GENERATED_H_ */''' % outname.upper())
  505. printe('''/**
  506. * @file ''' + outname + '''_generated_encoding_binary.h
  507. *
  508. * @brief Binary encoding for autogenerated data types
  509. *
  510. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  511. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  512. */\n
  513. #include "ua_types_encoding_binary.h"
  514. #include "''' + outname + '''_generated.h"''')
  515. printc('''/**
  516. * @file ''' + outname + '''_generated.c
  517. *
  518. * @brief Autogenerated data types
  519. *
  520. * Generated from ''' + inname + ''' with script ''' + sys.argv[0] + '''
  521. * on host ''' + platform.uname()[1] + ''' by user ''' + getpass.getuser() + ''' at ''' + time.strftime("%Y-%m-%d %I:%M:%S") + '''
  522. */\n
  523. #include "stddef.h"
  524. #include "ua_types.h"
  525. #include "''' + outname + '''_generated.h"\n
  526. const UA_DataType *''' + outname.upper() + ''' = (UA_DataType[]){''')
  527. if sys.version_info[0] < 3:
  528. values = types.itervalues()
  529. else:
  530. values = types.values()
  531. for t in values:
  532. printc("")
  533. printc("/* " + t.name + " */")
  534. if args.typedescriptions:
  535. td = typedescriptions[t.name]
  536. else:
  537. td = None
  538. printc(t.typelayout_c(args.namespace_id == 0, td, outname) + ",")
  539. printe("")
  540. printe("/* " + t.name + " */")
  541. printe(t.encoding_h(outname.upper()))
  542. printc("};\n")
  543. fh.close()
  544. fe.close()
  545. fc.close()