Replaced old pou variable list and variable tree generating by xslt stylesheet
authorLaurent Bessard
Mon, 09 Sep 2013 23:36:12 +0200
changeset 1308 ad61268dbdb6
parent 1307 26e8b99bc2c3
child 1309 85ce56758900
Replaced old pou variable list and variable tree generating by xslt stylesheet
PLCControler.py
controls/VariablePanel.py
editors/TextViewer.py
plcopen/variables_infos.xslt
--- a/PLCControler.py	Mon Sep 09 00:48:34 2013 +0200
+++ b/PLCControler.py	Mon Sep 09 23:36:12 2013 +0200
@@ -24,6 +24,7 @@
 
 from xml.dom import minidom
 from types import StringType, UnicodeType, TupleType
+from lxml import etree
 from copy import deepcopy
 import os,sys,re
 import datetime
@@ -99,6 +100,66 @@
  RESOURCES, PROPERTIES] = UNEDITABLE_NAMES
 
 #-------------------------------------------------------------------------------
+#                 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 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):
+    
+    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):
+        etree.XSLTExtension.__init__(self)
+        self.Controller = controller
+    
+    def execute(self, context, self_node, input_node, output_parent):
+        typename = input_node.get("name")
+        pou_infos = self.Controller.GetPou(typename)
+        if pou_infos is not None:
+            self.apply_templates(context, pou_infos, output_parent)
+            return
+        
+        datatype_infos = self.Controller.GetDataType(typename)
+        if datatype_infos is not None:
+            self.apply_templates(context, datatype_infos, output_parent)
+            return
+
+variables_infos_xslt = etree.parse(
+    os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"))
+
+#-------------------------------------------------------------------------------
 #                         Undo Buffer for PLCOpenEditor
 #-------------------------------------------------------------------------------
 
@@ -1224,63 +1285,17 @@
             current_varlist.appendvariable(tempvar)
         return varlist_list
     
-    def GetVariableDictionary(self, varlist, var):
-        '''
-        convert a PLC variable to the dictionary representation
-        returned by Get*Vars)
-        '''
-
-        tempvar = {"Name": var.getname()}
-
-        vartype_content = var.gettype().getcontent()
-        vartype_content_type = vartype_content.getLocalTag()
-        if vartype_content_type == "derived":
-            tempvar["Type"] = vartype_content.getname()
-        elif vartype_content_type == "array":
-            dimensions = []
-            for dimension in vartype_content.getdimension():
-                dimensions.append((dimension.getlower(), dimension.getupper()))
-            base_type = vartype_content.baseType.getcontent()
-            base_type_type = base_type.getLocalTag()
-            if base_type_type == "derived":
-                base_type_name = base_type.getname()
-            else:
-                base_type_name = base_type_type.upper()
-            tempvar["Type"] = ("array", base_type_name, dimensions)
-        else:
-            tempvar["Type"] = vartype_content_type.upper()
+    def GetVariableDictionary(self, object_with_vars):
+        variables = []
         
-        tempvar["Edit"] = True
+        variables_infos_xslt_tree = etree.XSLT(
+            variables_infos_xslt, extensions = {
+                ("var_infos_ns", "add_variable"): AddVariable(variables),
+                ("var_infos_ns", "var_tree"): VarTree(self)})
+        variables_infos_xslt_tree(object_with_vars)
         
-        initial = var.getinitialValue()
-        if initial is not None:
-            tempvar["Initial Value"] = initial.getvalue()
-        else:
-            tempvar["Initial Value"] = ""
-
-        address = var.getaddress()
-        if address:
-            tempvar["Location"] = address
-        else:
-            tempvar["Location"] = ""
-
-        if varlist.getconstant():
-            tempvar["Option"] = "Constant"
-        elif varlist.getretain():
-            tempvar["Option"] = "Retain"
-        elif varlist.getnonretain():
-            tempvar["Option"] = "Non-Retain"
-        else:
-            tempvar["Option"] = ""
-
-        doc = var.getdocumentation()
-        if doc is not None:
-            tempvar["Documentation"] = doc.getanyText()
-        else:
-            tempvar["Documentation"] = ""
-
-        return tempvar
-    
+        return variables
+            
     # Add a global var to configuration to configuration
     def AddConfigurationGlobalVar(self, config_name, type, var_name, 
                                            location="", description=""):
@@ -1304,19 +1319,15 @@
     
     # Return the configuration globalvars
     def GetConfigurationGlobalVars(self, name, debug = False):
-        vars = []
         project = self.GetProject(debug)
         if project is not None:
             # Found the configuration corresponding to name
             configuration = project.getconfiguration(name)
             if configuration is not None:
-                # Extract variables from every varLists
-                for varlist in configuration.getglobalVars():
-                    for var in varlist.getvariable():
-                        tempvar = self.GetVariableDictionary(varlist, var)
-                        tempvar["Class"] = "Global"
-                        vars.append(tempvar)
-        return vars
+                # Extract variables defined in configuration
+                return self.GetVariableDictionary(configuration)
+        
+        return []
 
     # Return configuration variable names
     def GetConfigurationVariableNames(self, config_name = None, debug = False):
@@ -1345,19 +1356,15 @@
     
     # Return the resource globalvars
     def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False):
-        vars = []
         project = self.GetProject(debug)
         if project is not None:
             # Found the resource corresponding to name
             resource = project.getconfigurationResource(config_name, name)
             if resource is not None:
-                # Extract variables from every varLists
-                for varlist in resource.getglobalVars():
-                    for var in varlist.getvariable():
-                        tempvar = self.GetVariableDictionary(varlist, var)
-                        tempvar["Class"] = "Global"
-                        vars.append(tempvar)
-        return vars
+                # Extract variables defined in configuration
+                return self.GetVariableDictionary(resource)
+        
+        return []
     
     # Return resource variable names
     def GetConfigurationResourceVariableNames(self, 
@@ -1375,73 +1382,15 @@
                                         for varlist in resource.globalVars],
                                     [])])
         return variables
-    
-    # Recursively generate element name tree for a structured variable
-    def GenerateVarTree(self, typename, debug = False):
-        project = self.GetProject(debug)
-        if project is not None:
-            blocktype = self.GetBlockType(typename, debug = debug)
-            if blocktype is not None:
-                tree = []
-                en = False
-                eno = False
-                for var_name, var_type, var_modifier in blocktype["inputs"] + blocktype["outputs"]:
-                    en |= var_name.upper() == "EN"
-                    eno |= var_name.upper() == "ENO"    
-                    tree.append((var_name, var_type, self.GenerateVarTree(var_type, debug)))
-                if not eno:
-                    tree.insert(0, ("ENO", "BOOL", ([], [])))
-                if not en:
-                    tree.insert(0, ("EN", "BOOL", ([], [])))
-                return tree, []
-            datatype = self.GetDataType(typename)
-            if datatype is not None:
-                tree = []
-                basetype_content = datatype.baseType.getcontent()
-                basetype_content_type = basetype_content.getLocalTag()
-                if basetype_content_type == "derived":
-                    return self.GenerateVarTree(basetype_content.getname())
-                elif basetype_content_type == "array":
-                    dimensions = []
-                    base_type = basetype_content.baseType.getcontent()
-                    if base_type.getLocalTag() == "derived":
-                        tree = self.GenerateVarTree(base_type.getname())
-                        if len(tree[1]) == 0:
-                            tree = tree[0]
-                        for dimension in basetype_content.getdimension():
-                            dimensions.append((dimension.getlower(), dimension.getupper()))
-                    return tree, dimensions
-                elif basetype_content_type == "struct":
-                    for element in basetype_content.getvariable():
-                        element_type = element.type.getcontent()
-                        element_type_type = element_type.getLocalTag()
-                        if element_type_type == "derived":
-                            tree.append((element.getname(), element_type.getname(), self.GenerateVarTree(element_type.getname())))
-                        else:
-                            tree.append((element.getname(), element_type_type, ([], [])))
-                    return tree, []
-        return [], []
 
     # Return the interface for the given pou
     def GetPouInterfaceVars(self, pou, debug = False):
-        vars = []
+        interface = pou.interface
         # Verify that the pou has an interface
-        if pou.interface is not None:
-            # Extract variables from every varLists
-            for type, varlist in pou.getvars():
-                for var in varlist.getvariable():
-                    tempvar = self.GetVariableDictionary(varlist, var)
-
-                    tempvar["Class"] = type
-                    tempvar["Tree"] = ([], [])
-
-                    vartype_content = var.gettype().getcontent()
-                    if vartype_content.getLocalTag() == "derived":
-                        tempvar["Edit"] = not pou.hasblock(tempvar["Name"])
-                        tempvar["Tree"] = self.GenerateVarTree(tempvar["Type"], debug)
-
-                    vars.append(tempvar)
-        return vars
+        if interface is not None:
+            # Extract variables defined in interface
+            return self.GetVariableDictionary(interface)
+        return []
 
     # Replace the Pou interface by the one given
     def SetPouInterfaceVars(self, name, vars):
@@ -1503,13 +1452,13 @@
             # Return the return type if there is one
             return_type = pou.interface.getreturnType()
             if return_type is not None:
-                returntype_content = return_type.getcontent()
-                returntype_content_type = returntype_content.getLocalTag()
-                if returntype_content_type == "derived":
-                    return returntype_content.getname()
-                else:
-                    return returntype_content_type.upper()
-        return None
+                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, ([], [])] 
 
     # Function that add a new confnode to the confnode list
     def AddConfNodeTypesList(self, typeslist):
@@ -1680,6 +1629,20 @@
         return datatypes
 
     # Return Data Type Object
+    def GetPou(self, typename, debug = False):
+        project = self.GetProject(debug)
+        if project is not None:
+            result = project.getpou(typename)
+            if result is not None:
+                return result
+        for confnodetype in self.ConfNodeTypes:
+            result = confnodetype["types"].getpou(typename)
+            if result is not None:
+                return result
+        return None
+
+
+    # Return Data Type Object
     def GetDataType(self, typename, debug = False):
         project = self.GetProject(debug)
         if project is not None:
--- a/controls/VariablePanel.py	Mon Sep 09 00:48:34 2013 +0200
+++ b/controls/VariablePanel.py	Mon Sep 09 23:36:12 2013 +0200
@@ -173,7 +173,7 @@
                             if var_class not in ["External", "InOut"]:
                                 if self.Parent.Controler.IsEnumeratedType(var_type):
                                     editor = wx.grid.GridCellChoiceEditor()
-                                    editor.SetParameters(",".join(self.Parent.Controler.GetEnumeratedDataValues(var_type)))
+                                    editor.SetParameters(",".join([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type)))
                                 else:
                                     editor = wx.grid.GridCellTextEditor()
                                 renderer = wx.grid.GridCellStringRenderer()
@@ -635,7 +635,7 @@
                 self.ReturnType.Clear()
                 for data_type in self.Controler.GetDataTypes(self.TagName, debug=self.Debug):
                     self.ReturnType.Append(data_type)
-                returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName)
+                returnType, (var_tree, dimensions) = self.Controler.GetEditedElementInterfaceReturnType(self.TagName)
             description = self.Controler.GetPouDescription(words[1])
             self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName, self.Debug)
         
--- a/editors/TextViewer.py	Mon Sep 09 00:48:34 2013 +0200
+++ b/editors/TextViewer.py	Mon Sep 09 23:36:12 2013 +0200
@@ -460,9 +460,8 @@
         words = self.TagName.split("::")
         self.Variables = self.GenerateVariableTree([(variable["Name"], variable["Type"], variable["Tree"]) for variable in self.Controler.GetEditedElementInterfaceVars(self.TagName, self.Debug)])
         if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL":
-            return_type = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, self.Debug)
+            return_type, (var_tree, var_dimension) = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, self.Debug)
             if return_type is not None:
-                var_tree, var_dimension = self.Controler.GenerateVarTree(return_type, self.Debug)
                 self.Variables[words[-1].upper()] = self.GenerateVariableTree(var_tree)
             else:
                 self.Variables[words[-1].upper()] = {}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plcopen/variables_infos.xslt	Mon Sep 09 23:36:12 2013 +0200
@@ -0,0 +1,207 @@
+<xsl:stylesheet version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:ppx="http://www.plcopen.org/xml/tc6_0201"
+    xmlns:xhtml="http://www.w3.org/1999/xhtml"
+    xmlns:ns="var_infos_ns"
+    extension-element-prefixes="ns"
+    exclude-result-prefixes="ns">
+  <xsl:template match="ppx:returnType">
+    <ReturnType>
+      <Type><xsl:apply-templates/></Type>
+      <Tree><xsl:apply-templates mode="var_tree"/></Tree>
+    </ReturnType>
+  </xsl:template>
+  <xsl:template match="ppx:localVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'Local'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="ppx:globalVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'Global'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="ppx:externalVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'External'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="ppx:tempVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'Temp'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="ppx:inputVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'Input'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="ppx:outputVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'Output'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template match="inOutVars">
+    <xsl:call-template name="variables_infos">
+      <xsl:with-param name="var_class" select="'InOut'"/>
+    </xsl:call-template>
+  </xsl:template>
+  <xsl:template name="variables_infos">
+    <xsl:param name="var_class"/>
+    <xsl:variable name="var_option">
+      <xsl:choose>
+        <xsl:when test="@constant='true' or @constant='1'">
+          <xsl:text>Constant</xsl:text>
+        </xsl:when>
+        <xsl:when test="@retain='true' or @retain='1'">
+          <xsl:text>Retain</xsl:text>
+        </xsl:when>
+        <xsl:when test="@nonretain='true' or @nonretain='1'">
+          <xsl:text>Non-Retain</xsl:text>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:variable>
+    <xsl:for-each select="ppx:variable">
+      <ns:add_variable>
+        <Name><xsl:value-of select="@name"/></Name>
+        <Class><xsl:value-of select="$var_class"/></Class>
+        <Type><xsl:apply-templates select="ppx:type"/></Type>
+        <Option><xsl:value-of select="$var_option"/></Option>
+        <Location><xsl:value-of select="@address"/></Location>
+        <Initial_Value><xsl:apply-templates select="ppx:initialValue"/></Initial_Value>
+        <Edit/>
+        <Tree><xsl:apply-templates select="ppx:type" mode="var_tree"/></Tree>
+        <Documentation>
+          <xsl:value-of select="ppx:documentation/xhtml:p/text()"/>
+        </Documentation>
+      </ns:add_variable>
+    </xsl:for-each>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:derived">
+    <xsl:value-of select="@name"/>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:array">
+    <array>
+      <xsl:apply-templates select="ppx:baseType"/>
+      <xsl:for-each select="ppx:dimension">
+        <dimension>
+          <xsl:attribute name="lower">
+            <xsl:value-of select="@lower"/>
+          </xsl:attribute>
+          <xsl:attribute name="upper">
+            <xsl:value-of select="@upper"/>
+          </xsl:attribute>
+        </dimension>
+      </xsl:for-each>
+    </array>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:string">
+    <xsl:text>STRING</xsl:text>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:wstring">
+    <xsl:text>WSTRING</xsl:text>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/*">
+    <xsl:value-of select="local-name()"/>
+  </xsl:template>
+    <xsl:template match="ppx:initialValue">
+    <xsl:apply-templates/>
+  </xsl:template>
+  <xsl:template match="ppx:value">
+    <xsl:choose>
+      <xsl:when test="@repetitionValue">
+        <xsl:value-of select="@repetitionValue"/>
+        <xsl:text>(</xsl:text>
+        <xsl:apply-templates/>
+        <xsl:text>)</xsl:text>
+      </xsl:when>
+      <xsl:when test="@member">
+        <xsl:value-of select="@member"/>
+        <xsl:text> := </xsl:text>
+        <xsl:apply-templates/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:apply-templates/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  <xsl:template match="ppx:simpleValue">
+    <xsl:value-of select="@value"/>
+  </xsl:template>
+  <xsl:template match="ppx:arrayValue">
+    <xsl:text>[</xsl:text>
+    <xsl:for-each select="ppx:value">
+      <xsl:apply-templates select="."/>
+      <xsl:choose>
+        <xsl:when test="position()!=last()">
+          <xsl:text>, </xsl:text>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:for-each>
+    <xsl:text>]</xsl:text>
+  </xsl:template>
+  <xsl:template match="ppx:structValue">
+    <xsl:text>(</xsl:text>
+    <xsl:for-each select="ppx:value">
+      <xsl:apply-templates select="."/>
+      <xsl:choose>
+        <xsl:when test="position()!=last()">
+          <xsl:text>, </xsl:text>
+        </xsl:when>
+      </xsl:choose>
+    </xsl:for-each>
+    <xsl:text>)</xsl:text>
+  </xsl:template>
+  <xsl:template match="ppx:pou" mode="var_tree">
+    <xsl:apply-templates select="ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars or self::ppx:outputVars]/ppx:variable" mode="var_tree"/>
+  </xsl:template>
+  <xsl:template match="ppx:variable" mode="var_tree">
+    <var>
+      <xsl:attribute name="name">
+        <xsl:value-of select="@name"/>
+      </xsl:attribute>
+      <xsl:apply-templates select="ppx:type" mode="var_tree"/>
+    </var>
+  </xsl:template>
+  <xsl:template match="ppx:dataType">
+    <xsl:apply-templates select="ppx:baseType" mode="var_tree"/>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:struct" mode="var_tree">
+    <xsl:apply-templates select="ppx:variable" mode="var_tree"/>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:derived" mode="var_tree">
+    <ns:var_tree/>
+    <xsl:choose>
+      <xsl:when test="count(./*) > 0">
+        <xsl:apply-templates mode="var_tree"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="@name"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:array" mode="var_tree">
+    <xsl:apply-templates select="ppx:baseType" mode="var_tree"/>
+    <xsl:for-each select="ppx:dimension">
+      <dimension>
+        <xsl:attribute name="lower">
+          <xsl:value-of select="@lower"/>
+        </xsl:attribute>
+        <xsl:attribute name="upper">
+          <xsl:value-of select="@upper"/>
+        </xsl:attribute>
+      </dimension>
+    </xsl:for-each>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:string" mode="var_tree">
+    <xsl:text>STRING</xsl:text>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/ppx:wstring" mode="var_tree">
+    <xsl:text>WSTRING</xsl:text>
+  </xsl:template>
+  <xsl:template match="*[self::ppx:type or self::ppx:baseType or self::ppx:returnType]/*" mode="var_tree">
+    <xsl:value-of select="local-name()"/>
+  </xsl:template>
+  <xsl:template match="text()"/>
+  <xsl:template match="text()" mode="var_tree"/>
+</xsl:stylesheet>
\ No newline at end of file