generate_open62541CCode.py 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. #!/usr/bin/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 __future__ import print_function
  18. from sys import argv, exit
  19. from os import path
  20. from ua_namespace import *
  21. from logger import *
  22. def usage():
  23. print("Script usage:")
  24. print("generate_open62541CCode [-i <ignorefile> | -b <blacklistfile>] <namespace XML> [namespace.xml[ namespace.xml[...]]] <output file>\n")
  25. print("generate_open62541CCode will first read all XML files passed on the command line, then ")
  26. print("link and check the namespace. All nodes that fullfill the basic requirements will then be")
  27. print("printed as C-Code intended to be included in the open62541 OPC-UA Server that will")
  28. print("initialize the corresponding name space.\n")
  29. print("Manditory Arguments:")
  30. print("<namespace XML> At least one Namespace XML file must be passed.")
  31. print("<output file> The basename for the <output file>.c and <output file>.h files to be generated.")
  32. print(" This will also be the function name used in the header and c-file.\n\n")
  33. print("Additional Arguments:")
  34. print(""" -i <ignoreFile> Loads a list of NodeIDs stored in ignoreFile (one NodeID per line)
  35. The compiler will assume that these Nodes have been created externally
  36. and not generate any code for them. They will however be linked to
  37. from other nodes.""")
  38. print(""" -b <blacklistFile> Loads a list of NodeIDs stored in blacklistFile (one NodeID per line)
  39. Any of the nodeIds encountered in this file will be removed from the namespace
  40. prior to compilation. Any references to these nodes will also be removed""")
  41. print(""" -s <attribute> Suppresses the generation of some node attributes. Currently supported
  42. options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask'
  43. and 'nodeid'.""")
  44. print(""" namespaceXML Any number of namespace descriptions in XML format. Note that the
  45. last description of a node encountered will be used and all prior definitions
  46. are discarded.""")
  47. if __name__ == '__main__':
  48. # Check if the parameters given correspond to actual files
  49. infiles = []
  50. ouffile = ""
  51. ignoreFiles = []
  52. blacklistFiles = []
  53. supressGenerationOfAttribute=[]
  54. GLOBAL_LOG_LEVEL = LOG_LEVEL_DEBUG
  55. arg_isIgnore = False
  56. arg_isBlacklist = False
  57. arg_isSupress = False
  58. if len(argv) < 2:
  59. usage()
  60. exit(1)
  61. for filename in argv[1:-1]:
  62. if arg_isIgnore:
  63. arg_isIgnore = False
  64. if path.exists(filename):
  65. ignoreFiles.append(filename)
  66. else:
  67. log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
  68. usage()
  69. exit(1)
  70. elif arg_isBlacklist:
  71. arg_isBlacklist = False
  72. if path.exists(filename):
  73. blacklistFiles.append(filename)
  74. else:
  75. log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
  76. usage()
  77. exit(1)
  78. elif arg_isSupress:
  79. arg_isSupress = False
  80. supressGenerationOfAttribute.append(filename.lower())
  81. else:
  82. if path.exists(filename):
  83. infiles.append(filename)
  84. elif filename.lower() == "-i" or filename.lower() == "--ignore" :
  85. arg_isIgnore = True
  86. elif filename.lower() == "-b" or filename.lower() == "--blacklist" :
  87. arg_isBlacklist = True
  88. elif filename.lower() == "-s" or filename.lower() == "--suppress" :
  89. arg_isSupress = True
  90. else:
  91. log(None, "File " + str(filename) + " does not exist.", LOG_LEVEL_ERROR)
  92. usage()
  93. exit(1)
  94. # Creating the header is tendious. We can skip the entire process if
  95. # the header exists.
  96. #if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
  97. # log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
  98. # log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
  99. # exit(0)
  100. # Open the output file
  101. outfileh = open(argv[-1]+".h", r"w+")
  102. outfilec = open(argv[-1]+".c", r"w+")
  103. # Create a new namespace
  104. # Note that the name is actually completely symbolic, it has no other
  105. # function but to distinguish this specific class.
  106. # A namespace class acts as a container for nodes. The nodes may belong
  107. # to any number of different OPC-UA namespaces.
  108. ns = opcua_namespace("open62541")
  109. # Parse the XML files
  110. for xmlfile in infiles:
  111. log(None, "Parsing " + str(xmlfile), LOG_LEVEL_INFO)
  112. ns.parseXML(xmlfile)
  113. # Remove blacklisted nodes from the namespace
  114. # Doing this now ensures that unlinkable pointers will be cleanly removed
  115. # during sanitation.
  116. for blacklist in blacklistFiles:
  117. bl = open(blacklist, "r")
  118. for line in bl.readlines():
  119. line = line.replace(" ","")
  120. id = line.replace("\n","")
  121. if ns.getNodeByIDString(id) == None:
  122. log(None, "Can't blacklist node, namespace does currently not contain a node with id " + str(id), LOG_LEVEL_WARN)
  123. else:
  124. ns.removeNodeById(line)
  125. bl.close()
  126. # Link the references in the namespace
  127. log(None, "Linking namespace nodes and references", LOG_LEVEL_INFO)
  128. ns.linkOpenPointers()
  129. # Remove nodes that are not printable or contain parsing errors, such as
  130. # unresolvable or no references or invalid NodeIDs
  131. ns.sanitize()
  132. # Parse Datatypes in order to find out what the XML keyed values actually
  133. # represent.
  134. # Ex. <rpm>123</rpm> is not encodable
  135. # only after parsing the datatypes, it is known that
  136. # rpm is encoded as a double
  137. log(None, "Building datatype encoding rules", LOG_LEVEL_INFO)
  138. ns.buildEncodingRules()
  139. # Allocate/Parse the data values. In order to do this, we must have run
  140. # buidEncodingRules.
  141. log(None, "Allocating variables", LOG_LEVEL_INFO)
  142. ns.allocateVariables()
  143. # Users may have manually defined some nodes in their code already (such as serverStatus).
  144. # To prevent those nodes from being reprinted, we will simply mark them as already
  145. # converted to C-Code. That way, they will still be reffered to by other nodes, but
  146. # they will not be created themselves.
  147. ignoreNodes = []
  148. for ignore in ignoreFiles:
  149. ig = open(ignore, "r")
  150. for line in ig.readlines():
  151. line = line.replace(" ","")
  152. id = line.replace("\n","")
  153. if ns.getNodeByIDString(id) == None:
  154. log(None, "Can't ignore node, Namespace does currently not contain a node with id " + str(id), LOG_LEVEL_WARN)
  155. else:
  156. ignoreNodes.append(ns.getNodeByIDString(id))
  157. ig.close()
  158. # Create the C Code
  159. log(None, "Generating Header", LOG_LEVEL_INFO)
  160. # Returns a tuple of (["Header","lines"],["Code","lines","generated"])
  161. generatedCode=ns.printOpen62541Header(ignoreNodes, supressGenerationOfAttribute, outfilename=path.basename(argv[-1]))
  162. for line in generatedCode[0]:
  163. outfileh.write(line+"\n")
  164. for line in generatedCode[1]:
  165. outfilec.write(line+"\n")
  166. outfilec.close()
  167. outfileh.close()
  168. exit(0)