ChimeraTK-ControlSystemAdapter-OPCUAAdapter  04.00.01
generate_open62541CCode.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 
4 # This Source Code Form is subject to the terms of the Mozilla Public
5 # License, v. 2.0. If a copy of the MPL was not distributed with this
6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 
8 
22 
23 from __future__ import print_function
24 from ua_namespace import *
25 import logging
26 import argparse
27 from open62541_XMLPreprocessor import open62541_XMLPreprocessor
28 
29 logger = logging.getLogger(__name__)
30 
31 parser = argparse.ArgumentParser(
32  description="""Parse OPC UA NamespaceXML file(s) and create C code for generating nodes in open62541
33 
34 generate_open62541CCode.py will first read all XML files passed on the command line, then link and check the namespace. All nodes that fulfill the basic requirements will then be printed as C-Code intended to be included in the open62541 OPC UA Server that will initialize the corresponding namespace.""",
35  formatter_class=argparse.RawDescriptionHelpFormatter)
36 parser.add_argument('infiles',
37  metavar="<namespaceXML>",
38  nargs='+',
39  type=argparse.FileType('r'),
40  help='Namespace XML file(s). Note that the last definition of a node encountered will be used and all prior definitions are discarded.')
41 parser.add_argument('outputFile',
42  metavar='<outputFile>',
43  #type=argparse.FileType('w', 0),
44  help='The 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.')
45 parser.add_argument('-i','--ignore',
46  metavar="<ignoreFile>",
47  type=argparse.FileType('r'),
48  action='append',
49  dest="ignoreFiles",
50  default=[],
51  help='Loads a list of NodeIDs stored in ignoreFile (one NodeID per line). The compiler will assume that these nodes have been created externally and not generate any code for them. They will however be linked to from other nodes.')
52 parser.add_argument('-b','--blacklist',
53  metavar="<blacklistFile>",
54  type=argparse.FileType('r'),
55  action='append',
56  dest="blacklistFiles",
57  default=[],
58  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 namespace prior to compilation. Any references to these nodes will also be removed')
59 parser.add_argument('-s','--suppress',
60  metavar="<attribute>",
61  action='append',
62  dest="suppressedAttributes",
63  choices=['description', 'browseName', 'displayName', 'writeMask', 'userWriteMask','nodeid'],
64  default=[],
65  help="Suppresses the generation of some node attributes. Currently supported options are 'description', 'browseName', 'displayName', 'writeMask', 'userWriteMask' and 'nodeid'.")
66 
67 parser.add_argument('-v','--verbose', action='count', help='Make the script more verbose. Can be applied up to 4 times')
68 
69 args = parser.parse_args()
70 
71 level = logging.CRITICAL
72 verbosity = 0
73 if args.verbose:
74  verbosity = int(args.verbose)
75 if (verbosity==1):
76  level = logging.ERROR
77 elif (verbosity==2):
78  level = logging.WARNING
79 elif (verbosity==3):
80  level = logging.INFO
81 elif (verbosity>=4):
82  level = logging.DEBUG
83 
84 logging.basicConfig(level=level)
85 logger.setLevel(logging.INFO)
86 
87 # Creating the header is tendious. We can skip the entire process if
88 # the header exists.
89 #if path.exists(argv[-1]+".c") or path.exists(argv[-1]+".h"):
90 # log(None, "File " + str(argv[-1]) + " does already exists.", LOG_LEVEL_INFO)
91 # log(None, "Header generation will be skipped. Delete the header and rerun this script if necessary.", LOG_LEVEL_INFO)
92 # exit(0)
93 
94 # Open the output file
95 outfileh = open(args.outputFile+".h", r"w+")
96 outfilec = open(args.outputFile+".c", r"w+")
97 
98 # Create a new namespace. Note that the namespace name is not significant.
99 ns = opcua_namespace("open62541")
100 
101 # Clean up the XML files by removing duplicate namespaces and unwanted prefixes
103 for xmlfile in args.infiles:
104  logger.info("Preprocessing " + str(xmlfile.name))
105  preProc.addDocument(xmlfile.name)
106 preProc.preprocessAll()
107 
108 # Parse the XML files
109 for xmlfile in preProc.getPreProcessedFiles():
110  logger.info("Parsing " + str(xmlfile))
111  ns.parseXML(xmlfile)
112 
113 # We need to notify the open62541 server of the namespaces used to be able to use i.e. ns=3
114 namespaceArrayNames = preProc.getUsedNamespaceArrayNames()
115 for key in namespaceArrayNames:
116  ns.addNamespace(key, namespaceArrayNames[key])
117 
118 # Remove any temp files - they are not needed after the AST is created
119 preProc.removePreprocessedFiles()
120 
121 # Remove blacklisted nodes from the namespace
122 # Doing this now ensures that unlinkable pointers will be cleanly removed
123 # during sanitation.
124 for blacklist in args.blacklistFiles:
125  for line in blacklist.readlines():
126  line = line.replace(" ","")
127  id = line.replace("\n","")
128  if ns.getNodeByIDString(id) == None:
129  logger.info("Can't blacklist node, namespace does currently not contain a node with id " + str(id))
130  else:
131  ns.removeNodeById(line)
132  blacklist.close()
133 
134 # Link the references in the namespace
135 logger.info("Linking namespace nodes and references")
136 ns.linkOpenPointers()
137 
138 # Remove nodes that are not printable or contain parsing errors, such as
139 # unresolvable or no references or invalid NodeIDs
140 ns.sanitize()
141 
142 # Parse Datatypes in order to find out what the XML keyed values actually
143 # represent.
144 # Ex. <rpm>123</rpm> is not encodable
145 # only after parsing the datatypes, it is known that
146 # rpm is encoded as a double
147 logger.info("Building datatype encoding rules")
148 ns.buildEncodingRules()
149 
150 # Allocate/Parse the data values. In order to do this, we must have run
151 # buidEncodingRules.
152 logger.info("Allocating variables")
153 ns.allocateVariables()
154 
155 # Users may have manually defined some nodes in their code already (such as serverStatus).
156 # To prevent those nodes from being reprinted, we will simply mark them as already
157 # converted to C-Code. That way, they will still be referred to by other nodes, but
158 # they will not be created themselves.
159 ignoreNodes = []
160 for ignore in args.ignoreFiles:
161  for line in ignore.readlines():
162  line = line.replace(" ","")
163  id = line.replace("\n","")
164  if ns.getNodeByIDString(id) == None:
165  logger.warn("Can't ignore node, Namespace does currently not contain a node with id " + str(id))
166  else:
167  ignoreNodes.append(ns.getNodeByIDString(id))
168  ignore.close()
169 
170 # Create the C Code
171 logger.info("Generating Header")
172 # Returns a tuple of (["Header","lines"],["Code","lines","generated"])
173 from os.path import basename
174 generatedCode = ns.printOpen62541Header(ignoreNodes, args.suppressedAttributes, outfilename=basename(args.outputFile))
175 for line in generatedCode[0]:
176  outfileh.write(line+"\n")
177 for line in generatedCode[1]:
178  outfilec.write(line+"\n")
179 
180 outfilec.close()
181 outfileh.close()
182 
183 logger.info("Namespace generation code successfully printed")
open62541_XMLPreprocessor.open62541_XMLPreprocessor
Definition: open62541_XMLPreprocessor.py:245
ua_namespace.opcua_namespace
Namespace Organizer.
Definition: ua_namespace.py:49
str
#define str(a)
Definition: ua_adapter.cpp:52