backend_open62541.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  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. import codecs
  25. try:
  26. from StringIO import StringIO
  27. except ImportError:
  28. from io import StringIO
  29. logger = logging.getLogger(__name__)
  30. from constants import *
  31. from nodes import *
  32. from nodeset import *
  33. from backend_open62541_nodes import generateNodeCode_begin, generateNodeCode_finish, generateReferenceCode
  34. ##############
  35. # Sort Nodes #
  36. ##############
  37. # Select the references that shall be generated after this node in the ordering
  38. # If both nodes of the reference are hidden we assume that the references between
  39. # those nodes are already setup. Still print if only the target node is hidden,
  40. # because we need that reference.
  41. def selectPrintRefs(nodeset, L, node):
  42. printRefs = []
  43. for ref in node.references:
  44. targetnode = nodeset.nodes[ref.target]
  45. if node.hidden and targetnode.hidden:
  46. continue
  47. if not targetnode.hidden and not targetnode in L:
  48. continue
  49. printRefs.append(ref)
  50. for ref in node.inverseReferences:
  51. targetnode = nodeset.nodes[ref.target]
  52. if node.hidden and targetnode.hidden:
  53. continue
  54. if not targetnode.hidden and not targetnode in L:
  55. continue
  56. printRefs.append(ref)
  57. return printRefs
  58. def addTypeRef(nodeset, type_refs, dataTypeId, referencedById):
  59. if not dataTypeId in type_refs:
  60. type_refs[dataTypeId] = [referencedById]
  61. else:
  62. type_refs[dataTypeId].append(referencedById)
  63. def reorderNodesMinDependencies(nodeset):
  64. # Kahn's algorithm
  65. # https://algocoding.wordpress.com/2015/04/05/topological-sorting-python/
  66. relevant_types = nodeset.getRelevantOrderingReferences()
  67. # determine in-degree
  68. in_degree = {u.id: 0 for u in nodeset.nodes.values()}
  69. dataType_refs = {}
  70. hiddenCount = 0
  71. for u in nodeset.nodes.values(): # of each node
  72. if u.hidden:
  73. hiddenCount += 1
  74. continue
  75. hasTypeDef = None
  76. for ref in u.references:
  77. if ref.referenceType.i == 40:
  78. hasTypeDef = ref.target
  79. elif (ref.referenceType in relevant_types and ref.isForward) and not nodeset.nodes[ref.target].hidden:
  80. in_degree[ref.target] += 1
  81. if hasTypeDef is not None and not nodeset.nodes[hasTypeDef].hidden:
  82. # we cannot print the node u because it first needs the variable type node
  83. in_degree[u.id] += 1
  84. if isinstance(u, VariableNode) and u.dataType is not None:
  85. dataTypeNode = nodeset.getDataTypeNode(u.dataType)
  86. if dataTypeNode is not None and not dataTypeNode.hidden:
  87. # we cannot print the node u because it first needs the data type node
  88. in_degree[u.id] += 1
  89. # to be able to decrement the in_degree count, we need to store it here
  90. addTypeRef(nodeset, dataType_refs,dataTypeNode.id, u.id)
  91. # collect nodes with zero in-degree
  92. Q = deque()
  93. for id in in_degree:
  94. if in_degree[id] == 0:
  95. # print referencetypenodes first
  96. n = nodeset.nodes[id]
  97. if isinstance(n, ReferenceTypeNode):
  98. Q.append(nodeset.nodes[id])
  99. else:
  100. Q.appendleft(nodeset.nodes[id])
  101. L = [] # list for order of nodes
  102. while Q:
  103. u = Q.pop() # choose node of zero in-degree
  104. # decide which references to print now based on the ordering
  105. u.printRefs = selectPrintRefs(nodeset, L, u)
  106. if u.hidden:
  107. continue
  108. L.append(u) # and 'remove' it from graph
  109. if isinstance(u, DataTypeNode):
  110. # decrement all the nodes which depend on this datatype
  111. if u.id in dataType_refs:
  112. for n in dataType_refs[u.id]:
  113. if not nodeset.nodes[n].hidden:
  114. in_degree[n] -= 1
  115. if in_degree[n] == 0:
  116. Q.append(nodeset.nodes[n])
  117. del dataType_refs[u.id]
  118. for ref in u.inverseReferences:
  119. if ref.referenceType.i == 40:
  120. if not nodeset.nodes[ref.target].hidden:
  121. in_degree[ref.target] -= 1
  122. if in_degree[ref.target] == 0:
  123. Q.append(nodeset.nodes[ref.target])
  124. for ref in u.references:
  125. if (ref.referenceType in relevant_types and ref.isForward):
  126. if not nodeset.nodes[ref.target].hidden:
  127. in_degree[ref.target] -= 1
  128. if in_degree[ref.target] == 0:
  129. Q.append(nodeset.nodes[ref.target])
  130. if len(L) + hiddenCount != len(nodeset.nodes.values()):
  131. stillOpen = ""
  132. for id in in_degree:
  133. if in_degree[id] == 0:
  134. continue
  135. node = nodeset.nodes[id]
  136. stillOpen += node.browseName.name + "/" + str(node.id) + " = " + str(in_degree[id]) + "\r\n"
  137. raise Exception("Node graph is circular on the specified references. Still open nodes:\r\n" + stillOpen)
  138. return L
  139. ###################
  140. # Generate C Code #
  141. ###################
  142. def generateOpen62541Code(nodeset, outfilename, generate_ns0=False, internal_headers=False, typesArray=[], max_string_length=0):
  143. outfilebase = basename(outfilename)
  144. # Printing functions
  145. outfileh = codecs.open(outfilename + ".h", r"w+", encoding='utf-8')
  146. outfilec = StringIO()
  147. def writeh(line):
  148. print(unicode(line), end='\n', file=outfileh)
  149. def writec(line):
  150. print(unicode(line), end='\n', file=outfilec)
  151. additionalHeaders = ""
  152. if len(typesArray) > 0:
  153. for arr in set(typesArray):
  154. if arr == "UA_TYPES":
  155. continue
  156. additionalHeaders += """#include "%s_generated.h"\n""" % arr.lower()
  157. # Print the preamble of the generated code
  158. writeh("""/* WARNING: This is a generated file.
  159. * Any manual changes will be overwritten. */
  160. #ifndef %s_H_
  161. #define %s_H_
  162. """ % (outfilebase.upper(), outfilebase.upper()))
  163. if internal_headers:
  164. writeh("""
  165. #ifdef UA_NO_AMALGAMATION
  166. # include "ua_server.h"
  167. # include "ua_types_encoding_binary.h"
  168. #else
  169. # include "open62541.h"
  170. /* The following declarations are in the open62541.c file so here's needed when compiling nodesets externally */
  171. # ifndef UA_Nodestore_remove //this definition is needed to hide this code in the amalgamated .c file
  172. typedef UA_StatusCode (*UA_exchangeEncodeBuffer)(void *handle, UA_Byte **bufPos,
  173. const UA_Byte **bufEnd);
  174. UA_StatusCode
  175. UA_encodeBinary(const void *src, const UA_DataType *type,
  176. UA_Byte **bufPos, const UA_Byte **bufEnd,
  177. UA_exchangeEncodeBuffer exchangeCallback,
  178. void *exchangeHandle) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
  179. UA_StatusCode
  180. UA_decodeBinary(const UA_ByteString *src, size_t *offset, void *dst,
  181. const UA_DataType *type, size_t customTypesSize,
  182. const UA_DataType *customTypes) UA_FUNC_ATTR_WARN_UNUSED_RESULT;
  183. size_t
  184. UA_calcSizeBinary(void *p, const UA_DataType *type);
  185. const UA_DataType *
  186. UA_findDataTypeByBinary(const UA_NodeId *typeId);
  187. # endif // UA_Nodestore_remove
  188. #endif
  189. %s
  190. """ % (additionalHeaders))
  191. else:
  192. writeh("""
  193. #include "open62541.h"
  194. """)
  195. writeh("""
  196. #ifdef __cplusplus
  197. extern "C" {
  198. #endif
  199. extern UA_StatusCode %s(UA_Server *server);
  200. #ifdef __cplusplus
  201. }
  202. #endif
  203. #endif /* %s_H_ */""" % \
  204. (outfilebase, outfilebase.upper()))
  205. writec("""/* WARNING: This is a generated file.
  206. * Any manual changes will be overwritten. */
  207. #include "%s.h"
  208. """ % (outfilebase))
  209. parentrefs = getSubTypesOf(nodeset, nodeset.getNodeByBrowseName("HierarchicalReferences"))
  210. parentrefs = list(map(lambda x: x.id, parentrefs))
  211. # Loop over the sorted nodes
  212. logger.info("Reordering nodes for minimal dependencies during printing")
  213. sorted_nodes = reorderNodesMinDependencies(nodeset)
  214. logger.info("Writing code for nodes and references")
  215. functionNumber = 0
  216. for node in sorted_nodes:
  217. # Print node
  218. if not node.hidden:
  219. writec("\n/* " + str(node.displayName) + " - " + str(node.id) + " */")
  220. code = generateNodeCode_begin(node, nodeset, max_string_length, generate_ns0, parentrefs)
  221. if code is None:
  222. writec("/* Ignored. No parent */")
  223. nodeset.hide_node(node.id)
  224. continue
  225. else:
  226. writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_begin(UA_Server *server, UA_UInt16* ns) {\n")
  227. if isinstance(node, MethodNode):
  228. writec("#ifdef UA_ENABLE_METHODCALLS")
  229. writec(code)
  230. # Print inverse references leading to this node
  231. for ref in node.printRefs:
  232. writec(generateReferenceCode(ref))
  233. writec("return retVal;")
  234. if isinstance(node, MethodNode):
  235. writec("#else")
  236. writec("return UA_STATUSCODE_GOOD;")
  237. writec("#endif /* UA_ENABLE_METHODCALLS */")
  238. writec("}");
  239. writec("\nstatic UA_StatusCode function_" + outfilebase + "_" + str(functionNumber) + "_finish(UA_Server *server, UA_UInt16* ns) {\n")
  240. if isinstance(node, MethodNode):
  241. writec("#ifdef UA_ENABLE_METHODCALLS")
  242. code = generateNodeCode_finish(node)
  243. writec("return " + code)
  244. if isinstance(node, MethodNode):
  245. writec("#else")
  246. writec("return UA_STATUSCODE_GOOD;")
  247. writec("#endif /* UA_ENABLE_METHODCALLS */")
  248. writec("}");
  249. functionNumber = functionNumber + 1
  250. writec("""
  251. UA_StatusCode %s(UA_Server *server) {
  252. UA_StatusCode retVal = UA_STATUSCODE_GOOD;""" % (outfilebase))
  253. # Generate namespaces (don't worry about duplicates)
  254. writec("/* Use namespace ids generated by the server */")
  255. writec("UA_UInt16 ns[" + str(len(nodeset.namespaces)) + "];")
  256. for i, nsid in enumerate(nodeset.namespaces):
  257. nsid = nsid.replace("\"", "\\\"")
  258. writec("ns[" + str(i) + "] = UA_Server_addNamespace(server, \"" + nsid + "\");")
  259. for i in range(0, functionNumber):
  260. writec("retVal |= function_" + outfilebase + "_" + str(i) + "_begin(server, ns);")
  261. for i in reversed(range(0, functionNumber)):
  262. writec("retVal |= function_" + outfilebase + "_" + str(i) + "_finish(server, ns);")
  263. writec("return retVal;\n}")
  264. outfileh.close()
  265. fullCode = outfilec.getvalue()
  266. outfilec.close()
  267. outfilec = codecs.open(outfilename + ".c", r"w+", encoding='utf-8')
  268. outfilec.write(fullCode)
  269. outfilec.close()