def generateImplementationFile(self, implementation, methodList, variableList, objectList): classname = toolBox_generator.getNodeCodeName(self.objectNode) implementation.write( "/* This file has been automatically generated. Remove the subsequent line to prevent any automated changes to this file\n" ) implementation.write("* @generated \n") implementation.write("*/ \n\n") implementation.write("#include \"" + classname + ".hpp\"\n") implementation.write("extern \"C\" {\n") implementation.write(INDENT + "#include \"open62541.h\"\n") implementation.write(INDENT + "#include \"" + self.generatedNamspaceFileName + ".h\"\n") implementation.write("}\n") implementation.write("#include \"ua_proxies.h\"\n") implementation.write("#include \"ua_proxies_callback.h\"\n") implementation.write("#include \"ua_proxies_typeconversion.h\"\n\n") implementation.write("#include <tuple>\n") if self.isServerClass(classname): self.generateServerClass(implementation, methodList, variableList, objectList) else: ## binding lib's self.generateClass(implementation, methodList, variableList, objectList) self.generateDestructor(implementation, classname) self.generateVariable(implementation, variableList, classname) self.generateMethods(implementation, methodList, classname) self.generateClassMapSelfToNS(implementation, classname, variableList, methodList, objectList)
def generateServerClass(self, implementation, methodList, variableList, objectList): classname = toolBox_generator.getNodeCodeName(self.objectNode) ## hard coded value "name" and "port" -> maybe bad? # -> needed? implementation.write("#include \"string.h\"\n\n") implementation.write( classname + "::" + classname + "(std::string name, uint16_t opcuaPort) : ua_mapped_class(nullptr, UA_NODEID_NULL) {\n" ) implementation.write(INDENT + "this->name = name;\n") implementation.write( INDENT + "this->runUAServer=UA_FALSE; // Needs workerThread_setup\n") implementation.write(INDENT + "this->constructserver(opcuaPort);\n") implementation.write("}\n\n") ## Method "constructserver" (special ServerClass method) implementation.write("void " + classname + "::constructserver(uint16_t opcuaPort) {\n") implementation.write( INDENT + "this->server_config = UA_ServerConfig_standard;\n") implementation.write( INDENT + "this->server_nl = UA_ServerNetworkLayerTCP(UA_ConnectionConfig_standard, opcuaPort);\n" ) implementation.write(INDENT + "this->server_config.logger = UA_Log_Stdout;\n") implementation.write( INDENT + "this->server_config.networkLayers = &this->server_nl;\n") implementation.write(INDENT + "this->server_config.networkLayersSize = 1;\n") # FIXME: This should be taken from the config XML; implementation.write( INDENT + "this->server_config.publishingIntervalLimits = { .min = 10.0, .max = 3600.0 * 1000.0 };\n" ) implementation.write( INDENT + "this->server_config.samplingIntervalLimits = { .min = 10.0, .max = 24.0 * 3600.0 * 1000.0 };\n" ) implementation.write( INDENT + "this->mappedServer = UA_Server_new(this->server_config);\n") # FIXME: This should be taken from the config XML; only yse UA_NS0ID_OBJECTSFOLDER as a fallback implementation.write( INDENT + "this->baseNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);\n" ) implementation.write( INDENT + "//MODEL_INITIALIZER_FUNCTION(this->mappedServer);\n") implementation.write(INDENT + self.generatedNamspaceFileName + "(this->mappedServer);\n") implementation.write(INDENT + "this->mapSelfToNamespace();\n") implementation.write("}\n\n") self.generateWorkerMethode(implementation, classname)
def generateHeaderMethods(self, header, methodList): # Method header for mn in methodList: header.write( INDENT + "UA_StatusCode " + toolBox_generator.getNodeCodeName(mn) + "(size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output);\n" )
def __init__(self, namespace, objectNode, serverList=None): self.namespace = namespace self.ignoredNodes = [] self.objectNode = objectNode if serverList != None and serverList.name == toolBox_generator.getNodeCodeName( self.objectNode): self.serverList = serverList else: self.serverList = None
def generateClass(self, implementation, methodList, variableList, objectList): classname = toolBox_generator.getNodeCodeName(self.objectNode) implementation.write( classname + "::" + classname + "(std::string name, UA_NodeId baseNodeId, UA_Server* server) : ua_mapped_class(server, baseNodeId) {\n" ) implementation.write(INDENT + "this->name = name;\n") implementation.write(INDENT + "this->mapSelfToNamespace();\n") implementation.write("}\n\n")
def generateMethods(self, implementation, methodList, classname): # Variable Getters/Setters for mn in methodList: # Methde Proxy implementation.write("UA_CALLPROXY(" + classname + ", " + toolBox_generator.getNodeCodeName(mn) + ")\n") # Method header implementation.write( "UA_StatusCode " + classname + "::" + toolBox_generator.getNodeCodeName(mn) + "(size_t inputSize, const UA_Variant *input, size_t outputSize, UA_Variant *output) {\n" ) implementation.write(INDENT + "if (input == nullptr && inputSize > 0 )\n") implementation.write(INDENT + INDENT + "return UA_STATUSCODE_BADINVALIDARGUMENT;\n") implementation.write(INDENT + "if (inputSize != 0)\n") implementation.write(INDENT + INDENT + "return UA_STATUSCODE_BADINVALIDARGUMENT;\n") implementation.write(INDENT + "return UA_STATUSCODE_GOOD;\n") implementation.write("}\n\n")
def generateVariable(self, implementation, variableList, classname): # Variable Getters/Setters for vn in variableList: # Reader Proxy implementation.write( "UA_RDPROXY_" + toolBox_generator.getProxyTypeByUAType( str(vn.dataType().target().browseName())) + "(" + classname + ", get_" + toolBox_generator.getNodeCodeName(vn) + ")\n") # Getter implementation.write( toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " " + classname + "::get_" + toolBox_generator.getNodeCodeName(vn) + "() {\n") implementation.write(INDENT + "return this->" + toolBox_generator.getNodeCodeName(vn) + ";\n") implementation.write("}\n\n") # Writer Proxy implementation.write( "UA_WRPROXY_" + toolBox_generator.getProxyTypeByUAType( str(vn.dataType().target().browseName())) + "(" + classname + ", set_" + toolBox_generator.getNodeCodeName(vn) + ")\n") # Setter implementation.write( "void " + classname + "::set_" + toolBox_generator.getNodeCodeName(vn) + "(" + toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " value) {\n") implementation.write(INDENT + "this->" + toolBox_generator.getNodeCodeName(vn) + " = value;\n") implementation.write("}\n\n")
def generateHeaderFile(self, header, methodList, variableList, objectList): classname = toolBox_generator.getNodeCodeName(self.objectNode) header.write( "/* This file has been automatically generated. Remove the subsequent line to prevent any automated changes to this file\n" ) header.write("* @generated \n") header.write("*/ \n\n") # Print Header Guards header.write("#ifndef HAVE_" + classname.capitalize() + "_H\n") header.write("#define HAVE_" + classname.capitalize() + "_H\n\n") if self.isServerClass(classname): header.write("#include <ipc_managed_object.h>\n") header.write("#include \"ua_mapped_class.h\"\n\n") if self.isServerClass(classname): header.write("class " + classname + " : public ipc_managed_object, ua_mapped_class {\n") header.write("private:\n") header.write(INDENT + "UA_Boolean runUAServer;\n") header.write(INDENT + "std::thread *serverThread;\n") else: header.write("class " + classname + " : ua_mapped_class {\n") header.write("private:\n") header.write(INDENT + "std::string name;\n") header.write(INDENT + "UA_NodeId rootNodeId;\n") if self.isServerClass(classname): self.generateHeaderServerVariables(header, variableList) for vn in variableList: header.write(INDENT + toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " " + toolBox_generator.getNodeCodeName(vn) + ";\n") self.generateHeaderServerMethoden(header) print("generate server header file") else: for vn in variableList: header.write(INDENT + toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " " + toolBox_generator.getNodeCodeName(vn) + ";\n") print("generate standard header file") ''' ' Do we need this kind of object?! You should think about... ' ''' '''#objectList ' for on in objectList: ' header.write(INDENT + "//FIXME " + toolBox_generator.getCPPTypeByUAType(str(on.id())) + " " + toolBox_generator.getNodeCodeName(on) + ";\n") ''' header.write("\n") for on in objectList: header.write(INDENT + "UA_NodeId " + toolBox_generator.getNodeCodeName(on) + ";\n") header.write("\n") header.write(INDENT + "UA_StatusCode mapSelfToNamespace();\n") header.write("\n") header.write("protected:\n") header.write("\n") header.write("public:\n") if self.isServerClass(classname): header.write(INDENT + classname + "(std::string name, uint16_t opcuaPort);\n") header.write(INDENT + "void workerThread_setup();\n") header.write(INDENT + "void workerThread_iterate();\n") header.write(INDENT + "void workerThread_cleanup();\n") else: header.write( INDENT + classname + "(std::string name, UA_NodeId baseNodeId, UA_Server* server);\n" ) header.write(INDENT + "~" + classname + "();\n") header.write("\n") header.write(INDENT + "// Getter and Setter functions \n") for vn in variableList: header.write(INDENT + toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " get_" + toolBox_generator.getNodeCodeName(vn) + "();\n") header.write(INDENT + "void set_" + toolBox_generator.getNodeCodeName(vn) + "(" + toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())) + " value);\n") header.write("\n") self.generateHeaderMethods(header, methodList) header.write("\n") header.write("};\n") header.write("\n#endif // Header guard\n")
def generateClassMapSelfToNS(self, implementation, classname, variableList, methodList, objectList): ## Lastly, always add mapSelfToNamespace implementation.write("UA_StatusCode " + classname + "::mapSelfToNamespace() {\n") # Create base node implementation.write(INDENT + "/* Create Root Node */") implementation.write(INDENT + "UA_StatusCode retval = UA_STATUSCODE_GOOD;\n") implementation.write(INDENT + "UA_NodeId createdNodeId = UA_NODEID_NULL;\n") implementation.write("\n") implementation.write( INDENT + "if (UA_NodeId_equal(&this->baseNodeId, &createdNodeId) == UA_TRUE) { \n" ) implementation.write( INDENT + INDENT + "return 0; // Something went UA_WRING (initializer should have set this!)\n" ) implementation.write(INDENT + "}\n\n") implementation.write("\n") implementation.write(INDENT + "UA_ObjectAttributes oAttr;\n") implementation.write( INDENT + "oAttr.displayName = UA_LOCALIZEDTEXT_ALLOC((char*)\"en_US\", this->name.c_str());\n" ) implementation.write( INDENT + "oAttr.description = UA_LOCALIZEDTEXT_ALLOC((char*)\"en_US\", this->name.c_str());\n" ) implementation.write("\n") implementation.write(INDENT + "UA_INSTATIATIONCALLBACK(icb);\n") implementation.write( INDENT + "UA_Server_addObjectNode(this->mappedServer, UA_NODEID_NUMERIC(1,0),\n" ) if self.serverList != None and self.serverList.baseNodeId != None: # BaseNodeId from config implementation.write( INDENT + INDENT + INDENT + toolBox_generator.getNodeIdInitializerFromNodeId( self.serverList.baseNodeIdAsUAType()) + ", UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),\n") else: implementation.write( INDENT + INDENT + INDENT + "UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER), UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES),\n" ) implementation.write( INDENT + INDENT + INDENT + "UA_QUALIFIEDNAME_ALLOC(1, this->name.c_str()), " + toolBox_generator.getNodeIdInitializer(self.objectNode) + ", oAttr, &icb, &createdNodeId);\n") implementation.write( INDENT + "UA_NodeId_copy(&createdNodeId, &this->rootNodeId);\n\n") # Map function calls implementation.write(INDENT + "\n/* Contained Functions */\n") implementation.write(INDENT + "UA_FunctionCall_Map mapThis;\n") for mn in methodList: implementation.write( INDENT + "mapThis.push_back((UA_FunctionCall_Map_Element) {.typeTemplateId = " + toolBox_generator.getNodeIdInitializer(mn) + ", .lookupTable = UA_CALLPROXY_TABLE(" + classname + ", " + toolBox_generator.getNodeCodeName(mn) + "), .callback = UA_CALLPROXY_NAME(" + classname + ", " + toolBox_generator.getNodeCodeName(mn) + ") }); \n") implementation.write(INDENT + "\n/* Contained variables & Proxy mapping */\n") implementation.write( INDENT + "this->ua_mapFunctions(this, &mapThis, createdNodeId);\n\n") # Map DataSources implementation.write(INDENT + "UA_DataSource_Map mapDs;\n") # create for every var setter/getter proxys for vn in variableList: implementation.write( INDENT + "mapDs.push_back((UA_DataSource_Map_Element) { .typeTemplateId =" + toolBox_generator.getNodeIdInitializer(vn) + ", .read=UA_RDPROXY_NAME(" + classname + ", get_" + toolBox_generator.getNodeCodeName(vn) + "), .write=UA_WRPROXY_NAME(" + classname + ", set_" + toolBox_generator.getNodeCodeName(vn) + ")});\n") implementation.write( INDENT + "ua_callProxy_mapDataSources(this->mappedServer, this->ownedNodes, &mapDs, (void *) this);\n" ) implementation.write(INDENT + "\n/* Contained Objects */\n") implementation.write(INDENT + "UA_NodeId *tmpNodeId;\n") for on in objectList: implementation.write( INDENT + "tmpNodeId = nodePairList_getTargetIdBySourceId(this->ownedNodes, " + toolBox_generator.getNodeIdInitializer(on) + ");\n") implementation.write(INDENT + "if(tmpNodeId != nullptr)\n") implementation.write(INDENT + INDENT + "UA_NodeId_copy( tmpNodeId, &this->" + toolBox_generator.getNodeCodeName(on) + ");\n") implementation.write(INDENT + "return UA_STATUSCODE_GOOD;\n") implementation.write("}\n\n")
def getMembersOfType(self, objectNode): variables = [] objects = [] methods = [] # Return a Triple of (varibales, objects, methods) for a given type definition for r in objectNode.getReferences(): if r.isForward() and r.target() != None: # FIXME: We are only checking hasProperty & hasComponent, but we should be checking any derived refType as well... if((r.target().nodeClass() == NODE_CLASS_VARIABLE or r.target().nodeClass() == NODE_CLASS_VARIABLETYPE)) and \ (r.referenceType().id().ns == 0 and r.referenceType().id().i == 46 or r.referenceType().id().ns == 0 and r.referenceType().id().i == 47 ): vn = r.target() if (vn.dataType() != None): print("+-Variable" + toolBox_generator.getNodeCodeName(r.target())) # Only create encodable types! if not "NonMappableType" in toolBox_generator.getCPPTypeByUAType( str(vn.dataType().target().browseName())): vnames = [] for v in variables: vnames.append( toolBox_generator.getNodeCodeName(v)) if not toolBox_generator.getNodeCodeName( r.target()) in vnames: variables.append(vn) if not r.target() in self.ignoredNodes: t = self.getMembersOfType(r.target()) variables += t[0] objects += t[1] methods += t[2] # FIXME: We are only checking hasProperty & hasComponent, but we should be checking any derived refType as well... if (r.target().nodeClass() == NODE_CLASS_OBJECT) and \ (r.referenceType().id().ns == 0 and r.referenceType().id().i == 46 or r.referenceType().id().ns == 0 and r.referenceType().id().i == 47 ): print("+-Object" + toolBox_generator.getNodeCodeName(r.target())) if not r.target() in self.ignoredNodes: print("-- contains -->" + toolBox_generator.getNodeCodeName(r.target())) t = self.getMembersOfType(r.target()) variables += t[0] objects += t[1] methods += t[2] onames = [] for o in objects: onames.append(toolBox_generator.getNodeCodeName(o)) if not toolBox_generator.getNodeCodeName( r.target()) in onames: objects.append(r.target()) if(r.target().nodeClass() == NODE_CLASS_METHOD) and \ (r.referenceType().id().ns == 0 and r.referenceType().id().i == 46 or r.referenceType().id().ns == 0 and r.referenceType().id().i == 47 ): print("+-Method" + toolBox_generator.getNodeCodeName(r.target())) methods.append(r.target()) ## If this type inherits attributes from its parent, we need to add these to this objects list of variables/objects/methods # FIXME: We are only checking hasSubtype, but we should be checking any derived refType as well... if not r.isForward() and r.target() != None and r.referenceType( ).id().ns == 0 and r.referenceType().id().i == 45: if not r.target() in self.ignoredNodes: print("-- supertype -->" + toolBox_generator.getNodeCodeName(r.target())) t = self.getMembersOfType(r.target()) variables += t[0] objects += t[1] methods += t[2] return (variables, objects, methods)
def generateAll(self, outputPath): for objectNode in self.namespace.nodes: # TODO: clientReflection classes if objectNode.nodeClass( ) == NODE_CLASS_OBJECTTYPE and not objectNode in self.ignoredNodes: classname = toolBox_generator.getNodeCodeName(objectNode) print(classname + ".cpp, " + classname + ".hpp") cppPath = outputPath + "/serverReflection/" hppPath = cppPath if not os.path.exists(cppPath): os.makedirs(cppPath) if not os.path.exists(hppPath): os.makedirs(hppPath) logger.debug("Generating ObjectType " + str(objectNode.id()) + " " + classname) methods = [] variables = [] objects = [] print("Discovering generatable subnodes") (variables, objects, methods) = self.getMembersOfType(objectNode) ## create all files classname = toolBox_generator.getNodeCodeName(objectNode) config = None for serverConfig in self.serverHostList: if serverConfig.name == classname: config = serverConfig cppfile = cppfile_generator(self.generatedNamspaceFileName, objectNode, config) hppfile = headerfile_generator(self.namespace, objectNode, config) ''' ' Check if file still exist. ' If there is a "@generated" string in the first lines of code, the file can be reprinted ' if not, the file was changed by an human or somethink like a human... hence we dont touch it with the generator ''' if os.path.isfile(cppPath + classname + ".cpp"): existingCodeFile = open(cppPath + classname + ".cpp") for line in existingCodeFile: if (string.find(line.rstrip(), "@generated") != -1): codefile = open(cppPath + classname + ".cpp", r"w+") cppfile.generateImplementationFile( codefile, methods, variables, objects) codefile.close() break else: logger.warn( cppPath + classname + ".cpp has been modified (is missing the @generated comment). Skipping code generation for this class" ) existingCodeFile.close() else: codefile = open(cppPath + classname + ".cpp", r"w+") cppfile.generateImplementationFile(codefile, methods, variables, objects) codefile.close() if os.path.isfile(hppPath + classname + ".hpp"): existingCodeFile = open(hppPath + classname + ".hpp") for line in existingCodeFile: if (string.find(line.rstrip(), "@generated") != -1): headerfile = open(hppPath + classname + ".hpp", r"w+") hppfile.generateHeaderFile(headerfile, methods, variables, objects) headerfile.close() break else: logger.warn( cppPath + classname + ".hpp has been modified (is missing the @generated comment). Skipping code generation for this class" ) existingCodeFile.close() else: headerfile = open(hppPath + classname + ".hpp", r"w+") hppfile.generateHeaderFile(headerfile, methods, variables, objects) headerfile.close()