00001 """
00002 Generate address space c++ code from xml file specification
00003 """
00004 import sys
00005
00006 import xml.etree.ElementTree as ET
00007
00008 class ObjectStruct(object):
00009 def __init__(self):
00010 self.nodetype = None
00011 self.nodeid = None
00012 self.browsename = None
00013 self.displayname = None
00014 self.symname = None
00015 self.parent = None
00016 self.parentlink = None
00017 self.desc = ""
00018 self.typedef = None
00019 self.refs = []
00020 self.nodeclass = None
00021 self.eventnotifier = 0
00022
00023
00024 self.datatype = None
00025 self.rank = -1
00026 self.value = []
00027 self.dimensions = None
00028 self.accesslevel = None
00029 self.useraccesslevel = None
00030 self.minsample = None
00031
00032
00033 self.inversename = ""
00034 self.abstract = "false"
00035 self.symmetric = "false"
00036
00037
00038 self.definition = []
00039
00040
00041
00042
00043 class RefStruct():
00044 def __init__(self):
00045 self.reftype = None
00046 self.forward = "true"
00047 self.target = None
00048
00049
00050 class CodeGenerator(object):
00051 def __init__(self, input_path, output_path):
00052 self.input_path = input_path
00053 self.output_path = output_path
00054 self.output_file = None
00055 self.part = self.input_path.split(".")[-2]
00056
00057 def run(self):
00058 sys.stderr.write("Generating C++ {} for XML file {}".format(self.output_path, self.input_path) + "\n")
00059 self.output_file = open(self.output_path, "w")
00060 self.make_header()
00061 tree = ET.parse(xmlpath)
00062 root = tree.getroot()
00063 for child in root:
00064 if child.tag[51:] == 'UAObject':
00065 node = self.parse_node(child)
00066 self.make_object_code(node)
00067 elif child.tag[51:] == 'UAObjectType':
00068 node = self.parse_node(child)
00069 self.make_object_type_code(node)
00070 elif child.tag[51:] == 'UAVariable':
00071 node = self.parse_node(child)
00072 self.make_variable_code(node)
00073 elif child.tag[51:] == 'UAVariableType':
00074 node = self.parse_node(child)
00075 self.make_variable_type_code(node)
00076 elif child.tag[51:] == 'UAReferenceType':
00077 node = self.parse_node(child)
00078 self.make_reference_code(node)
00079 elif child.tag[51:] == 'UADataType':
00080 node = self.parse_node(child)
00081 self.make_datatype_code(node)
00082 else:
00083 sys.stderr.write("Not implemented node type: " + child.tag[51:] + "\n")
00084 self.make_footer()
00085
00086 def writecode(self, *args):
00087 self.output_file.write(" ".join(args) + "\n")
00088
00089 def make_header(self, ):
00090 self.writecode('''
00091 // DO NOT EDIT THIS FILE!
00092 // It is automatically generated from opcfoundation.org schemas.
00093 //
00094
00095 #include "standard_address_space_parts.h"
00096 #include <opc/ua/protocol/string_utils.h>
00097 #include <opc/common/addons_core/addon.h>
00098 #include <opc/ua/protocol/strings.h>
00099 #include <opc/ua/protocol/variable_access_level.h>
00100 #include <opc/ua/services/node_management.h>
00101
00102 #include <algorithm>
00103 #include <iostream>
00104 #include <map>
00105
00106 namespace OpcUa
00107 {
00108 void CreateAddressSpace%s(OpcUa::NodeManagementServices& registry)
00109 {''' % (self.part))
00110
00111 def make_footer(self, ):
00112 self.writecode('''
00113 }
00114
00115 } // namespace
00116 ''')
00117
00118
00119 def parse_node(self, child):
00120 obj = ObjectStruct()
00121 obj.nodetype = child.tag[53:]
00122 for key, val in child.attrib.items():
00123 if key == "NodeId":
00124 obj.nodeid = val
00125 elif key == "BrowseName":
00126 obj.browsename = val
00127 elif key == "SymbolicName":
00128 obj.symname = val
00129 elif key == "ParentNodeId":
00130 obj.parent = val
00131 elif key == "DataType":
00132 obj.datatype = val
00133 elif key == "IsAbstract":
00134 obj.abstract = val
00135 elif key == "EventNotifier":
00136 obj.eventnotifier = val
00137 elif key == "ValueRank":
00138 obj.rank = val
00139 elif key == "ArrayDimensions":
00140 obj.dimensions = val
00141 elif key == "MinimumSamplingInterval":
00142 obj.minsample = val
00143 elif key == "AccessLevel":
00144 obj.accesslevel = val
00145 elif key == "UserAccessLevel":
00146 obj.useraccesslevel = val
00147 elif key == "Symmetric":
00148 obj.symmetric = val
00149 else:
00150 sys.stderr.write("Attribute not implemented: " + key + " " + val + "\n")
00151
00152 obj.displayname = obj.browsename
00153 for el in child:
00154 tag = el.tag[51:]
00155
00156 if tag == "DisplayName":
00157 obj.displayname = el.text
00158 elif tag == "Description":
00159 obj.desc = el.text
00160 elif tag == "References":
00161 for ref in el:
00162
00163 if ref.attrib["ReferenceType"] == "HasTypeDefinition":
00164 obj.typedef = ref.text
00165 elif "IsForward" in ref.attrib and ref.attrib["IsForward"] == "false":
00166
00167
00168 obj.parent = ref.text
00169 obj.parentlink = ref.attrib["ReferenceType"]
00170 else:
00171 struct = RefStruct()
00172 if "IsForward" in ref.attrib: struct.forward = ref.attrib["IsForward"]
00173 struct.target = ref.text
00174 struct.reftype = ref.attrib["ReferenceType"]
00175 obj.refs.append(struct)
00176 elif tag == "Value":
00177 for val in el:
00178 ntag = val.tag[47:]
00179 if ntag == "Int32":
00180 obj.value.append("(int32_t) " + val.text)
00181 elif ntag == "UInt32":
00182 obj.value.append("(uint32_t) " + val.text)
00183 elif ntag in ('ByteString', 'String'):
00184 mytext = val.text.replace('\r', '')
00185 if len(mytext) < 65535:
00186 mytext = ['"{}"'.format(x) for x in val.text.replace('\r', '').splitlines()]
00187 mytext = '\n'.join(mytext)
00188 obj.value.append('+{}'.format(mytext))
00189 else:
00190 def batch_gen(data, batch_size):
00191 for i in xrange(0, len(data), batch_size):
00192 yield data[i:i+batch_size]
00193 mytext = '({}).c_str()'.format(
00194 ' +\n'.join(
00195 ['std::string({})'.format(
00196 '\n'.join(
00197 ['"{}"'.format(x) for x in segment.splitlines()]
00198 )
00199 ) for segment in batch_gen(mytext, 65000)
00200 ]
00201 )
00202 )
00203 elif ntag == "ListOfExtensionObject":
00204 pass
00205 elif ntag == "ListOfLocalizedText":
00206 pass
00207 else:
00208 self.writecode("Missing type: ", ntag)
00209 elif tag == "InverseName":
00210 obj.inversename = el.text
00211 elif tag == "Definition":
00212 for field in el:
00213 obj.definition.append(field)
00214 else:
00215 sys.stderr.write("Not implemented tag: "+ str(el) + "\n")
00216 return obj
00217
00218 def make_node_code(self, obj, indent):
00219 self.writecode(indent, 'AddNodesItem node;')
00220 self.writecode(indent, 'node.RequestedNewNodeId = ToNodeId("{}");'.format(obj.nodeid))
00221 self.writecode(indent, 'node.BrowseName = ToQualifiedName("{}");'.format(obj.browsename))
00222 self.writecode(indent, 'node.Class = NodeClass::{};'.format(obj.nodetype))
00223 if obj.parent: self.writecode(indent, 'node.ParentNodeId = ToNodeId("{}");'.format(obj.parent))
00224 if obj.parent: self.writecode(indent, 'node.ReferenceTypeId = {};'.format(self.to_ref_type(obj.parentlink)))
00225 if obj.typedef: self.writecode(indent, 'node.TypeDefinition = ToNodeId("{}");'.format(obj.typedef))
00226
00227 def to_vector(self, dims):
00228 s = "std::vector<uint32_t>{"
00229 s += dims
00230 s+= "}"
00231 return s
00232
00233 def to_data_type(self, nodeid):
00234 if not nodeid:
00235 return "ObjectId::String"
00236 if "=" in nodeid:
00237 return 'ToNodeId("{}")'.format(nodeid)
00238 else:
00239 return 'ObjectId::{}'.format(nodeid)
00240
00241 def to_ref_type(self, nodeid):
00242 if "=" in nodeid:
00243 return 'ToNodeId("{}")'.format(nodeid)
00244 else:
00245 return 'ReferenceId::{}'.format(nodeid)
00246
00247 def make_object_code(self, obj):
00248 indent = " "
00249 self.writecode(indent)
00250 self.writecode(indent, "{")
00251 self.make_node_code(obj, indent)
00252 self.writecode(indent, 'ObjectAttributes attrs;')
00253 if obj.desc: self.writecode(indent, 'attrs.Description = LocalizedText("{}");'.format(obj.desc))
00254 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00255 self.writecode(indent, 'attrs.EventNotifier = {};'.format(obj.eventnotifier))
00256 self.writecode(indent, 'node.Attributes = attrs;')
00257 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00258 self.make_refs_code(obj, indent)
00259 self.writecode(indent, "}")
00260
00261 def make_object_type_code(self, obj):
00262 indent = " "
00263 self.writecode(indent)
00264 self.writecode(indent, "{")
00265 self.make_node_code(obj, indent)
00266 self.writecode(indent, 'ObjectTypeAttributes attrs;')
00267 if obj.desc: self.writecode(indent, 'attrs.Description = LocalizedText("{}");'.format(obj.desc))
00268 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00269 self.writecode(indent, 'attrs.IsAbstract = {};'.format(obj.abstract))
00270 self.writecode(indent, 'node.Attributes = attrs;')
00271 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00272 self.make_refs_code(obj, indent)
00273 self.writecode(indent, "}")
00274
00275
00276 def make_variable_code(self, obj):
00277 indent = " "
00278 self.writecode(indent)
00279 self.writecode(indent, "{")
00280 self.make_node_code(obj, indent)
00281 self.writecode(indent, 'VariableAttributes attrs;')
00282 if obj.desc: self.writecode(indent, 'attrs.Description = LocalizedText("{}");'.format(obj.desc))
00283 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00284 self.writecode(indent, 'attrs.Type = {};'.format(self.to_data_type(obj.datatype)))
00285 if obj.value and len(obj.value) == 1: self.writecode(indent, 'attrs.Value = {};'.format(obj.value[0]))
00286 if obj.rank: self.writecode(indent, 'attrs.Rank = {};'.format(obj.rank))
00287 if obj.accesslevel: self.writecode(indent, 'attrs.AccessLevel = (VariableAccessLevel) {};'.format(obj.accesslevel))
00288 if obj.useraccesslevel: self.writecode(indent, 'attrs.UserAccessLevel = (VariableAccessLevel) {};'.format(obj.useraccesslevel))
00289 if obj.minsample: self.writecode(indent, 'attrs.MinimumSamplingInterval = {};'.format(obj.minsample))
00290 if obj.dimensions: self.writecode(indent, 'attrs.Dimensions = {};'.format(self.to_vector(obj.dimensions)))
00291 self.writecode(indent, 'node.Attributes = attrs;')
00292 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00293 self.make_refs_code(obj, indent)
00294 self.writecode(indent, "}")
00295
00296 def make_variable_type_code(self, obj):
00297 indent = " "
00298 self.writecode(indent)
00299 self.writecode(indent, "{")
00300 self.make_node_code(obj, indent)
00301 self.writecode(indent, 'VariableTypeAttributes attrs;')
00302 if obj.desc: self.writecode(indent, 'attrs.Description = LocalizedText("{}");'.format(obj.desc))
00303 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00304 self.writecode(indent, 'attrs.Type = {};'.format(self.to_data_type(obj.datatype)))
00305 if obj.value and len(obj.value) == 1: self.writecode(indent, 'attrs.Value = {};'.format(obj.value[0]))
00306 if obj.rank: self.writecode(indent, 'attrs.Rank = {};'.format(obj.rank))
00307 if obj.abstract: self.writecode(indent, 'attrs.IsAbstract = {};'.format(obj.abstract))
00308 if obj.dimensions: self.writecode(indent, 'attrs.Dimensions = {};'.format(self.to_vector(obj.dimensions)))
00309 self.writecode(indent, 'node.Attributes = attrs;')
00310 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00311 self.make_refs_code(obj, indent)
00312 self.writecode(indent, "}")
00313
00314
00315
00316 def make_reference_code(self, obj):
00317 indent = " "
00318 self.writecode(indent)
00319 self.writecode(indent, "{")
00320 self.make_node_code(obj, indent)
00321 self.writecode(indent, 'ReferenceTypeAttributes attrs;')
00322 if obj.desc: self.writecode(indent, 'attrs.Description = LocalizedText("{}");'.format(obj.desc))
00323 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00324 if obj. inversename: self.writecode(indent, 'attrs.InverseName = LocalizedText("{}");'.format(obj.inversename))
00325 if obj.abstract: self.writecode(indent, 'attrs.IsAbstract = {};'.format(obj.abstract))
00326 if obj.symmetric: self.writecode(indent, 'attrs.Symmetric = {};'.format(obj.symmetric))
00327 self.writecode(indent, 'node.Attributes = attrs;')
00328 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00329 self.make_refs_code(obj, indent)
00330 self.writecode(indent, "}")
00331
00332 def make_datatype_code(self, obj):
00333 indent = " "
00334 self.writecode(indent)
00335 self.writecode(indent, "{")
00336 self.make_node_code(obj, indent)
00337 self.writecode(indent, 'DataTypeAttributes attrs;')
00338 if obj.desc: self.writecode(indent, u'attrs.Description = LocalizedText("{}");'.format(obj.desc.encode('ascii', 'replace')))
00339 self.writecode(indent, 'attrs.DisplayName = LocalizedText("{}");'.format(obj.displayname))
00340 if obj.abstract: self.writecode(indent, 'attrs.IsAbstract = {};'.format(obj.abstract))
00341 self.writecode(indent, 'node.Attributes = attrs;')
00342 self.writecode(indent, 'registry.AddNodes(std::vector<AddNodesItem>{node});')
00343 self.make_refs_code(obj, indent)
00344 self.writecode(indent, "}")
00345
00346 def make_refs_code(self, obj, indent):
00347 if not obj.refs:
00348 return
00349 self.writecode(indent, "std::vector<AddReferencesItem> refs;")
00350 for ref in obj.refs:
00351 self.writecode(indent, "{")
00352 self.writecode(indent, 'AddReferencesItem ref;')
00353 self.writecode(indent, 'ref.IsForward = true;')
00354 self.writecode(indent, 'ref.ReferenceTypeId = {};'.format(self.to_ref_type(ref.reftype)))
00355 self.writecode(indent, 'ref.SourceNodeId = ToNodeId("{}");'.format(obj.nodeid))
00356 self.writecode(indent, 'ref.TargetNodeClass = NodeClass::DataType;')
00357 self.writecode(indent, 'ref.TargetNodeId = ToNodeId("{}");'.format(ref.target))
00358 self.writecode(indent, "refs.push_back(ref);")
00359 self.writecode(indent, "}")
00360 self.writecode(indent, 'registry.AddReferences(refs);')
00361
00362
00363 if __name__ == "__main__":
00364 if len(sys.argv) == 2 and sys.argv[1] == "all":
00365 for i in (3, 4, 5, 8, 9, 10, 11, 13):
00366 xmlpath = "Opc.Ua.NodeSet2.Part{}.xml".format(str(i))
00367 cpppath = "../src/server/standard_address_space_part{}.cpp".format(str(i))
00368 c = CodeGenerator(xmlpath, cpppath)
00369 c.run()
00370
00371
00372 elif len(sys.argv) != 3:
00373 print(sys.argv)
00374 print("usage: generate_address_space.py xml_input_file cpp_output_file")
00375 print(" or generate_address_space.py all")
00376 sys.exit(1)
00377 else:
00378 xmlpath = sys.argv[1]
00379 cpppath = sys.argv[2]
00380 c = CodeGenerator(xmlpath, cpppath)
00381 c.run()
00382