backend_open62541_nodes.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. ###
  4. ### Author: Chris Iatrou (ichrispa@core-vector.net)
  5. ### Version: rev 13
  6. ###
  7. ### This program was created for educational purposes and has been
  8. ### contributed to the open62541 project by the author. All licensing
  9. ### terms for this source is inherited by the terms and conditions
  10. ### specified for by the open62541 project (see the projects readme
  11. ### file for more information on the LGPL terms and restrictions).
  12. ###
  13. ### This program is not meant to be used in a production environment. The
  14. ### author is not liable for any complications arising due to the use of
  15. ### this program.
  16. ###
  17. from nodes import *
  18. from backend_open62541_datatypes import *
  19. import re
  20. import logging
  21. import sys
  22. if sys.version_info[0] >= 3:
  23. # strings are already parsed to unicode
  24. def unicode(s):
  25. return s
  26. logger = logging.getLogger(__name__)
  27. #################
  28. # Generate Code #
  29. #################
  30. def generateNodeIdPrintable(node):
  31. if isinstance(node.id, NodeId):
  32. CodePrintable = node.__class__.__name__ + "_" + str(node.id)
  33. else:
  34. CodePrintable = node.__class__.__name__ + "_unknown_nid"
  35. return re.sub('[^0-9a-z_]+', '_', CodePrintable.lower())
  36. def generateNodeValueInstanceName(node, parent, arrayIndex):
  37. return generateNodeIdPrintable(parent) + "_" + str(node.alias) + "_" + str(arrayIndex)
  38. def generateReferenceCode(reference):
  39. if reference.isForward:
  40. return "retVal |= UA_Server_addReference(server, %s, %s, %s, true);" % \
  41. (generateNodeIdCode(reference.source),
  42. generateNodeIdCode(reference.referenceType),
  43. generateExpandedNodeIdCode(reference.target))
  44. else:
  45. return "retVal |= UA_Server_addReference(server, %s, %s, %s, false);" % \
  46. (generateNodeIdCode(reference.source),
  47. generateNodeIdCode(reference.referenceType),
  48. generateExpandedNodeIdCode(reference.target))
  49. def generateReferenceTypeNodeCode(node):
  50. code = []
  51. code.append("UA_ReferenceTypeAttributes attr = UA_ReferenceTypeAttributes_default;")
  52. if node.isAbstract:
  53. code.append("attr.isAbstract = true;")
  54. if node.symmetric:
  55. code.append("attr.symmetric = true;")
  56. if node.inverseName != "":
  57. code.append("attr.inverseName = UA_LOCALIZEDTEXT(\"\", \"%s\");" % \
  58. node.inverseName)
  59. return code
  60. def generateObjectNodeCode(node):
  61. code = []
  62. code.append("UA_ObjectAttributes attr = UA_ObjectAttributes_default;")
  63. if node.eventNotifier:
  64. code.append("attr.eventNotifier = true;")
  65. return code
  66. def setNodeDatatypeRecursive(node, nodeset):
  67. if not isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
  68. raise RuntimeError("DataType can only be set for VariableNode and VariableTypeNode")
  69. if node.dataType is not None:
  70. return
  71. # If BaseVariableType
  72. if node.id == NodeId("ns=0;i=62"):
  73. if node.dataType is None:
  74. # Set to default BaseDataType
  75. node.dataType = NodeId("ns=0;i=24")
  76. return
  77. if isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
  78. typeDefNode = nodeset.getNodeTypeDefinition(node)
  79. if typeDefNode is None:
  80. # Use the parent type.
  81. raise RuntimeError("Cannot get node for HasTypeDefinition of VariableNode " + node.browseName.name + " " + str(node.id))
  82. setNodeDatatypeRecursive(typeDefNode, nodeset)
  83. node.dataType = typeDefNode.dataType
  84. else:
  85. # Use the parent type.
  86. if node.parent is None:
  87. raise RuntimeError("Parent node not defined for " + node.browseName.name + " " + str(node.id))
  88. setNodeDatatypeRecursive(node.parent, nodeset)
  89. node.dataType = node.parent.dataType
  90. def generateCommonVariableCode(node, nodeset):
  91. code = []
  92. codeCleanup = []
  93. codeGlobal = []
  94. if node.valueRank is not None:
  95. code.append("attr.valueRank = %d;" % node.valueRank)
  96. if node.valueRank > 0:
  97. code.append("attr.arrayDimensionsSize = %d;" % node.valueRank)
  98. code.append("UA_UInt32 arrayDimensions[{}];".format(node.valueRank))
  99. if len(node.arrayDimensions) == node.valueRank:
  100. for idx, v in enumerate(node.arrayDimensions):
  101. code.append("arrayDimensions[{}] = {};".format(idx, int(str(v))))
  102. else:
  103. for dim in range(0, node.valueRank):
  104. code.append("arrayDimensions[{}] = 0;".format(dim))
  105. code.append("attr.arrayDimensions = &arrayDimensions[0];")
  106. else:
  107. # Default value for the value rank as defined in the NodeSet2 xsd file
  108. code.append("attr.valueRank = -1;")
  109. if node.dataType is None:
  110. # Inherit the datatype from the HasTypeDefinition reference, as stated in the OPC UA Spec:
  111. # 6.4.2
  112. # "Instances inherit the initial values for the Attributes that they have in common with the
  113. # TypeDefinitionNode from which they are instantiated, with the exceptions of the NodeClass and
  114. # NodeId."
  115. setNodeDatatypeRecursive(node, nodeset)
  116. code.append("/* DataType inherited */")
  117. dataTypeNode = nodeset.getBaseDataType(nodeset.getDataTypeNode(node.dataType))
  118. if dataTypeNode is None:
  119. raise RuntimeError("Cannot get BaseDataType for dataType : " + str(node.dataType) + " of node " + node.browseName.name + " " + str(node.id))
  120. code.append("attr.dataType = %s;" % generateNodeIdCode(node.dataType))
  121. if dataTypeNode.isEncodable():
  122. if node.value is not None:
  123. [code1, codeCleanup1, codeGlobal1] = generateValueCode(node.value, nodeset.nodes[node.id], nodeset)
  124. code += code1
  125. codeCleanup += codeCleanup1
  126. codeGlobal += codeGlobal1
  127. if node.valueRank is not None and node.valueRank > 0 and len(node.arrayDimensions) == node.valueRank and len(node.value.value) > 0:
  128. numElements = 1
  129. hasZero = False
  130. for v in node.arrayDimensions:
  131. dim = int(unicode(v))
  132. if dim > 0:
  133. numElements = numElements * dim
  134. else:
  135. hasZero = True
  136. if hasZero == False and len(node.value.value) == numElements:
  137. code.append("attr.value.arrayDimensionsSize = attr.arrayDimensionsSize;")
  138. code.append("attr.value.arrayDimensions = attr.arrayDimensions;")
  139. logger.warning("printing arrayDimensions")
  140. else:
  141. logger.error("Dimension with size 0 or value count mismatch detected, ArrayDimensions won't be copied to the Value attribute.")
  142. elif node.value is not None:
  143. raise RuntimeError("Cannot encode dataTypeNode: " + dataTypeNode.browseName.name + " for value of node " + node.browseName.name + " " + str(node.id))
  144. return [code, codeCleanup, codeGlobal]
  145. def generateVariableNodeCode(node, nodeset):
  146. code = []
  147. codeCleanup = []
  148. codeGlobal = []
  149. code.append("UA_VariableAttributes attr = UA_VariableAttributes_default;")
  150. if node.historizing:
  151. code.append("attr.historizing = true;")
  152. code.append("attr.minimumSamplingInterval = %f;" % node.minimumSamplingInterval)
  153. code.append("attr.userAccessLevel = %d;" % node.userAccessLevel)
  154. code.append("attr.accessLevel = %d;" % node.accessLevel)
  155. # in order to be compatible with mostly OPC UA client
  156. # force valueRank = -1 for scalar VariableNode
  157. if node.valueRank == -2 and node.value is not None and len(node.value.value) == 1:
  158. node.valueRank = -1
  159. if node.valueRank is None:
  160. # If the VariableNode does not have any valueRank, use the one from the VariableType
  161. # The type may define the valueRank to e.g. 1 which is not the default value for the variable itself.
  162. # This is e.g. the case for 'ActiveBackground1' of the Adi.NodeSet2.xml
  163. variableTypeNode = nodeset.getNodeTypeDefinition(node)
  164. if variableTypeNode is None:
  165. raise RuntimeError("Cannot get node for HasTypeDefinition of VariableNode " + node.browseName.name + " " + str(node.id))
  166. if variableTypeNode.valueRank is not None and variableTypeNode.valueRank > -2:
  167. code.append("attr.valueRank = %d; /* From VariableType */" % variableTypeNode.valueRank)
  168. [code1, codeCleanup1, codeGlobal1] = generateCommonVariableCode(node, nodeset)
  169. code += code1
  170. codeCleanup += codeCleanup1
  171. codeGlobal += codeGlobal1
  172. return [code, codeCleanup, codeGlobal]
  173. def generateVariableTypeNodeCode(node, nodeset):
  174. code = []
  175. codeCleanup = []
  176. codeGlobal = []
  177. code.append("UA_VariableTypeAttributes attr = UA_VariableTypeAttributes_default;")
  178. if node.isAbstract:
  179. code.append("attr.isAbstract = true;")
  180. [code1, codeCleanup1, codeGlobal1] = generateCommonVariableCode(node, nodeset)
  181. code += code1
  182. codeCleanup += codeCleanup1
  183. codeGlobal += codeGlobal1
  184. return [code, codeCleanup, codeGlobal]
  185. def lowerFirstChar(inputString):
  186. return inputString[0].lower() + inputString[1:]
  187. def generateExtensionObjectSubtypeCode(node, parent, nodeset, global_var_code, instanceName=None, isArrayElement=False):
  188. code = [""]
  189. codeCleanup = [""]
  190. logger.debug("Building extensionObject for " + str(parent.id))
  191. logger.debug("Value " + str(node.value))
  192. logger.debug("Encoding " + str(node.encodingRule))
  193. typeBrowseNode = makeCIdentifier(nodeset.getDataTypeNode(parent.dataType).browseName.name)
  194. #TODO: review this
  195. if typeBrowseNode == "NumericRange":
  196. # in the stack we define a separate structure for the numeric range, but
  197. # the value itself is just a string
  198. typeBrowseNode = "String"
  199. typeString = "UA_" + typeBrowseNode
  200. if instanceName is None:
  201. instanceName = generateNodeValueInstanceName(node, parent, 0)
  202. code.append("UA_STACKARRAY(" + typeString + ", " + instanceName + ", 1);")
  203. typeArr = nodeset.getDataTypeNode(parent.dataType).typesArray
  204. typeString = nodeset.getDataTypeNode(parent.dataType).browseName.name.upper()
  205. typeArrayString = typeArr + "[" + typeArr + "_" + typeString + "]"
  206. code.append("UA_init({ref}{instanceName}, &{typeArrayString});".format(ref="&" if isArrayElement else "",
  207. instanceName=instanceName,
  208. typeArrayString=typeArrayString))
  209. # Assign data to the struct contents
  210. # Track the encoding rule definition to detect arrays and/or ExtensionObjects
  211. encFieldIdx = 0
  212. for subv in node.value:
  213. encField = node.encodingRule[encFieldIdx]
  214. encFieldIdx = encFieldIdx + 1
  215. memberName = lowerFirstChar(encField[0])
  216. if isinstance(subv, list):
  217. if len(subv) == 0:
  218. continue
  219. logger.info("ExtensionObject contains array")
  220. memberName = lowerFirstChar(encField[0])
  221. encTypeString = "UA_" + subv[0].__class__.__name__
  222. code.append("UA_STACKARRAY(" + encTypeString + ", " + instanceName + "_" + memberName+", {0});".format(len(subv)))
  223. encTypeArr = nodeset.getDataTypeNode(subv[0].__class__.__name__).typesArray
  224. encTypeArrayString = encTypeArr + "[" + encTypeArr + "_" + subv[0].__class__.__name__.upper() + "]"
  225. code.append("UA_init({ref}{instanceName}, &{typeArrayString});".format(ref="&" if isArrayElement else "",
  226. instanceName=instanceName + "_" + memberName,
  227. typeArrayString=encTypeArrayString))
  228. subArrayIdx = 0
  229. for val in subv:
  230. code.append(generateNodeValueCode(instanceName + "_" + memberName + "[" + str(subArrayIdx) + "]" +" = ", val, instanceName,instanceName + "_gehtNed_member", global_var_code, asIndirect=False))
  231. subArrayIdx = subArrayIdx + 1
  232. code.append(instanceName + "->" + memberName + " = " + instanceName+"_"+ memberName+";")
  233. continue
  234. else:
  235. logger.debug(
  236. "Encoding of field " + memberName + " is " + str(subv.encodingRule) + "defined by " + str(encField))
  237. # Check if this is an array
  238. accessor = "." if isArrayElement else "->"
  239. if subv.valueRank is None or subv.valueRank == 0:
  240. valueName = instanceName + accessor + memberName
  241. code.append(generateNodeValueCode(valueName + " = " ,
  242. subv, instanceName,valueName, global_var_code, asIndirect=False))
  243. else:
  244. memberName = lowerFirstChar(encField[0])
  245. code.append(generateNodeValueCode(instanceName + accessor + memberName + "Size = ", subv, instanceName,valueName, global_var_code, asIndirect=False))
  246. if not isArrayElement:
  247. code.append("UA_Variant_setScalar(&attr.value, " + instanceName + ", &" + typeArrayString + ");")
  248. return [code, codeCleanup]
  249. def getTypeBrowseName(dataTypeNode):
  250. typeBrowseName = makeCIdentifier(dataTypeNode.browseName.name)
  251. #TODO: review this
  252. if typeBrowseName == "NumericRange":
  253. # in the stack we define a separate structure for the numeric range, but
  254. # the value itself is just a string
  255. typeBrowseName = "String"
  256. return typeBrowseName
  257. def getTypesArrayForValue(nodeset, value):
  258. typeNode = nodeset.getNodeByBrowseName(value.__class__.__name__)
  259. if typeNode is None or value.isInternal:
  260. typesArray = "UA_TYPES"
  261. else:
  262. typesArray = typeNode.typesArray
  263. typeName = makeCIdentifier(value.__class__.__name__.upper())
  264. return "&" + typesArray + "[" + typesArray + "_" + typeName + "]"
  265. def isArrayVariableNode(node, parentNode):
  266. return parentNode.valueRank is not None and (parentNode.valueRank != -1 and (parentNode.valueRank >= 0
  267. or (len(node.value) > 1
  268. and (parentNode.valueRank != -2 or parentNode.valueRank != -3))))
  269. def generateValueCode(node, parentNode, nodeset, bootstrapping=True):
  270. code = []
  271. codeCleanup = []
  272. codeGlobal = []
  273. valueName = generateNodeIdPrintable(parentNode) + "_variant_DataContents"
  274. # node.value either contains a list of multiple identical BUILTINTYPES, or it
  275. # contains a single builtintype (which may be a container); choose if we need
  276. # to create an array or a single variable.
  277. # Note that some genious defined that there are arrays of size 1, which are
  278. # distinctly different then a single value, so we need to check that as well
  279. # Semantics:
  280. # -3: Scalar or 1-dim
  281. # -2: Scalar or x-dim | x>0
  282. # -1: Scalar
  283. # 0: x-dim | x>0
  284. # n: n-dim | n>0
  285. if (len(node.value) == 0):
  286. return ["", "", ""]
  287. if not isinstance(node.value[0], Value):
  288. return ["", "", ""]
  289. dataTypeNode = nodeset.getDataTypeNode(parentNode.dataType)
  290. if isArrayVariableNode(node, parentNode):
  291. # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
  292. if isinstance(node.value[0], Guid):
  293. logger.warn("Don't know how to print array of GUID in node " + str(parentNode.id))
  294. elif isinstance(node.value[0], DiagnosticInfo):
  295. logger.warn("Don't know how to print array of DiagnosticInfo in node " + str(parentNode.id))
  296. elif isinstance(node.value[0], StatusCode):
  297. logger.warn("Don't know how to print array of StatusCode in node " + str(parentNode.id))
  298. else:
  299. if isinstance(node.value[0], ExtensionObject):
  300. code.append("UA_" + getTypeBrowseName(dataTypeNode) + " " + valueName + "[" + str(len(node.value)) + "];")
  301. for idx, v in enumerate(node.value):
  302. logger.debug("Building extObj array index " + str(idx))
  303. instanceName = valueName + "[" + str(idx) + "]"
  304. [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(v, parent=parentNode, nodeset=nodeset,
  305. global_var_code=codeGlobal, instanceName=instanceName,
  306. isArrayElement=True)
  307. code = code + code1
  308. codeCleanup = codeCleanup + codeCleanup1
  309. else:
  310. code.append("UA_" + node.value[0].__class__.__name__ + " " + valueName + "[" + str(len(node.value)) + "];")
  311. for idx, v in enumerate(node.value):
  312. instanceName = generateNodeValueInstanceName(v, parentNode, idx)
  313. code.append(generateNodeValueCode(
  314. valueName + "[" + str(idx) + "] = " , v, instanceName, valueName, codeGlobal))
  315. code.append("UA_Variant_setArray(&attr.value, &" + valueName +
  316. ", (UA_Int32) " + str(len(node.value)) + ", " + "&" +
  317. dataTypeNode.typesArray + "["+dataTypeNode.typesArray + "_" + getTypeBrowseName(dataTypeNode).upper() +"]);")
  318. #scalar value
  319. else:
  320. # User the following strategy for all directly mappable values a la 'UA_Type MyInt = (UA_Type) 23;'
  321. if isinstance(node.value[0], Guid):
  322. logger.warn("Don't know how to print scalar GUID in node " + str(parentNode.id))
  323. elif isinstance(node.value[0], DiagnosticInfo):
  324. logger.warn("Don't know how to print scalar DiagnosticInfo in node " + str(parentNode.id))
  325. elif isinstance(node.value[0], StatusCode):
  326. logger.warn("Don't know how to print scalar StatusCode in node " + str(parentNode.id))
  327. else:
  328. # The following strategy applies to all other types, in particular strings and numerics.
  329. if isinstance(node.value[0], ExtensionObject):
  330. [code1, codeCleanup1] = generateExtensionObjectSubtypeCode(node.value[0], parent=parentNode, nodeset=nodeset, global_var_code=codeGlobal, isArrayElement=False)
  331. code = code + code1
  332. codeCleanup = codeCleanup + codeCleanup1
  333. instanceName = generateNodeValueInstanceName(node.value[0], parentNode, 0)
  334. if not(isinstance(node.value[0], ExtensionObject)):
  335. code.append("UA_" + node.value[0].__class__.__name__ + " *" + valueName + " = UA_" + node.value[
  336. 0].__class__.__name__ + "_new();")
  337. code.append("if (!" + valueName + ") return UA_STATUSCODE_BADOUTOFMEMORY;")
  338. code.append(generateNodeValueCode("*" + valueName + " = " , node.value[0], instanceName, valueName, codeGlobal, asIndirect=True))
  339. code.append(
  340. "UA_Variant_setScalar(&attr.value, " + valueName + ", " +
  341. getTypesArrayForValue(nodeset, node.value[0]) + ");")
  342. if node.value[0].__class__.__name__ == "ByteString":
  343. # The data is on the stack, not heap, so we can not delete the ByteString
  344. codeCleanup.append("{}->data = NULL;".format(valueName))
  345. codeCleanup.append("{}->length = 0;".format(valueName))
  346. codeCleanup.append("UA_{0}_delete({1});".format(
  347. node.value[0].__class__.__name__, valueName))
  348. return [code, codeCleanup, codeGlobal]
  349. def generateMethodNodeCode(node):
  350. code = []
  351. code.append("UA_MethodAttributes attr = UA_MethodAttributes_default;")
  352. if node.executable:
  353. code.append("attr.executable = true;")
  354. if node.userExecutable:
  355. code.append("attr.userExecutable = true;")
  356. return code
  357. def generateObjectTypeNodeCode(node):
  358. code = []
  359. code.append("UA_ObjectTypeAttributes attr = UA_ObjectTypeAttributes_default;")
  360. if node.isAbstract:
  361. code.append("attr.isAbstract = true;")
  362. return code
  363. def generateDataTypeNodeCode(node):
  364. code = []
  365. code.append("UA_DataTypeAttributes attr = UA_DataTypeAttributes_default;")
  366. if node.isAbstract:
  367. code.append("attr.isAbstract = true;")
  368. return code
  369. def generateViewNodeCode(node):
  370. code = []
  371. code.append("UA_ViewAttributes attr = UA_ViewAttributes_default;")
  372. if node.containsNoLoops:
  373. code.append("attr.containsNoLoops = true;")
  374. code.append("attr.eventNotifier = (UA_Byte)%s;" % str(node.eventNotifier))
  375. return code
  376. def generateSubtypeOfDefinitionCode(node):
  377. for ref in node.inverseReferences:
  378. # 45 = HasSubtype
  379. if ref.referenceType.i == 45:
  380. return generateNodeIdCode(ref.target)
  381. return "UA_NODEID_NULL"
  382. def generateNodeCode_begin(node, nodeset, code_global):
  383. code = []
  384. codeCleanup = []
  385. code.append("UA_StatusCode retVal = UA_STATUSCODE_GOOD;")
  386. # Attributes
  387. if isinstance(node, ReferenceTypeNode):
  388. code.extend(generateReferenceTypeNodeCode(node))
  389. elif isinstance(node, ObjectNode):
  390. code.extend(generateObjectNodeCode(node))
  391. elif isinstance(node, VariableNode) and not isinstance(node, VariableTypeNode):
  392. [code1, codeCleanup1, codeGlobal1] = generateVariableNodeCode(node, nodeset)
  393. code.extend(code1)
  394. codeCleanup.extend(codeCleanup1)
  395. code_global.extend(codeGlobal1)
  396. elif isinstance(node, VariableTypeNode):
  397. [code1, codeCleanup1, codeGlobal1] = generateVariableTypeNodeCode(node, nodeset)
  398. code.extend(code1)
  399. codeCleanup.extend(codeCleanup1)
  400. code_global.extend(codeGlobal1)
  401. elif isinstance(node, MethodNode):
  402. code.extend(generateMethodNodeCode(node))
  403. elif isinstance(node, ObjectTypeNode):
  404. code.extend(generateObjectTypeNodeCode(node))
  405. elif isinstance(node, DataTypeNode):
  406. code.extend(generateDataTypeNodeCode(node))
  407. elif isinstance(node, ViewNode):
  408. code.extend(generateViewNodeCode(node))
  409. if node.displayName is not None:
  410. code.append("attr.displayName = " + generateLocalizedTextCode(node.displayName, alloc=False) + ";")
  411. if node.description is not None:
  412. code.append("#ifdef UA_ENABLE_NODESET_COMPILER_DESCRIPTIONS")
  413. code.append("attr.description = " + generateLocalizedTextCode(node.description, alloc=False) + ";")
  414. code.append("#endif")
  415. if node.writeMask is not None:
  416. code.append("attr.writeMask = %d;" % node.writeMask)
  417. if node.userWriteMask is not None:
  418. code.append("attr.userWriteMask = %d;" % node.userWriteMask)
  419. # AddNodes call
  420. code.append("retVal |= UA_Server_addNode_begin(server, UA_NODECLASS_{},".
  421. format(makeCIdentifier(node.__class__.__name__.upper().replace("NODE" ,""))))
  422. code.append(generateNodeIdCode(node.id) + ",")
  423. code.append(generateNodeIdCode(node.parent.id if node.parent else NodeId()) + ",")
  424. code.append(generateNodeIdCode(node.parentReference.id if node.parent else NodeId()) + ",")
  425. code.append(generateQualifiedNameCode(node.browseName) + ",")
  426. if isinstance(node, VariableNode) or isinstance(node, ObjectNode):
  427. typeDefRef = node.popTypeDef()
  428. code.append(generateNodeIdCode(typeDefRef.target) + ",")
  429. else:
  430. code.append(" UA_NODEID_NULL,")
  431. code.append("(const UA_NodeAttributes*)&attr, &UA_TYPES[UA_TYPES_{}ATTRIBUTES],NULL, NULL);".
  432. format(makeCIdentifier(node.__class__.__name__.upper().replace("NODE" ,""))))
  433. code.extend(codeCleanup)
  434. return "\n".join(code)
  435. def generateNodeCode_finish(node):
  436. code = []
  437. if isinstance(node, MethodNode):
  438. code.append("UA_Server_addMethodNode_finish(server, ")
  439. else:
  440. code.append("UA_Server_addNode_finish(server, ")
  441. code.append(generateNodeIdCode(node.id))
  442. if isinstance(node, MethodNode):
  443. code.append(", NULL, 0, NULL, 0, NULL);")
  444. else:
  445. code.append(");")
  446. return "\n".join(code)