PLCControler.py
changeset 1347 533741e5075c
parent 1341 0923e602c603
child 1348 aee0a7eb833a
--- 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():