backend_open62541.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #!/usr/bin/env/python
  2. # -*- coding: utf-8 -*-
  3. ###
  4. ### Authors:
  5. ### - Chris Iatrou (ichrispa@core-vector.net)
  6. ### - Julius Pfrommer
  7. ### - Stefan Profanter (profanter@fortiss.org)
  8. ###
  9. ### This program was created for educational purposes and has been
  10. ### contributed to the open62541 project by the author. All licensing
  11. ### terms for this source is inherited by the terms and conditions
  12. ### specified for by the open62541 project (see the projects readme
  13. ### file for more information on the LGPL terms and restrictions).
  14. ###
  15. ### This program is not meant to be used in a production environment. The
  16. ### author is not liable for any complications arising due to the use of
  17. ### this program.
  18. ###
  19. from __future__ import print_function
  20. import string
  21. from collections import deque
  22. from os.path import basename
  23. import logging
  24. try:
  25. from StringIO import StringIO
  26. except ImportError:
  27. from io import StringIO
  28. logger = logging.getLogger(__name__)
  29. from constants import *
  30. from nodes import *
  31. from nodeset import *
  32. from backend_open62541_nodes import generateNodeCode, generateReferenceCode
  33. ##############
  34. # Sort Nodes #
  35. ##############
  36. # Select the references that shall be generated after this node in the ordering
  37. # If both nodes of the reference are hidden we assume that the references between
  38. # those nodes are already setup. Still print if only the target node is hidden,
  39. # because we need that reference.
  40. def selectPrintRefs(nodeset, L, node):
  41. printRefs = []
  42. for ref in node.references:
  43. targetnode = nodeset.nodes[ref.target]
  44. if node.hidden and targetnode.hidden:
  45. continue
  46. if not targetnode.hidden and not targetnode in L:
  47. continue
  48. printRefs.append(ref)
  49. for ref in node.inverseReferences:
  50. targetnode = nodeset.nodes[ref.target]
  51. if node.hidden and targetnode.hidden:
  52. continue
  53. if not targetnode.hidden and not targetnode in L:
  54. continue
  55. printRefs.append(ref)
  56. return printRefs
  57. def addTypeRef(nodeset, type_refs, dataTypeId, referencedById):
  58. if not dataTypeId in type_refs:
  59. type_refs[dataTypeId] = [referencedById]
  60. else:
  61. type_refs[dataTypeId].append(referencedById)
  62. def reorderNodesMinDependencies(nodeset):
  63. # Kahn's algorithm
  64. # https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
  65. relevant_types = nodeset.getRelevantOrderingReferences()
  66. # determine in-degree
  67. in_degree = {u.id: 0 for u in nodeset.nodes.values()}
  68. dataType_refs = {}
  69. hiddenCount = 0
  70. for u in nodeset.nodes.values(): # of each node
  71. if u.hidden:
  72. hiddenCount += 1
  73. continue
  74. hasTypeDef = None
  75. for ref in u.references:
  76. if ref.referenceType.i == 40:
  77. hasTypeDef = ref.target
  78. elif (ref.referenceType in relevant_types and ref.isForward) and not nodeset.nodes[ref.target].hidden:
  79. in_degree[ref.target] += 1
  80. if hasTypeDef is not None and not nodeset.nodes[hasTypeDef].hidden:
  81. # we cannot print the node u because it first needs the variable type node
  82. in_degree[u.id] += 1
  83. if isinstance(u, VariableNode) and u.dataType is not None:
  84. dataTypeNode = nodeset.getDataTypeNode(u.dataType)
  85. if dataTypeNode is not None and not dataTypeNode.hidden:
  86. # we cannot print the node u because it first needs the data type node
  87. in_degree[u.id] += 1
  88. # to be able to decrement the in_degree count, we need to store it here
  89. addTypeRef(nodeset, dataType_refs,dataTypeNode.id, u.id)
  90. # collect nodes with zero in-degree
  91. Q = deque()
  92. for id in in_degree:
  93. if in_degree[id] == 0:
  94. # print referencetypenodes first
  95. n = nodeset.nodes[id]
  96. if isinstance(n, ReferenceTypeNode):
  97. Q.append(nodeset.nodes[id])
  98. else:
  99. Q.appendleft(nodeset.nodes[id])
  100. L = [] # list for order of nodes
  101. while Q:
  102. u = Q.pop() # choose node of zero in-degree
  103. # decide which references to print now based on the ordering
  104. u.printRefs = selectPrintRefs(nodeset, L, u)
  105. if u.hidden:
  106. continue
  107. L.append(u) # and 'remove' it from graph
  108. if isinstance(u, DataTypeNode):
  109. # decrement all the nodes which depend on this datatype
  110. if u.id in dataType_refs:
  111. for n in dataType_refs[u.id]:
  112. if not nodeset.nodes[n].hidden:
  113. in_degree[n] -= 1
  114. if in_degree[n] == 0:
  115. Q.append(nodeset.nodes[n])
  116. del dataType_refs[u.id]
  117. for ref in u.inverseReferences:
  118. if ref.referenceType.i == 40:
  119. if not nodeset.nodes[ref.target].hidden:
  120. in_degree[ref.target] -= 1
  121. if in_degree[ref.target] == 0:
  122. Q.append(nodeset.nodes[ref.target])
  123. for ref in u.references:
  124. if (ref.referenceType in relevant_types and ref.isForward):
  125. if not nodeset.nodes[ref.target].hidden:
  126. in_degree[ref.target] -= 1
  127. if in_degree[ref.target] == 0:
  128. Q.append(nodeset.nodes[ref.target])
  129. if len(L) + hiddenCount != len(nodeset.nodes.values()):
  130. stillOpen = ""
  131. for id in in_degree:
  132. if in_degree[id] == 0:
  133. continue
  134. node = nodeset.nodes[id]
  135. stillOpen += node.browseName.name + "/" + str(node.id) + " = " + str(in_degree[id]) + "\r\n"
  136. raise Exception("Node graph is circular on the specified references. Still open nodes:\r\n" + stillOpen)
  137. return L
  138. ###################
  139. # Generate C Code #
  140. ###################
  141. def generateOpen62541Code(nodeset, outfilename, supressGenerationOfAttribute=[], generate_ns0=False, internal_headers=False, typesArray=[], max_string_length=0):
  142. outfilebase = basename(outfilename)
  143. # Printing functions
  144. outfileh = open(outfilename + ".h", r"w+")
  145. outfilec = StringIO()
  146. def writeh(line):
  147. print(unicode(line), end='\n', file=outfileh)
  148. def writec(line):
  149. print(unicode(line), end='\n', file=outfilec)
  150. additionalHeaders = ""
  151. if len(typesArray) > 0:
  152. for arr in set(typesArray):
  153. if arr == "UA_TYPES":
  154. continue
  155. additionalHeaders += """#include "%s_generated.h"\n""" % arr.lower()
  156. # Print the preamble of the generated code
  157. writeh("""/* WARNING: This is a generated file.
  158. * Any manual changes will be overwritten. */
  159. #ifndef %s_H_
  160. #define %s_H_
  161. """ % (outfilebase.upper(), outfilebase.upper()))
  162. if internal_headers:
  163. writeh("""
  164. #ifdef UA_NO_AMALGAMATION
  165. #include "ua_server.h"
  166. #include "ua_types_encoding_binary.h"
  167. %s
  168. #else
  169. #include "open62541.h"
  170. #endif
  171. """ % (additionalHeaders))
  172. else:
  173. writeh("""
  174. #include "open62541.h"
  175. """)
  176. writeh("""
  177. extern UA_StatusCode %s(UA_Server *server);
  178. #endif /* %s_H_ */""" % \
  179. (outfilebase, outfilebase.upper()))
  180. writec("""/* WARNING: This is a generated file.
  181. * Any manual changes will be overwritten. */
  182. #include "%s.h"
  183. UA_StatusCode %s(UA_Server *server) { // NOLINT
  184. UA_StatusCode retVal = UA_STATUSCODE_GOOD;
  185. """ % (outfilebase, outfilebase))
  186. parentrefs = getSubTypesOf(nodeset, nodeset.getNodeByBrowseName("HierarchicalReferences"))
  187. parentrefs = list(map(lambda x: x.id, parentrefs))
  188. # Generate namespaces (don't worry about duplicates)
  189. writec("/* Use namespace ids generated by the server */")
  190. for i, nsid in enumerate(nodeset.namespaces):
  191. nsid = nsid.replace("\"", "\\\"")
  192. writec("UA_UInt16 ns" + str(i) + " = UA_Server_addNamespace(server, \"" + nsid + "\");")
  193. # Loop over the sorted nodes
  194. logger.info("Reordering nodes for minimal dependencies during printing")
  195. sorted_nodes = reorderNodesMinDependencies(nodeset)
  196. logger.info("Writing code for nodes and references")
  197. for node in sorted_nodes:
  198. # Print node
  199. if not node.hidden:
  200. writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
  201. code = generateNodeCode(node, supressGenerationOfAttribute, generate_ns0, parentrefs, nodeset, max_string_length)
  202. if code is None:
  203. writec("/* Ignored. No parent */")
  204. nodeset.hide_node(node.id)
  205. continue
  206. else:
  207. writec(code)
  208. # Print inverse references leading to this node
  209. for ref in node.printRefs:
  210. writec(generateReferenceCode(ref))
  211. # Finalize the generated source
  212. writec("return retVal;")
  213. writec("} // closing nodeset()")
  214. outfileh.close()
  215. fullCode = outfilec.getvalue()
  216. outfilec.close()
  217. outfilec = open(outfilename + ".c", r"w+")
  218. outfilec.write(fullCode)
  219. outfilec.close()