SVGHMI: added deduction of HMI tree from list of HMI_* instances. svghmi
authorEdouard Tisserant
Tue, 13 Aug 2019 14:04:13 +0200
branchsvghmi
changeset 2757 c901baa36bb3
parent 2756 f94bc35a023e
child 2758 5f79b194fa63
SVGHMI: added deduction of HMI tree from list of HMI_* instances.
svghmi/svghmi.py
tests/svghmi/plc.xml
--- a/svghmi/svghmi.py	Mon Aug 12 13:09:55 2019 +0200
+++ b/svghmi/svghmi.py	Tue Aug 13 14:04:13 2019 +0200
@@ -9,6 +9,8 @@
 from __future__ import absolute_import
 import os
 import shutil
+from itertools import izip
+from pprint import pprint, pformat
 
 import wx
 
@@ -33,16 +35,100 @@
 
 ScriptDirectory = paths.AbsDir(__file__)
 
+class HMITreeNode(object):
+    def __init__(self, path, name, nodetype):
+        self.path = path
+        self.name = name
+        self.nodetype = nodetype
+        if nodetype in ["HMI_LABEL", "HMI_ROOT"]:
+            self.children = []
+
+    def pprint(self, indent = 0):
+        res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n"
+        if hasattr(self, "children"): 
+            res += "\n".join([child.pprint(indent = indent + 1)
+                              for child in self.children])
+            res += "\n"
+            
+        return res
+
+    def place_node(self, node):
+        best_child = None
+        known_best_match = 0
+        for child in self.children:
+            in_common = 0
+            for child_path_item, node_path_item in izip(child.path, node.path):
+                if child_path_item == node_path_item:
+                    in_common +=1
+                else:
+                    break
+            if in_common > known_best_match:
+                known_best_match = in_common
+                best_child = child
+        if best_child is not None and best_child.nodetype == "HMI_LABEL":
+            best_child.place_node(node)
+        else:
+            self.children.append(node)
+            
+
 class SVGHMILibrary(POULibrary):
     def GetLibraryPath(self):
          return paths.AbsNeighbourFile(__file__, "pous.xml")
 
     def Generate_C(self, buildpath, varlist, IECCFLAGS):
 
+        """
+        PLC Instance Tree:
+          prog0
+           +->v1 HMI_INT
+           +->v2 HMI_INT
+           +->fb0 (type mhoo)
+           |   +->va HMI_LABEL
+           |   +->v3 HMI_INT
+           |   +->v4 HMI_INT
+           |
+           +->fb1 (type mhoo)
+           |   +->va HMI_LABEL
+           |   +->v3 HMI_INT
+           |   +->v4 HMI_INT
+           |
+           +->fb2
+               +->v5 HMI_IN
+
+        HMI tree:
+          hmi0
+           +->v1
+           +->v2
+           +->fb0_va
+           |   +-> v3
+           |   +-> v4
+           |
+           +->fb1_va
+           |   +-> v3
+           |   +-> v4
+           |
+           +->v5
+
+        """
+
         # Filter known HMI types
         hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES]
-
-        # TODO deduce HMI tree
+        # TODO XXX !!!  filter intermediate variables added for FBD feedback loop
+
+        hmi_tree_root = HMITreeNode(None, "hmi0", "HMI_ROOT")
+
+        # TODO add always available variables here ?
+        #    - plc status
+        #    - current page
+        #    - ...
+
+        # deduce HMI tree from PLC HMI_* instances
+        for v in hmi_types_instances:
+            path = v["IEC_path"].split(".")
+            new_node = HMITreeNode(path, path[-1], v["derived"])
+            hmi_tree_root.place_node(new_node)
+
+        print(hmi_tree_root.pprint())
 
         # TODO generate C code to observe/access HMI tree variables
         svghmi_c_filepath = paths.AbsNeighbourFile(__file__, "svghmi.c")
--- a/tests/svghmi/plc.xml	Mon Aug 12 13:09:55 2019 +0200
+++ b/tests/svghmi/plc.xml	Tue Aug 13 14:04:13 2019 +0200
@@ -1,7 +1,7 @@
 <?xml version='1.0' encoding='utf-8'?>
 <project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
   <fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2019-08-06T14:23:42"/>
-  <contentHeader name="Unnamed" modificationDateTime="2019-08-09T11:20:07">
+  <contentHeader name="Unnamed" modificationDateTime="2019-08-13T10:34:09">
     <coordinateInfo>
       <fbd>
         <scaling x="5" y="5"/>
@@ -22,7 +22,12 @@
           <localVars>
             <variable name="LocalVar0">
               <type>
-                <DINT/>
+                <derived name="HMI_INT"/>
+              </type>
+            </variable>
+            <variable name="Pump0">
+              <type>
+                <derived name="Pump"/>
               </type>
             </variable>
           </localVars>
@@ -85,7 +90,93 @@
               <connectionPointOut>
                 <relPosition x="60" y="10"/>
               </connectionPointOut>
-              <expression>DINT#1</expression>
+              <expression>1</expression>
+            </inVariable>
+            <block localId="4" typeName="Pump" instanceName="Pump0" executionOrderId="0" height="20" width="45">
+              <position x="595" y="50"/>
+              <inputVariables/>
+              <inOutVariables/>
+              <outputVariables/>
+            </block>
+          </FBD>
+        </body>
+      </pou>
+      <pou name="Pump" pouType="functionBlock">
+        <interface>
+          <localVars>
+            <variable name="hmi">
+              <type>
+                <derived name="HMI_LABEL"/>
+              </type>
+            </variable>
+            <variable name="Pressure">
+              <type>
+                <derived name="HMI_INT"/>
+              </type>
+            </variable>
+          </localVars>
+        </interface>
+        <body>
+          <FBD>
+            <inOutVariable localId="1" executionOrderId="0" height="30" width="75" negatedOut="false" negatedIn="false">
+              <position x="285" y="105"/>
+              <connectionPointIn>
+                <relPosition x="0" y="15"/>
+                <connection refLocalId="2" formalParameter="OUT">
+                  <position x="285" y="120"/>
+                  <position x="275" y="120"/>
+                  <position x="275" y="95"/>
+                  <position x="550" y="95"/>
+                  <position x="550" y="135"/>
+                  <position x="540" y="135"/>
+                </connection>
+              </connectionPointIn>
+              <connectionPointOut>
+                <relPosition x="75" y="15"/>
+              </connectionPointOut>
+              <expression>Pressure</expression>
+            </inOutVariable>
+            <block localId="2" typeName="ADD" executionOrderId="0" height="60" width="65">
+              <position x="475" y="105"/>
+              <inputVariables>
+                <variable formalParameter="IN1">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                    <connection refLocalId="1">
+                      <position x="475" y="135"/>
+                      <position x="417" y="135"/>
+                      <position x="417" y="120"/>
+                      <position x="360" y="120"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="IN2">
+                  <connectionPointIn>
+                    <relPosition x="0" y="50"/>
+                    <connection refLocalId="3">
+                      <position x="475" y="155"/>
+                      <position x="432" y="155"/>
+                      <position x="432" y="150"/>
+                      <position x="410" y="150"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables>
+                <variable formalParameter="OUT">
+                  <connectionPointOut>
+                    <relPosition x="65" y="30"/>
+                  </connectionPointOut>
+                </variable>
+              </outputVariables>
+            </block>
+            <inVariable localId="3" executionOrderId="0" height="25" width="30" negated="false">
+              <position x="380" y="140"/>
+              <connectionPointOut>
+                <relPosition x="30" y="10"/>
+              </connectionPointOut>
+              <expression>23</expression>
             </inVariable>
           </FBD>
         </body>