open62541_backend.py 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  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. import string
  18. import logging; logger = logging.getLogger(__name__)
  19. from ua_constants import *
  20. from open62541_backend_nodes import Node_printOpen62541CCode, getCreateStandaloneReference
  21. ####################
  22. # Helper Functions #
  23. ####################
  24. def substitutePunctuationCharacters(input):
  25. # No punctuation characters <>!$
  26. for illegal_char in list(string.punctuation):
  27. if illegal_char == '_': # underscore is allowed
  28. continue
  29. input = input.replace(illegal_char, "_") # map to underscore
  30. return input
  31. defined_typealiases = []
  32. def getNodeIdDefineString(node):
  33. extrNs = node.browseName().split(":")
  34. symbolic_name = ""
  35. # strip all characters that would be illegal in C-Code
  36. if len(extrNs) > 1:
  37. nodename = extrNs[1]
  38. else:
  39. nodename = extrNs[0]
  40. symbolic_name = substitutePunctuationCharacters(nodename)
  41. if symbolic_name != nodename :
  42. logger.warn("Substituted characters in browsename for nodeid " + \
  43. str(node.id().i) + " while generating C-Code ")
  44. if symbolic_name in defined_typealiases:
  45. logger.warn("Typealias definition of " + str(node.id().i) + " is non unique!")
  46. extendedN = 1
  47. while (symbolic_name+"_"+str(extendedN) in defined_typealiases):
  48. logger.warn("Typealias definition of " + str(node.id().i) + " is non unique!")
  49. extendedN+=1
  50. symbolic_name = symbolic_name+"_"+str(extendedN)
  51. defined_typealiases.append(symbolic_name)
  52. return "#define UA_NS%sID_%s %s" % (node.id().ns, symbolic_name.upper(), node.id().i)
  53. ###################
  54. # Generate C Code #
  55. ###################
  56. def generateCCode(nodeset, printedExternally=[], supressGenerationOfAttribute=[],
  57. outfilename="", high_level_api=False):
  58. unPrintedNodes = []
  59. unPrintedRefs = []
  60. code = []
  61. header = []
  62. # Reorder our nodes to produce a bare minimum of bootstrapping dependencies
  63. logger.debug("Reordering nodes for minimal dependencies during printing.")
  64. nodeset.reorderNodesMinDependencies(printedExternally)
  65. # Populate the unPrinted-Lists with everything we have. Every Time a nodes
  66. # printfunction is called, it will pop itself and all printed references
  67. # from these lists.
  68. for n in nodeset.nodes:
  69. if not n in printedExternally:
  70. unPrintedNodes.append(n)
  71. else:
  72. logger.debug("Node " + str(n.id()) + " is being ignored.")
  73. for n in unPrintedNodes:
  74. for r in n.getReferences():
  75. if (r.target() != None) and (r.target().id() != None) and (r.parent() != None):
  76. unPrintedRefs.append(r)
  77. logger.debug("%d nodes and %d references need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
  78. # Print the preamble of the generated code
  79. header.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
  80. code.append("/* WARNING: This is a generated file.\n * Any manual changes will be overwritten.\n\n */")
  81. header.append('#ifndef '+outfilename.upper()+'_H_')
  82. header.append('#define '+outfilename.upper()+'_H_')
  83. header.append('#ifdef UA_NO_AMALGAMATION')
  84. header.append('#include "ua_types.h"')
  85. if high_level_api:
  86. header.append('#include "ua_job.h"')
  87. header.append('#include "ua_server.h"')
  88. if not high_level_api:
  89. header.append('#include "server/ua_server_internal.h"')
  90. header.append('#include "server/ua_nodes.h"')
  91. header.append('#include "ua_util.h"')
  92. header.append('#include "ua_types_encoding_binary.h"')
  93. header.append('#include "ua_types_generated_encoding_binary.h"')
  94. header.append('#include "ua_transport_generated_encoding_binary.h"')
  95. header.append('#else')
  96. header.append('#include "open62541.h"')
  97. header.append('#define NULL ((void *)0)')
  98. header.append('#endif')
  99. code.append('#include "'+outfilename+'.h"')
  100. code.append("UA_INLINE void "+outfilename+"(UA_Server *server) {")
  101. # Before printing nodes, we need to request additional namespace arrays from the server
  102. for nsid in nodeset.namespaceIdentifiers:
  103. if nsid == 0 or nsid==1:
  104. continue
  105. else:
  106. name = nodeset.namespaceIdentifiers[nsid]
  107. name = name.replace("\"","\\\"")
  108. code.append("UA_Server_addNamespace(server, \"" + name + "\");")
  109. # Find all references necessary to create the namespace and
  110. # "Bootstrap" them so all other nodes can safely use these referencetypes whenever
  111. # they can locate both source and target of the reference.
  112. logger.debug("Collecting all references used in the namespace.")
  113. refsUsed = []
  114. for n in nodeset.nodes:
  115. # Since we are already looping over all nodes, use this chance to print NodeId defines
  116. if n.id().ns != 0:
  117. nc = n.nodeClass()
  118. if nc != NODE_CLASS_OBJECT and nc != NODE_CLASS_VARIABLE and nc != NODE_CLASS_VIEW:
  119. header.append(getNodeIdDefineString(n))
  120. # Now for the actual references...
  121. for r in n.getReferences():
  122. # Only print valid references in namespace 0 (users will not want their refs bootstrapped)
  123. if not r.referenceType() in refsUsed and r.referenceType() != None and r.referenceType().id().ns == 0:
  124. refsUsed.append(r.referenceType())
  125. logger.debug("%d reference types are used in the namespace, which will now get bootstrapped.", len(refsUsed))
  126. for r in refsUsed:
  127. code.extend(Node_printOpen62541CCode(r, unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute))
  128. logger.debug("%d Nodes, %d References need to get printed.", len(unPrintedNodes), len(unPrintedRefs))
  129. if not high_level_api:
  130. # Note to self: do NOT - NOT! - try to iterate over unPrintedNodes!
  131. # Nodes remove themselves from this list when printed.
  132. logger.debug("Printing all other nodes.")
  133. for n in nodeset.nodes:
  134. code.extend(Node_printOpen62541CCode(n, unPrintedNodes, unPrintedRefs, supressGenerationOfAttribute))
  135. if len(unPrintedNodes) != 0:
  136. logger.warn("%d nodes could not be translated to code.", len(unPrintedNodes))
  137. else:
  138. logger.debug("Printing suceeded for all nodes")
  139. if len(unPrintedRefs) != 0:
  140. logger.debug("Attempting to print " + str(len(unPrintedRefs)) + " unprinted references.")
  141. tmprefs = []
  142. for r in unPrintedRefs:
  143. if not (r.target() not in unPrintedNodes) and not (r.parent() in unPrintedNodes):
  144. if not isinstance(r.parent(), opcua_node_t):
  145. logger.debug("Reference has no parent!")
  146. elif not isinstance(r.parent().id(), opcua_node_id_t):
  147. logger.debug("Parents nodeid is not a nodeID!")
  148. else:
  149. if (len(tmprefs) == 0):
  150. code.append("// Creating leftover references:")
  151. code.extend(getCreateStandaloneReference(r.parent(), r))
  152. code.append("")
  153. tmprefs.append(r)
  154. # Remove printed refs from list
  155. for r in tmprefs:
  156. unPrintedRefs.remove(r)
  157. if len(unPrintedRefs) != 0:
  158. logger.warn("" + str(len(unPrintedRefs)) + " references could not be translated to code.")
  159. else:
  160. logger.debug("Printing succeeded for all references")
  161. else: # Using only High Level API
  162. already_printed = list(printedExternally)
  163. while unPrintedNodes:
  164. node_found = False
  165. for node in unPrintedNodes:
  166. for ref in node.getReferences():
  167. if ref.referenceType() in already_printed and ref.target() in already_printed:
  168. node_found = True
  169. code.extend(Node_printOpen62541CCode_HL_API(node, ref, supressGenerationOfAttribute))
  170. unPrintedRefs.remove(ref)
  171. unPrintedNodes.remove(node)
  172. already_printed.append(node)
  173. break
  174. if not node_found:
  175. logger.critical("no complete code generation with high level API possible; not all nodes will be created")
  176. code.append("CRITICAL: no complete code generation with high level API possible; not all nodes will be created")
  177. break
  178. code.append("// creating references")
  179. for r in unPrintedRefs:
  180. code.extend(getCreateStandaloneReference(r.parent(), r))
  181. # finalizing source and header
  182. header.append("extern void "+outfilename+"(UA_Server *server);\n")
  183. header.append("#endif /* "+outfilename.upper()+"_H_ */")
  184. code.append("} // closing nodeset()")
  185. return (header,code)