diff -r 92efd8fe3120 -r 533741e5075c PLCControler.py --- a/PLCControler.py Fri Oct 04 12:17:03 2013 +0200 +++ b/PLCControler.py Wed Oct 09 10:57:20 2013 +0200 @@ -101,77 +101,89 @@ RESOURCES, PROPERTIES] = UNEDITABLE_NAMES #------------------------------------------------------------------------------- +# Helper object for loading library in xslt stylesheets +#------------------------------------------------------------------------------- + +class LibraryResolver(etree.Resolver): + + def __init__(self, controller, debug=False): + self.Controller = controller + self.Debug = debug + + def resolve(self, url, pubid, context): + lib_name = os.path.basename(url) + if lib_name in ["project", "stdlib", "extensions"]: + lib_el = etree.Element(lib_name) + if lib_name == "project": + lib_el.append(deepcopy(self.Controller.GetProject(self.Debug))) + elif lib_name == "stdlib": + for lib in [StdBlockLibrary, AddnlBlockLibrary]: + lib_el.append(deepcopy(lib)) + else: + for ctn in self.Controller.ConfNodeTypes: + lib_el.append(deepcopy(ctn["types"])) + return self.resolve_string(etree.tostring(lib_el), context) + +#------------------------------------------------------------------------------- +# Helpers functions for translating list of arguments +# from xslt to valid arguments +#------------------------------------------------------------------------------- + +_BoolValue = lambda x: x in ["true", "0"] + +def _translate_args(translations, args): + return [translate(arg[0]) if len(arg) > 0 else None + for translate, arg in + zip(translations, args)] + +#------------------------------------------------------------------------------- # Helpers object for generating pou var list #------------------------------------------------------------------------------- -def compute_dimensions(el): - return [ - (dimension.get("lower"), dimension.get("upper")) - for dimension in el.findall("dimension")] - -def extract_param(el): - if el.tag == "Type" and el.text is None: - array = el.find("array") - return ('array', array.text, compute_dimensions(array)) - elif el.tag == "Tree": - return generate_var_tree(el) - elif el.tag == "Edit": - return el.text == "True" - elif el.text is None: - return '' - return el.text - -def generate_var_tree(tree): - return ([ - (var.get("name"), var.text, generate_var_tree(var)) - for var in tree.findall("var")], - compute_dimensions(tree)) - -class AddVariable(etree.XSLTExtension): +class _VariableInfos(object): + __slots__ = ["Name", "Class", "Option", "Location", "InitialValue", + "Edit", "Documentation", "Type", "Tree", "Number"] + def __init__(self, *args): + for attr, value in zip(self.__slots__, args): + setattr(self, attr, value if value is not None else "") + def copy(self): + return _VariableInfos(*[getattr(self, attr) for attr in self.__slots__]) + +class VariablesInfosFactory: def __init__(self, variables): - etree.XSLTExtension.__init__(self) self.Variables = variables - - def execute(self, context, self_node, input_node, output_parent): - infos = etree.Element('var_infos') - self.process_children(context, infos) - self.Variables.append( - {el.tag.replace("_", " "): extract_param(el) for el in infos}) - -class VarTree(etree.XSLTExtension): - - def __init__(self, controller, debug): - etree.XSLTExtension.__init__(self) - self.Controller = controller - self.Debug = debug - - def execute(self, context, self_node, input_node, output_parent): - typename = input_node.get("name") - pou_infos = self.Controller.GetPou(typename, self.Debug) - if pou_infos is not None: - self.apply_templates(context, pou_infos, output_parent) - return - - datatype_infos = self.Controller.GetDataType(typename, self.Debug) - if datatype_infos is not None: - self.apply_templates(context, datatype_infos, output_parent) - return - -class VarIsEdited(etree.XSLTExtension): - - def __init__(self, controller, debug): - etree.XSLTExtension.__init__(self) - self.Controller = controller - self.Debug = debug - - def execute(self, context, self_node, input_node, output_parent): - typename = input_node.get("name") - output_parent.text = str( - self.Controller.GetPou(typename, self.Debug) is None) - -variables_infos_xslt = etree.parse( - os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt")) + self.TreeStack = [] + self.Type = None + self.Dimensions = None + + def SetType(self, context, *args): + self.Type = args[0][0] + + def GetType(self): + if len(self.Dimensions) > 0: + return ("array", self.Type, self.Dimensions) + return self.Type + + def GetTree(self): + return (self.TreeStack.pop(-1), self.Dimensions) + + def AddDimension(self, context, *args): + self.Dimensions.append(tuple( + _translate_args([str] * 2, args))) + + def AddTree(self, context, *args): + self.TreeStack.append([]) + self.Dimensions = [] + + def AddVarToTree(self, context, *args): + var = (args[0][0], self.Type, self.GetTree()) + self.TreeStack[-1].append(var) + + def AddVariable(self, context, *args): + self.Variables.append(_VariableInfos(*(_translate_args( + [str] * 5 + [_BoolValue] + [str], args) + + [self.GetType(), self.GetTree()]))) #------------------------------------------------------------------------------- # Helpers object for generating pou variable instance list @@ -349,8 +361,6 @@ # Helpers object for generating pou block instances list #------------------------------------------------------------------------------- -_BoolValue = lambda x: x in ["true", "0"] - _Point = namedtuple("Point", ["x", "y"]) _BlockInstanceInfos = namedtuple("BlockInstanceInfos", @@ -427,11 +437,6 @@ def copy(self): return _ActionInfos(*[getattr(self, attr) for attr in self.__slots__]) -def _translate_args(translations, args): - return [translate(arg[0]) if len(arg) > 0 else None - for translate, arg in - zip(translations, args)] - class BlockInstanceFactory: def __init__(self, block_instances): @@ -677,7 +682,7 @@ if project is not None: for pou in project.getpous(): if pou_name is None or pou_name == pou.getname(): - variables.extend([var["Name"] for var in self.GetPouInterfaceVars(pou, debug)]) + variables.extend([var.Name for var in self.GetPouInterfaceVars(pou, debug=debug)]) for transition in pou.gettransitionList(): variables.append(transition.getname()) for action in pou.getactionList(): @@ -1299,35 +1304,35 @@ current_varlist = None current_type = None for var in vars: - next_type = (var["Class"], - var["Option"], - var["Location"] in ["", None] or + next_type = (var.Class, + var.Option, + var.Location in ["", None] or # When declaring globals, located # and not located variables are # in the same declaration block - var["Class"] == "Global") + var.Class == "Global") if current_type != next_type: current_type = next_type - infos = VAR_CLASS_INFOS.get(var["Class"], None) + infos = VAR_CLASS_INFOS.get(var.Class, None) if infos is not None: current_varlist = PLCOpenParser.CreateElement(infos[0], "interface") else: current_varlist = PLCOpenParser.CreateElement("varList") - varlist_list.append((var["Class"], current_varlist)) - if var["Option"] == "Constant": + varlist_list.append((var.Class, current_varlist)) + if var.Option == "Constant": current_varlist.setconstant(True) - elif var["Option"] == "Retain": + elif var.Option == "Retain": current_varlist.setretain(True) - elif var["Option"] == "Non-Retain": + elif var.Option == "Non-Retain": current_varlist.setnonretain(True) # Create variable and change its properties tempvar = PLCOpenParser.CreateElement("variable", "varListPlain") - tempvar.setname(var["Name"]) + tempvar.setname(var.Name) var_type = PLCOpenParser.CreateElement("type", "variable") - if isinstance(var["Type"], TupleType): - if var["Type"][0] == "array": - array_type, base_type_name, dimensions = var["Type"] + if isinstance(var.Type, TupleType): + if var.Type[0] == "array": + array_type, base_type_name, dimensions = var.Type array = PLCOpenParser.CreateElement("array", "dataType") baseType = PLCOpenParser.CreateElement("baseType", "array") array.setbaseType(baseType) @@ -1349,43 +1354,51 @@ derived_datatype.setname(base_type_name) baseType.setcontent(derived_datatype) var_type.setcontent(array) - elif var["Type"] in self.GetBaseTypes(): + elif var.Type in self.GetBaseTypes(): var_type.setcontent(PLCOpenParser.CreateElement( - var["Type"].lower() - if var["Type"] in ["STRING", "WSTRING"] - else var["Type"], "dataType")) + var.Type.lower() + if var.Type in ["STRING", "WSTRING"] + else var.Type, "dataType")) else: derived_type = PLCOpenParser.CreateElement("derived", "dataType") - derived_type.setname(var["Type"]) + derived_type.setname(var.Type) var_type.setcontent(derived_type) tempvar.settype(var_type) - if var["Initial Value"] != "": + if var.InitialValue != "": value = PLCOpenParser.CreateElement("initialValue", "variable") - value.setvalue(var["Initial Value"]) + value.setvalue(var.InitialValue) tempvar.setinitialValue(value) - if var["Location"] != "": - tempvar.setaddress(var["Location"]) + if var.Location != "": + tempvar.setaddress(var.Location) else: tempvar.setaddress(None) - if var['Documentation'] != "": + if var.Documentation != "": ft = PLCOpenParser.CreateElement("documentation", "variable") - ft.setanyText(var['Documentation']) + ft.setanyText(var.Documentation) tempvar.setdocumentation(ft) # Add variable to varList current_varlist.appendvariable(tempvar) return varlist_list - def GetVariableDictionary(self, object_with_vars, debug=False): + def GetVariableDictionary(self, object_with_vars, tree=False, debug=False): variables = [] + factory = VariablesInfosFactory(variables) + + parser = etree.XMLParser() + if tree: + parser.resolvers.add(LibraryResolver(self, debug)) variables_infos_xslt_tree = etree.XSLT( - variables_infos_xslt, extensions = { - ("var_infos_ns", "add_variable"): AddVariable(variables), - ("var_infos_ns", "var_tree"): VarTree(self, debug), - ("var_infos_ns", "is_edited"): VarIsEdited(self, debug)}) - variables_infos_xslt_tree(object_with_vars) + etree.parse( + os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), + parser), + extensions = {("var_infos_ns", name): getattr(factory, name) + for name in ["SetType", "AddDimension", "AddTree", + "AddVarToTree", "AddVariable"]}) + variables_infos_xslt_tree(object_with_vars, + tree=etree.XSLT.strparam(str(tree))) return variables @@ -1479,12 +1492,12 @@ return variables # Return the interface for the given pou - def GetPouInterfaceVars(self, pou, debug = False): + def GetPouInterfaceVars(self, pou, tree=False, debug = False): interface = pou.interface # Verify that the pou has an interface if interface is not None: # Extract variables defined in interface - return self.GetVariableDictionary(interface, debug) + return self.GetVariableDictionary(interface, tree, debug) return [] # Replace the Pou interface by the one given @@ -1530,30 +1543,35 @@ if pou is not None: pou.updateElementName(old_name, new_name) - # Return the return type of the pou given by its name - def GetPouInterfaceReturnTypeByName(self, name): - project = self.GetProject(debug) - if project is not None: - # Found the pou correponding to name and return the return type - pou = project.getpou(name) - if pou is not None: - return self.GetPouInterfaceReturnType(pou) - return False - # Return the return type of the given pou - def GetPouInterfaceReturnType(self, pou): + def GetPouInterfaceReturnType(self, pou, tree=False, debug=False): # Verify that the pou has an interface if pou.interface is not None: # Return the return type if there is one return_type = pou.interface.getreturnType() if return_type is not None: + factory = VariablesInfosFactory([]) + + parser = etree.XMLParser() + if tree: + parser.resolvers.add(LibraryResolver(self)) + return_type_infos_xslt_tree = etree.XSLT( - variables_infos_xslt, extensions = { - ("var_infos_ns", "var_tree"): VarTree(self)}) - return [extract_param(el) - for el in return_type_infos_xslt_tree(return_type).getroot()] - - return [None, ([], [])] + etree.parse( + os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), + parser), + extensions = {("var_infos_ns", name): getattr(factory, name) + for name in ["SetType", "AddDimension", + "AddTree", "AddVarToTree"]}) + return_type_infos_xslt_tree(return_type, + tree=etree.XSLT.strparam(str(tree))) + if tree: + return [factory.GetType(), factory.GetTree()] + return factory.GetType() + + if tree: + return [None, ([], [])] + return None # Function that add a new confnode to the confnode list def AddConfNodeTypesList(self, typeslist): @@ -2209,25 +2227,25 @@ return None # Return the edited element variables - def GetEditedElementInterfaceVars(self, tagname, debug = False): + def GetEditedElementInterfaceVars(self, tagname, tree=False, debug = False): words = tagname.split("::") if words[0] in ["P","T","A"]: project = self.GetProject(debug) if project is not None: pou = project.getpou(words[1]) if pou is not None: - return self.GetPouInterfaceVars(pou, debug) + return self.GetPouInterfaceVars(pou, tree, debug) return [] # Return the edited element return type - def GetEditedElementInterfaceReturnType(self, tagname, debug = False): + def GetEditedElementInterfaceReturnType(self, tagname, tree=False, debug = False): words = tagname.split("::") if words[0] == "P": project = self.GetProject(debug) if project is not None: pou = self.Project.getpou(words[1]) if pou is not None: - return self.GetPouInterfaceReturnType(pou) + return self.GetPouInterfaceReturnType(pou, tree, debug) elif words[0] == 'T': return "BOOL" return None @@ -2328,8 +2346,8 @@ names[datatype.getname().upper()] = True for pou in project.getpous(): names[pou.getname().upper()] = True - for var in self.GetPouInterfaceVars(pou, debug): - names[var["Name"].upper()] = True + for var in self.GetPouInterfaceVars(pou, debug=debug): + names[var.Name.upper()] = True for transition in pou.gettransitionList(): names[transition.getname().upper()] = True for action in pou.getactionList():