From f3908fe7ce43b0c4c185269c259ca55b3d25c718 Mon Sep 17 00:00:00 2001 From: Susa Giogoli <su.giogoli@icloud.com> Date: Fri, 14 Jul 2023 14:31:34 +0200 Subject: [PATCH] more cleaning and documentation --- newmodeljsonparse.py | 144 ++++++++++++++++++++++++++--------- writefile.py | 174 +++++++++++++++++++------------------------ 2 files changed, 187 insertions(+), 131 deletions(-) diff --git a/newmodeljsonparse.py b/newmodeljsonparse.py index 8aa643a..730f3c2 100644 --- a/newmodeljsonparse.py +++ b/newmodeljsonparse.py @@ -8,16 +8,16 @@ from collections import namedtuple This File takes the json structure and parses it, to pass it to writefile.py Functions: measurementJsonDecod, parameterJsonDecod, functionJsonDecod, dimensionchange, getdatatypes, changerange, getunit, getobjnames, getMeasurements, getParameters, getFunctions, searchlists, getdefaultvalue, - iskeythere, getInterfaceParam, getVariables, getFunctionVariables, getEvents, getStreams, buildComponents, + iskeythere, getInterfaceParam, getComponentVarList buildComponents, instantiate, instantiateComponents, main Classes: Measurement, Parameter, Function """ -#creates the constructor for the measurement-object with corresponding parameters + def measurementJsonDecod(measurementdict): """ - Method that takes a measurementdict and pareses it into an object. + Method that takes a measurementdict and creates a construnctor with corresponding parameters. :param measurementdict: Measurement variable as Dictionary :return: Tuple with all valuessorted according to an object hook """ @@ -27,7 +27,7 @@ def measurementJsonDecod(measurementdict): #creates the constructor for the parameter-object with corresponding parameters def parameterJsonDecod(parameterdict): """ - Method that takes a parametertdict and pareses it into an object. + Method that takes a parameterdict and creates a construnctor with corresponding parameters. :param parameterdict: Parameter variable as Dictionary :return: Tuple with all valuessorted according to an object hook """ @@ -37,14 +37,13 @@ def parameterJsonDecod(parameterdict): #creates the constructor for the function-object with corresponding parameters def functionJsonDecod(functiondict): """ - Method that takes a functiondict and pareses it into an object. + Method that takes a functiondict and creates a construnctor with corresponding parameters. :param functiondict: Function as Dictionary :return: Tuple with all valuessorted according to an object hook """ return namedtuple('Function', functiondict.keys())(*functiondict.values()) -#Measurement class that can be instantiated using JsonDecod functions class Measurement: """ Class for measurement variables, is instantiated using MeasurementJsonDecod. @@ -124,8 +123,12 @@ class Function: self.outargsdatatypes = outargsdatatypes -#switches the dimension into the right format def dimensionchange(string): + """ + Changes the Format of the dimension from [dimension] to dimension + :param string: dimension of the json object + :return: dimension without braces or 0 if dimension is an empty list + """ dimarr = string["dimension"] if type(dimarr) == list: if dimarr: @@ -137,8 +140,12 @@ def dimensionchange(string): return string -#gets the datatypes of functions to set for opcua def getdatatypes(argarray): + """ + Retrieves the datatypes of an arguments of an argumentarry. + :param argarray: array of argument names + :return: array of argument datatypes + """ returnarr = [] if len(argarray) > 0: for i in argarray: @@ -148,8 +155,12 @@ def getdatatypes(argarray): return None -#switches range into the right format def changerange(obj): + """ + Adds and changes the range attribute to [None, None] for enums and parameters without range. + :param obj: The Json object that will be changed + :return: Changed object + """ if iskeythere(obj, "range"): if obj["datatype"] == "enum": obj.update({"range": [None, None]}) @@ -159,6 +170,11 @@ def changerange(obj): def getunit(string): + """ + Adds unit to parameters without unit. + :param string: The json object that will be changed + :return: changed object + """ if not iskeythere(string, "unit"): string["unit"] = "UNITLESS" return string @@ -166,6 +182,11 @@ def getunit(string): #gets the objectnames for arguments of the function def getobjnames(argarray): + """ + Changes the object names of function in order for them to fit the coding standard. + :param argarray: array of arguments + :return: array of changed arguments + """ returnarr = [] if len(argarray) > 0: for i in argarray: @@ -176,17 +197,17 @@ def getobjnames(argarray): return None -#returns a list of all the parameters as objects -def getParameters(parametersDict, variablelist): +def getParameters(parametersDict): + """ + Parses parameterdicts into parameterobjects. + :param parametersDict: List of parameterdicts + :return: List of parameter objects + """ # extracts the measurements of a Json-Mode parameterobj = [] if len(parametersDict) > 0: for i in parametersDict: - obj = i.copy() - for var in variablelist: - if obj["uuid"] == var["value"]: - obj["name"] = var["name"] - variablelist.remove(var) + obj = i obj = dimensionchange(obj) obj["value"] = getdefaultvalue(obj) obj = changerange(obj) @@ -197,8 +218,12 @@ def getParameters(parametersDict, variablelist): return parameterobj -#returns a list of all the measurements as objects -def getMeasurements(measurementsDict, variablelist): +def getMeasurements(measurementsDict): + """ + Parses measurementdicts into measurement objects + :param measurementsDict: List of measurementdicts + :return:List of measurement objects + """ measurementobj = [] if len(measurementsDict) > 0: for i in measurementsDict: @@ -216,6 +241,13 @@ def getMeasurements(measurementsDict, variablelist): #returns a list of all the functions as objects def getFunctions(functionsDict, jsonmeasurements, jsonparameters): + """ + Parses functiondicts into function objects. + :param functionsDict: list of functiondicts + :param jsonmeasurements: List of all measurement elements + :param jsonparameters: List of all parameter elements + :return: List of function objects + """ functionobj = [] if len(functionsDict) > 0: for i in functionsDict: @@ -236,6 +268,12 @@ def getFunctions(functionsDict, jsonmeasurements, jsonparameters): def searchlists(list1, list2): + """ + Matches the objects of two lists + :param list1: List of Elements from a Component Format {name: value} + :param list2: List of all elements of that type in general + :return: list of Elements from a component in the {elementtype: ,name: , uuid: , ...} format + """ objmeasurementlist = [] if len(list1) <= 0: return [] @@ -247,6 +285,11 @@ def searchlists(list1, list2): def getdefaultvalue(obj): + """ + Adds a value to a dict. In case of Parameters with a default value it just takes that + :param obj: the parameterdict before the changes + :return: the new value + """ if iskeythere(obj, "default"): if obj["datatype"] == "string" or obj["datatype"] == "enum": return "\"" + obj["default"] + "\"" @@ -269,14 +312,30 @@ def getdefaultvalue(obj): return "\"\"" -def iskeythere(list, key): - for keysearch in list: +def iskeythere(list1, key): + """ + Verifies if the key exists in the dict. + :param list1: list of all keys from the object + :param key: searched key + :return: Boolean value determining if the key is existent in the dict. + """ + for keysearch in list1: if keysearch == key: return True return False def getInterfaceparam(list1, interface, searched, jsoncomponents, searchinglist): + """ + Gets a list of searched elements from a component and their baseComponents + and in the Format {elementtype: , name: , uuid: ,...} + :param list1: list in which the retrieved elements are saved + :param interface: the component the values belong to + :param searched: the elementtype that is looked for + :param jsoncomponents: a list of all components + :param searchinglist: a list of all elements of the searched elementtype + :return: returns the list in which the retrieved elements were saved + """ for inputsearch in interface[searched]: for searchobj in searchinglist: if inputsearch["value"] == searchobj["uuid"]: @@ -288,7 +347,16 @@ def getInterfaceparam(list1, interface, searched, jsoncomponents, searchinglist) else: return list1 + def getComponentVarList(searched, list1, component, jsoncomponents): + """ + Seatches recursivly through the component and its baseComponents for elements of the serached type. + :param searched: Elementtype that is searched + :param list1: the list in which all retrieved dicts get stored + :param component: The component which is viewed at the moment + :param jsoncomponents: a list of all components + :return:The list with all stored elements. It has the Format {name : value} + """ variablelist = component[searched] list1 = list1 + variablelist if iskeythere(list(component.keys()), "baseComponent"): @@ -300,6 +368,21 @@ def getComponentVarList(searched, list1, component, jsoncomponents): def buildComponents(interfacesreturn, variabledict, jsoninterfaces, jsoncomponents, jsonmeasurements, jsonparameters, jsonfunctions, objcomponentsclone, enumnodedict, enumvalues, enumnames): + """ + Iterates recursively through + :param interfacesreturn: + :param variabledict: + :param jsoninterfaces: + :param jsoncomponents: + :param jsonmeasurements: + :param jsonparameters: + :param jsonfunctions: + :param objcomponentsclone: + :param enumnodedict: + :param enumvalues: + :param enumnames: + :return: + """ for obj in jsoninterfaces: rootobj = {} if obj["elementType"] == "interface": @@ -316,24 +399,20 @@ def buildComponents(interfacesreturn, variabledict, jsoninterfaces, jsoncomponen objmeasurementlist = getInterfaceparam([], rootobj, "measurements", jsoncomponents, jsonmeasurements) measurementlist = getComponentVarList("measurements", [], rootobj, jsoncomponents) parameterlist = getComponentVarList("parameters", [], rootobj, jsoncomponents) - #variablelist = measurementlist + parameterlist - measurementsobjs = getMeasurements(objmeasurementlist, measurementlist.copy()) - wf.writemeasurement(measurementsobjs, interfacesreturn[str(obj["uuid"])], enumnodedict, enumvalues, enumnames, measurementlist.copy()) - #variabledict[obj["uuid"]] = variablelist + measurementsobjs = getMeasurements(objmeasurementlist) + wf.writemeasurement(measurementsobjs, interfacesreturn[str(obj["uuid"])], enumnodedict, enumvalues, measurementlist.copy()) objparameterlist = getInterfaceparam([], rootobj, "parameters", jsoncomponents, jsonparameters) - parameterobjs = getParameters(objparameterlist, parameterlist.copy()) - wf.writeparameter(parameterobjs, interfacesreturn[str(obj["uuid"])], enumnodedict, enumvalues, enumnames, parameterlist.copy()) + parameterobjs = getParameters(objparameterlist) + wf.writeparameter(parameterobjs, interfacesreturn[str(obj["uuid"])], enumnodedict, enumvalues, parameterlist.copy()) objfunctionlist = getInterfaceparam([], rootobj, "functions", jsoncomponents, jsonfunctions) functionobj = getFunctions(objfunctionlist, jsonparameters, jsonmeasurements) functionvars = getComponentVarList("functions", [], rootobj, jsoncomponents) wf.writefunction(functionobj, interfacesreturn[str(obj["uuid"])], functionvars.copy()) buildComponents(interfacesreturn, variabledict, objcomponentslist, jsoncomponents, jsonmeasurements, jsonparameters, jsonfunctions, objcomponentsclone, enumnodedict, enumvalues, enumnames) - return interfacesreturn #, variabledict + return interfacesreturn def instantiateComponents(typesdict, interfaces, jsoncomponents, instantiatedict, eventsdict, streamsdict, componentlist, headnodeindex, variabledict): - count = 0 - countcomponent = 0 for interface in interfaces: root = interface interfacename = "" @@ -402,7 +481,6 @@ def main(): jsoninterfaces = [] jsonenums = [] for obj in elements: - #print(obj) if obj["elementType"] == "measurement": jsonmeasurements.append(obj) elif obj["elementType"] == "parameter": @@ -420,8 +498,6 @@ def main(): enumvalues = returnvalenums[1] enumnames = returnvalenums[2] typesdict = buildComponents({}, {}, jsoninterfaces, jsoncomponents, jsonmeasurements, jsonparameters, jsonfunctions, [], enumnodedict, enumvalues, enumnames) - #typesdict = returnComponents[0] - #variabledict = returnComponents[1] returnval = instantiate(typesdict, jsoninterfaces, jsoncomponents) instantiatedict = returnval[0] eventsdict = returnval[1] @@ -442,10 +518,10 @@ def main(): wf.writendpt1() for key in list(eventsdict.keys()): - wf.triggerevents(generatordict, eventsdict[key], instantiatedict[key], variabledict[key], jsonmeasurements, jsonparameters) + wf.triggerevents(generatordict, eventsdict[key], instantiatedict[key], variabledict[key]) for key in list(streamsdict.keys()): - wf.triggerstreams(streamgendict, streamsdict[key], instantiatedict[key], variabledict[key], jsonmeasurements, jsonparameters, savestreamvaldict) + wf.triggerstreams(streamgendict, streamsdict[key], instantiatedict[key], variabledict[key], savestreamvaldict) wf.writendpt2() diff --git a/writefile.py b/writefile.py index 52300d5..6cd1692 100644 --- a/writefile.py +++ b/writefile.py @@ -9,70 +9,6 @@ This File takes the prepared Data structures and writes the OPC-UA Server based """ -def defFunction(functionname, file, indent, inargs, function): - #return values are not written in the function at the moment - """ - writes the body for each function, it leaves the body empty and gives a description for the method which should be implemented - arguments: - - functionname(name of the function changed to a lowercase String without blanks), - - file(file it writes in) - - indent(indent it writes with) - - inargs(arguments of the function - - function(the object) - """ - writeindent = "" - for i in range(indent): - writeindent = writeindent + " " - file.write(writeindent + "async def " + functionname + "(parent") - if inargs is not None: - file.write(", ") - for i in range(len(inargs)): - if i < (len(inargs) - 1): - file.write(inargs[i] + ", ") - else: - file.write(inargs[i]) - file.write("):\n") - writeindent = writeindent + " " - file.write(writeindent + "#ToDo: Write a Function that fits the decription: \"" + str(function.description) + "\"\n") - file.write(writeindent + " # Use \"await\" before changing values and cast to Ua Varianttype\n") - file.write(writeindent + " # Use get and set_value to change the value at a given nodeid e.g: await testvar.set_value(ua.Variant(position))\n") - file.write(writeindent + "pass\n") - - -def getVariantType(datatype): - """ - returns the ua.VariantType according to the datatype of a variable. - arguments: - - the datatype - returns: - - ua.VarianType.<datatype> as String - (float and double get casted to ua.VariantType.Double and all forms of int get casted to ua.VariantType.Int64) - """ - varianttype = "ua.VariantType.Int64" - if datatype == "float" or datatype == "double": - varianttype = "ua.VariantType.Double" - elif datatype == "Boolean": - varianttype = "ua.VariantType.Boolean" - elif datatype == "time": - varianttype = "ua.VariantType.DateTime" - elif datatype == "string": - varianttype = "ua.VariantType.String" - elif datatype == "enum": - varianttype = "ua.VariantType.String" - return varianttype - - -with open("config.toml", "rb") as t: - """ - opens toml config file to access its data - """ - toml_dict = tomli.load(t) - -#sets opcua-server address and namespace according to config file -server_endpoint = toml_dict['server'] -namespace = toml_dict['namespace'] - - def writebeginning(): """ delets the existing opcua-server.py and generates a new one. This new file is started with all Imports and the needed set up @@ -96,7 +32,7 @@ _logger = logging.getLogger('asyncua') """) server.write("\nasync def main():") - #äinten = "\tab" + # äinten = "\tab" server.write(f""" # setup our server server = Server() @@ -108,15 +44,50 @@ _logger = logging.getLogger('asyncua') idx = await server.register_namespace(uri) #id of rangetype rangeid = await server.nodes.base_data_type.get_child(["0:Structure", "0:Range"]) - - - + + + #creating dataypes for parameters and measurements measurementtype = await server.nodes.base_data_type.add_data_type(idx, "Measurement") parametertype = await server.nodes.base_data_type.add_data_type(idx, "Parameter") + """) +def getVariantType(datatype): + """ + returns the ua.VariantType according to the datatype of a variable. + arguments: + - the datatype + returns: + - ua.VarianType.<datatype> as String + (float and double get casted to ua.VariantType.Double and all forms of int get casted to ua.VariantType.Int64) + """ + varianttype = "ua.VariantType.Int64" + if datatype == "float" or datatype == "double": + varianttype = "ua.VariantType.Double" + elif datatype == "Boolean": + varianttype = "ua.VariantType.Boolean" + elif datatype == "time": + varianttype = "ua.VariantType.DateTime" + elif datatype == "string": + varianttype = "ua.VariantType.String" + elif datatype == "enum": + varianttype = "ua.VariantType.String" + return varianttype + + +with open("config.toml", "rb") as t: + """ + opens toml config file to access its data + """ + toml_dict = tomli.load(t) + +#sets opcua-server address and namespace according to config file +server_endpoint = toml_dict['server'] +namespace = toml_dict['namespace'] + + def writeenums(enumslist): """ Creates the needed enums in the beginning of the server. And creates three dicts needed for Information. @@ -139,7 +110,8 @@ def writeenums(enumslist): for i in range(len(values)): retval = str(i) + ":" + str(values[i]) returnvallist.append(retval) - server.write(writeindent + enumname + "= await new_enum(server, idx, \""+ str(enum["name"]) + "\", values=" + str(enum["values"]) + ")\n") + server.write(writeindent + "#creating the enums\n") + server.write(writeindent + enumname + "= await new_enum(server, idx, \""+ str(enum["name"]) + "\", values=" + str(enum["values"]) + ")\n\n") enumnodenames[enum["uuid"]] = enumname returnvalues[enum["uuid"]] = returnvallist variantnames[enum["uuid"]] = enum["name"] @@ -147,7 +119,6 @@ def writeenums(enumslist): def openobject(objname, description): - #ToDO:description adden """ Creates the needed objecttype :param objname: Name of object @@ -164,14 +135,14 @@ def openobject(objname, description): return objtypename -def writemeasurement(measurementobj, headnode, enumnodedict, enumvaldict, enumvariantdict, varlist): +def writemeasurement(measurementobj, headnode, enumnodedict, enumvaldict, varlist): """ Creates the measurement variables with their properties :param measurementobj: a list of measurementobjects :param headnode: the component to which the varianles belong :param enumnodedict: Dict for the names of the enum nodes from writeenums (used for writeenumobj) :param enumvaldict: Dict for the enumvalues from writenums (used for writeenumobj) - :param enumvariantdict: Dict for the name of the enums (used for writeenumobj) + :param varlist: List of all the in measurements of the componengt in Format {name : value} :return: None """ server = open("opcua-server.py", "a") @@ -180,7 +151,6 @@ def writemeasurement(measurementobj, headnode, enumnodedict, enumvaldict, enumva if len(measurementobj) > 0: server.write(writeindent + "# measurements for " + headnode + "\n") for i in measurementobj: - # varname = ((i.name).lower()).replace(" ", "") + "var" varname = "" measname = "" for fobj in varlist: @@ -219,15 +189,14 @@ def writemeasurement(measurementobj, headnode, enumnodedict, enumvaldict, enumva server.write(writeindent + "await " + varname + "description.set_modelling_rule(True)\n\n") -def writeparameter(parameterobj, headnode, enumnodedict, enumvaldict, enumvariantdict, parameterlist): +def writeparameter(parameterobj, headnode, enumnodedict, enumvaldict, parameterlist): """ Creates the parameter values. (Similar to writemeasurement) :param parameterobj: A list of the objparameters :param headnode: The component to which the variables belong :param enumnodedict: Dict for the names of the enum nodes from writeenums (used for writeenumobj) :param enumvaldict: Dict for the enumvalues from writenums (used for writeenumobj) - :param enumvariantdict: Dict for the name of the enums (used for writeenumobj) - :param parameterlist: List of componentparameterlist with Format {name: value} + :param parameterlist: List of all the parameters of the component with Format {name: value} :return: None """ writeindent = " " @@ -277,6 +246,36 @@ def writeparameter(parameterobj, headnode, enumnodedict, enumvaldict, enumvarian server.write(writeindent + "await " + varname + "description.set_modelling_rule(True)\n\n") +def defFunction(functionname, file, indent, inargs, function): + #return values are not written in the function at the moment + """ + writes the body for each function, it leaves the body empty and gives a description for the method which should be implemented + arguments: + - functionname(name of the function changed to a lowercase String without blanks), + - file(file it writes in) + - indent(indent it writes with) + - inargs(arguments of the function + - function(the object) + """ + writeindent = "" + for i in range(indent): + writeindent = writeindent + " " + file.write(writeindent + "async def " + functionname + "(parent") + if inargs is not None: + file.write(", ") + for i in range(len(inargs)): + if i < (len(inargs) - 1): + file.write(inargs[i] + ", ") + else: + file.write(inargs[i]) + file.write("):\n") + writeindent = writeindent + " " + file.write(writeindent + "#ToDo: Write a Function that fits the decription: \"" + str(function.description) + "\"\n") + file.write(writeindent + " # Use \"await\" before changing values and cast to Ua Varianttype\n") + file.write(writeindent + " # Use get and set_value to change the value at a given nodeid e.g: await testvar.set_value(ua.Variant(position))\n") + file.write(writeindent + "pass\n") + + def writefunction(functionobj, headnode, functionlist): """ Takes the function objects writes the function body by calling deffunction and adds it to the objecttypes @@ -317,8 +316,6 @@ def writeobjects(objtype, objname, headnode): :param objtype: The node of the node with the objecttype :param objname: The name of the object :param headnode: The node the component belongs to. - :param count: (Currently not really implemented) but for counting the names up - if an object with the same name is instantiated twice. :return: The name of the newly created objectnode """ server = open("opcua-server.py", "a") @@ -442,7 +439,7 @@ def writendpt1(): while True:\n""") -def triggerevents(generatordict, eventslist, component, variablelist, jsonmeasurements, jsonparameters): +def triggerevents(generatordict, eventslist, component, variablelist): """ Writes the if cases in which the events get triggered. :param generatordict: Dict with the format @@ -450,13 +447,10 @@ def triggerevents(generatordict, eventslist, component, variablelist, jsonmeasur :param eventslist: List of eventdicts :param component: Component the events belong to :param variablelist: List of all variabledicts from component in the format {name : value} - :param jsonmeasurements: List of all measurements - :param jsonparameters: List of all parameters :return: None """ server = open("opcua-server.py", "a") indent = 3 - writeindent = "" variablenamelist = {} if len(eventslist) > 0: server.write(" # triggering events from " + component + "\n") @@ -473,12 +467,6 @@ def triggerevents(generatordict, eventslist, component, variablelist, jsonmeasur for variable in variablelist: if variable["name"] == event["element"]: eventsearchname = variable["name"] - # for measurement in jsonmeasurements: - # if variable["value"] == measurement["uuid"]: - # eventsearchname = measurement["name"] - # for parameter in jsonparameters: - # if variable["value"] == parameter["uuid"]: - # eventsearchname = parameter["name"] variablenamelist[str(event["element"])] = eventsearchname server.write(writeindent + valuename + "= await " + component + ".get_child([\"2:" + eventsearchname + "\"])\n") dictname = (event["element"].lower()).replace(" ", "") + component + event["severity"] @@ -495,7 +483,7 @@ def triggerevents(generatordict, eventslist, component, variablelist, jsonmeasur writeindent += " " -def triggerstreams(generatordict, streamslist, component, variablelist, jsonmeasurements, jsonparameters, savevaldict): +def triggerstreams(generatordict, streamslist, component, variablelist, savevaldict): """ Generates the Streams according to their stream type. (Dynamic streams are still missing) :param generatordict: Dict with the format @@ -503,8 +491,6 @@ def triggerstreams(generatordict, streamslist, component, variablelist, jsonmeas :param streamslist: List of streamdicts :param component: Component the streams belong to :param variablelist: List of all variabledicts from the component in the format {name: value} - :param jsonmeasurements: List of all measurements - :param jsonparameters: List of all parameters :param savevaldict: Dict with the name of the compare values for update streams. Format {streamname : comparevaluename} :return: None @@ -522,12 +508,6 @@ def triggerstreams(generatordict, streamslist, component, variablelist, jsonmeas for variable in variablelist: if variable["name"] == stream["measurement"]: streamname = variable["name"] - # for measurement in jsonmeasurements: - # if measurement["uuid"] == variable["value"]: - # streamname = measurement["name"] - # for parameter in jsonparameters: - # if parameter["uuid"] == variable["value"]: - # streamname = parameter["name"] server.write(writeindent + valuename + " = await " + component + ".get_child([\"2:" + streamname + "\"])\n") generatorname = generatordict[(stream["measurement"].lower()).replace(" ", "") + component] if stream["streamType"] == "update": -- GitLab