nodeset_compiler.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. # This Source Code Form is subject to the terms of the Mozilla Public
  4. # License, v. 2.0. If a copy of the MPL was not distributed with this
  5. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6. ###
  7. ### Authors:
  8. ### - Chris Iatrou (ichrispa@core-vector.net)
  9. ### - Julius Pfrommer
  10. ### - Stefan Profanter (profanter@fortiss.org)
  11. ###
  12. ### This program was created for educational purposes and has been
  13. ### contributed to the open62541 project by the author. All licensing
  14. ### terms for this source is inherited by the terms and conditions
  15. ### specified for by the open62541 project (see the projects readme
  16. ### file for more information on the MPLv2 terms and restrictions).
  17. ###
  18. ### This program is not meant to be used in a production environment. The
  19. ### author is not liable for any complications arising due to the use of
  20. ### this program.
  21. ###
  22. import logging
  23. import argparse
  24. from nodeset import *
  25. parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter)
  26. parser.add_argument('-e', '--existing',
  27. metavar="<existingNodeSetXML>",
  28. type=argparse.FileType('rb'),
  29. dest="existing",
  30. action='append',
  31. default=[],
  32. help='NodeSet XML files with nodes that are already present on the server.')
  33. parser.add_argument('-x', '--xml',
  34. metavar="<nodeSetXML>",
  35. type=argparse.FileType('rb'),
  36. action='append',
  37. dest="infiles",
  38. default=[],
  39. help='NodeSet XML files with nodes that shall be generated.')
  40. parser.add_argument('outputFile',
  41. metavar='<outputFile>',
  42. help='The path/basename for the <output file>.c and <output file>.h files to be generated. This will also be the function name used in the header and c-file.')
  43. parser.add_argument('--generate-ns0',
  44. action='store_true',
  45. dest="generate_ns0",
  46. help='Omit some consistency checks for bootstrapping namespace 0, create references to parents and type definitions manually')
  47. parser.add_argument('--internal-headers',
  48. action='store_true',
  49. dest="internal_headers",
  50. help='Include internal headers instead of amalgamated header')
  51. parser.add_argument('-b', '--blacklist',
  52. metavar="<blacklistFile>",
  53. type=argparse.FileType('r'),
  54. action='append',
  55. dest="blacklistFiles",
  56. default=[],
  57. help='Loads a list of NodeIDs stored in blacklistFile (one NodeID per line). Any of the nodeIds encountered in this file will be removed from the nodeset prior to compilation. Any references to these nodes will also be removed')
  58. parser.add_argument('-i', '--ignore',
  59. metavar="<ignoreFile>",
  60. type=argparse.FileType('r'),
  61. action='append',
  62. dest="ignoreFiles",
  63. default=[],
  64. help='Loads a list of NodeIDs stored in ignoreFile (one NodeID per line). Any of the nodeIds encountered in this file will be kept in the nodestore but not printed in the generated code')
  65. parser.add_argument('-t', '--types-array',
  66. metavar="<typesArray>",
  67. action='append',
  68. type=str,
  69. dest="typesArray",
  70. default=[],
  71. help='Types array for the given namespace. Can be used mutliple times to define (in the same order as the .xml files, first for --existing, then --xml) the type arrays')
  72. parser.add_argument('--encode-binary-size',
  73. type=int,
  74. dest="encode_binary_size",
  75. default=32000,
  76. help='Size of the temporary array used to encode custom datatypes. If you don\'t know what it is, do not use this option')
  77. parser.add_argument('-v', '--verbose', action='count',
  78. default=1,
  79. help='Make the script more verbose. Can be applied up to 4 times')
  80. parser.add_argument('--backend',
  81. default='open62541',
  82. const='open62541',
  83. nargs='?',
  84. choices=['open62541', 'graphviz'],
  85. help='Backend for the output files (default: %(default)s)')
  86. args = parser.parse_args()
  87. # Set up logging
  88. logger = logging.getLogger(__name__)
  89. logger.setLevel(logging.INFO)
  90. verbosity = 0
  91. if args.verbose:
  92. verbosity = int(args.verbose)
  93. if (verbosity == 1):
  94. logging.basicConfig(level=logging.ERROR)
  95. elif (verbosity == 2):
  96. logging.basicConfig(level=logging.WARNING)
  97. elif (verbosity == 3):
  98. logging.basicConfig(level=logging.INFO)
  99. elif (verbosity >= 4):
  100. logging.basicConfig(level=logging.DEBUG)
  101. else:
  102. logging.basicConfig(level=logging.CRITICAL)
  103. # Create a new nodeset. The nodeset name is not significant.
  104. # Parse the XML files
  105. ns = NodeSet()
  106. nsCount = 0
  107. def getTypesArray(nsIdx):
  108. if nsIdx < len(args.typesArray):
  109. return args.typesArray[nsIdx]
  110. else:
  111. return "UA_TYPES"
  112. for xmlfile in args.existing:
  113. logger.info("Preprocessing (existing) " + str(xmlfile.name))
  114. ns.addNodeSet(xmlfile, True, typesArray=getTypesArray(nsCount))
  115. nsCount +=1
  116. for xmlfile in args.infiles:
  117. logger.info("Preprocessing " + str(xmlfile.name))
  118. ns.addNodeSet(xmlfile, typesArray=getTypesArray(nsCount))
  119. nsCount +=1
  120. # # We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
  121. # namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
  122. # for key in namespaceArrayNames:
  123. # ns.addNamespace(key, namespaceArrayNames[key])
  124. # Remove blacklisted nodes from the nodeset
  125. # Doing this now ensures that unlinkable pointers will be cleanly removed
  126. # during sanitation.
  127. for blacklist in args.blacklistFiles:
  128. for line in blacklist.readlines():
  129. line = line.replace(" ", "")
  130. id = line.replace("\n", "")
  131. if ns.getNodeByIDString(id) == None:
  132. logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
  133. else:
  134. ns.removeNodeById(line)
  135. blacklist.close()
  136. # Set the nodes from the ignore list to hidden. This removes them from dependency calculation
  137. # and from printing their generated code.
  138. # These nodes should be already pre-created on the server to avoid any errors during
  139. # creation.
  140. for ignoreFile in args.ignoreFiles:
  141. for line in ignoreFile.readlines():
  142. line = line.replace(" ", "")
  143. id = line.replace("\n", "")
  144. ns.hide_node(NodeId(id))
  145. #if not ns.hide_node(NodeId(id)):
  146. # logger.info("Can't ignore node, namespace does currently not contain a node with id " + str(id))
  147. ignoreFile.close()
  148. # Remove nodes that are not printable or contain parsing errors, such as
  149. # unresolvable or no references or invalid NodeIDs
  150. ns.sanitize()
  151. # Parse Datatypes in order to find out what the XML keyed values actually
  152. # represent.
  153. # Ex. <rpm>123</rpm> is not encodable
  154. # only after parsing the datatypes, it is known that
  155. # rpm is encoded as a double
  156. ns.buildEncodingRules()
  157. # Allocate/Parse the data values. In order to do this, we must have run
  158. # buidEncodingRules.
  159. ns.allocateVariables()
  160. ns.addInverseReferences()
  161. logger.info("Generating Code for Backend: {}".format(args.backend))
  162. if args.backend == "open62541":
  163. # Create the C code with the open62541 backend of the compiler
  164. from backend_open62541 import generateOpen62541Code
  165. generateOpen62541Code(ns, args.outputFile, args.generate_ns0, args.internal_headers, args.typesArray, args.encode_binary_size)
  166. elif args.backend == "graphviz":
  167. from backend_graphviz import generateGraphvizCode
  168. generateGraphvizCode(ns, filename=args.outputFile)
  169. else:
  170. logger.error("Unsupported backend: {}".format(args.backend))
  171. exit(1)
  172. logger.info("NodeSet generation code successfully printed")