Merged SVGHMI branches svghmi
authorEdouard Tisserant <edouard.tisserant@gmail.com>
Tue, 18 May 2021 09:28:44 +0200
branchsvghmi
changeset 3242 f037e901a17c
parent 3241 fe945f1f48b7 (current diff)
parent 3240 5f756332ada1 (diff)
child 3243 92cc21f88bf8
Merged SVGHMI branches
svghmi/gen_index_xhtml.xslt
svghmi/widget_jump.ysl2
--- a/bacnet/bacnet.py	Tue May 18 09:22:17 2021 +0200
+++ b/bacnet/bacnet.py	Tue May 18 09:28:44 2021 +0200
@@ -38,9 +38,7 @@
 from ConfigTreeNode import ConfigTreeNode
 import util.paths as paths
 
-base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
-base_folder = os.path.join(base_folder, "..")
-BacnetPath  = os.path.join(base_folder, "BACnet")
+BacnetPath = paths.ThirdPartyPath("BACnet")
 BacnetLibraryPath = os.path.join(BacnetPath, "lib")
 BacnetIncludePath = os.path.join(BacnetPath, "include")
 BacnetIncludePortPath = os.path.join(BacnetPath, "ports")
--- a/canfestival/canfestival.py	Tue May 18 09:22:17 2021 +0200
+++ b/canfestival/canfestival.py	Tue May 18 09:28:44 2021 +0200
@@ -33,14 +33,14 @@
 from gnosis.xml.pickle.util import setParanoia  # pylint: disable=import-error
 
 import util.paths as paths
+
 from util.TranslationCatalogs import AddCatalog
 from ConfigTreeNode import ConfigTreeNode
 from PLCControler import \
     LOCATION_CONFNODE, \
     LOCATION_VAR_MEMORY
 
-base_folder = paths.AbsParentDir(__file__, 2)  # noqa
-CanFestivalPath = os.path.join(base_folder, "CanFestival-3")  # noqa
+CanFestivalPath = paths.ThirdPartyPath("CanFestival-3")  # noqa
 sys.path.append(os.path.join(CanFestivalPath, "objdictgen"))  # noqa
 
 # pylint: disable=wrong-import-position
--- a/modbus/modbus.py	Tue May 18 09:22:17 2021 +0200
+++ b/modbus/modbus.py	Tue May 18 09:28:44 2021 +0200
@@ -32,9 +32,7 @@
 from PLCControler import LOCATION_CONFNODE, LOCATION_VAR_MEMORY
 import util.paths as paths
 
-base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0]
-base_folder = os.path.join(base_folder, "..")
-ModbusPath = os.path.join(base_folder, "Modbus")
+ModbusPath = paths.ThirdPartyPath("Modbus")
 
 
 #
--- a/svghmi/gen_index_xhtml.xslt	Tue May 18 09:22:17 2021 +0200
+++ b/svghmi/gen_index_xhtml.xslt	Tue May 18 09:28:44 2021 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0"?>
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exsl="http://exslt.org/common" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:str="http://exslt.org/strings" xmlns:func="http://exslt.org/functions" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:debug="debug" xmlns:preamble="preamble" xmlns:declarations="declarations" xmlns:definitions="definitions" xmlns:epilogue="epilogue" xmlns:ns="beremiz" version="1.0" extension-element-prefixes="ns func exsl regexp str dyn" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions">
-  <xsl:output cdata-section-elements="xhtml:script" method="xml"/>
+<xsl:stylesheet xmlns:ns="beremiz" xmlns:definitions="definitions" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:func="http://exslt.org/functions" xmlns:epilogue="epilogue" xmlns:preamble="preamble" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:svg="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:str="http://exslt.org/strings" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:exsl="http://exslt.org/common" xmlns:declarations="declarations" xmlns:debug="debug" exclude-result-prefixes="ns func exsl regexp str dyn debug preamble epilogue declarations definitions" extension-element-prefixes="ns func exsl regexp str dyn" version="1.0">
+  <xsl:output method="xml" cdata-section-elements="xhtml:script"/>
   <xsl:variable name="svg" select="/svg:svg"/>
   <xsl:variable name="hmi_elements" select="//svg:*[starts-with(@inkscape:label, 'HMI:')]"/>
   <xsl:variable name="hmitree" select="ns:GetHMITree()"/>
@@ -1743,7 +1743,7 @@
 </xsl:text>
   </xsl:template>
   <xsl:variable name="excluded_types" select="str:split('Page VarInit VarInitPersistent')"/>
-  <xsl:key name="TypesKey" match="widget" use="@type"/>
+  <xsl:key use="@type" name="TypesKey" match="widget"/>
   <declarations:hmi-classes/>
   <xsl:template match="declarations:hmi-classes">
     <xsl:text>
@@ -5463,21 +5463,21 @@
 </xsl:text>
     <xsl:text>                 /* show active */ 
 </xsl:text>
-    <xsl:text>                 this.active_elt.setAttribute("style", this.active_elt_style);
+    <xsl:text>                 this.active_elt.style.display = "";
 </xsl:text>
     <xsl:text>                 /* hide inactive */ 
 </xsl:text>
-    <xsl:text>                 this.inactive_elt.setAttribute("style", "display:none");
+    <xsl:text>                 this.inactive_elt.style.display = "none";
 </xsl:text>
     <xsl:text>            } else {
 </xsl:text>
     <xsl:text>                 /* show inactive */ 
 </xsl:text>
-    <xsl:text>                 this.inactive_elt.setAttribute("style", this.inactive_elt_style);
+    <xsl:text>                 this.inactive_elt.style.display = "";
 </xsl:text>
     <xsl:text>                 /* hide active */ 
 </xsl:text>
-    <xsl:text>                 this.active_elt.setAttribute("style", "display:none");
+    <xsl:text>                 this.active_elt.style.display = "none";
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
@@ -5485,6 +5485,36 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>        update_disability() {
+</xsl:text>
+    <xsl:text>            if(this.disabled) {
+</xsl:text>
+    <xsl:text>                /* show disabled */ 
+</xsl:text>
+    <xsl:text>                this.disabled_elt.style.display = "";
+</xsl:text>
+    <xsl:text>                /* hide inactive */ 
+</xsl:text>
+    <xsl:text>                this.inactive_elt.style.display = "none";
+</xsl:text>
+    <xsl:text>                /* hide active */ 
+</xsl:text>
+    <xsl:text>                this.active_elt.style.display = "none";
+</xsl:text>
+    <xsl:text>            } else {
+</xsl:text>
+    <xsl:text>                /* hide disabled */ 
+</xsl:text>
+    <xsl:text>                this.disabled_elt.style.display = "none";
+</xsl:text>
+    <xsl:text>                this.update_activity();
+</xsl:text>
+    <xsl:text>            }
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>        make_on_click() {
 </xsl:text>
     <xsl:text>            let that = this;
@@ -5493,13 +5523,21 @@
 </xsl:text>
     <xsl:text>            return function(evt){
 </xsl:text>
-    <xsl:text>                /* TODO: suport path pointing to local variable whom value 
-</xsl:text>
-    <xsl:text>                   would be an HMI_TREE index to jump to a relative page */
-</xsl:text>
-    <xsl:text>                const index = that.indexes.length &gt; 0 ? that.indexes[0] + that.offset : undefined;
-</xsl:text>
-    <xsl:text>                switch_page(name, index);
+    <xsl:text>                /* TODO: in order to allow jumps to page selected through for exemple a dropdown,
+</xsl:text>
+    <xsl:text>                   support path pointing to local variable whom value 
+</xsl:text>
+    <xsl:text>                   would be an HMI_TREE index and then jump to a relative page not hard-coded in advance */
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>                if(!that.disabled) {
+</xsl:text>
+    <xsl:text>                    const index = that.indexes.length &gt; 0 ? that.indexes[0] + that.offset : undefined;
+</xsl:text>
+    <xsl:text>                    switch_page(name, index);
+</xsl:text>
+    <xsl:text>                }
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
@@ -5517,7 +5555,7 @@
 </xsl:text>
     <xsl:text>                this.active = ((ref_name == undefined || ref_name == page_name) &amp;&amp; index == ref_index);
 </xsl:text>
-    <xsl:text>                this.update_activity();
+    <xsl:text>                this.update_state();
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
@@ -5529,29 +5567,7 @@
 </xsl:text>
     <xsl:text>            this.disabled = !Number(value);
 </xsl:text>
-    <xsl:text>            if(this.disabled) {
-</xsl:text>
-    <xsl:text>              /* show disabled */ 
-</xsl:text>
-    <xsl:text>              this.disabled_elt.setAttribute("style", this.disabled_elt_style);
-</xsl:text>
-    <xsl:text>              /* hide inactive */ 
-</xsl:text>
-    <xsl:text>              this.inactive_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>              /* hide active */ 
-</xsl:text>
-    <xsl:text>              this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>            } else {
-</xsl:text>
-    <xsl:text>              /* hide disabled */ 
-</xsl:text>
-    <xsl:text>              this.disabled_elt.setAttribute("style", "display:none");
-</xsl:text>
-    <xsl:text>              this.update_activity();
-</xsl:text>
-    <xsl:text>            }
+    <xsl:text>            this.update_state();
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
@@ -5587,23 +5603,27 @@
     <xsl:text>        this.element.onclick = this.make_on_click();
 </xsl:text>
     <xsl:if test="$have_activity">
-      <xsl:text>        this.active_elt_style = this.active_elt.getAttribute("style");
-</xsl:text>
-      <xsl:text>        this.inactive_elt_style = this.inactive_elt.getAttribute("style");
-</xsl:text>
       <xsl:text>        this.activable = true;
 </xsl:text>
     </xsl:if>
+    <xsl:if test="not($have_disability)">
+      <xsl:text>        this.unsubscribable = true;
+</xsl:text>
+    </xsl:if>
+    <xsl:text>        this.update_state = </xsl:text>
     <xsl:choose>
       <xsl:when test="$have_disability">
-        <xsl:text>        this.disabled_elt_style = this.disabled_elt.getAttribute("style");
-</xsl:text>
+        <xsl:text>this.update_disability</xsl:text>
+      </xsl:when>
+      <xsl:when test="$have_activity">
+        <xsl:text>this.update_activity</xsl:text>
       </xsl:when>
       <xsl:otherwise>
-        <xsl:text>        this.unsubscribable = true;
-</xsl:text>
+        <xsl:text>null</xsl:text>
       </xsl:otherwise>
     </xsl:choose>
+    <xsl:text>;
+</xsl:text>
     <xsl:text>    },
 </xsl:text>
   </xsl:template>
@@ -7426,9 +7446,9 @@
     <xsl:comment>
       <xsl:text>Made with SVGHMI. https://beremiz.org</xsl:text>
     </xsl:comment>
-    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <html xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/1999/xhtml">
       <head>
-        <style type="text/css" media="screen">
+        <style media="screen" type="text/css">
           <xsl:value-of select="ns:GetFonts()"/>
         </style>
       </head>
--- a/svghmi/widget_jump.ysl2	Tue May 18 09:22:17 2021 +0200
+++ b/svghmi/widget_jump.ysl2	Tue May 18 09:28:44 2021 +0200
@@ -33,14 +33,29 @@
         update_activity() {
             if(this.active) {
                  /* show active */ 
-                 this.active_elt.setAttribute("style", this.active_elt_style);
+                 this.active_elt.style.display = "";
                  /* hide inactive */ 
-                 this.inactive_elt.setAttribute("style", "display:none");
+                 this.inactive_elt.style.display = "none";
             } else {
                  /* show inactive */ 
-                 this.inactive_elt.setAttribute("style", this.inactive_elt_style);
+                 this.inactive_elt.style.display = "";
                  /* hide active */ 
-                 this.active_elt.setAttribute("style", "display:none");
+                 this.active_elt.style.display = "none";
+            }
+        }
+
+        update_disability() {
+            if(this.disabled) {
+                /* show disabled */ 
+                this.disabled_elt.style.display = "";
+                /* hide inactive */ 
+                this.inactive_elt.style.display = "none";
+                /* hide active */ 
+                this.active_elt.style.display = "none";
+            } else {
+                /* hide disabled */ 
+                this.disabled_elt.style.display = "none";
+                this.update_activity();
             }
         }
 
@@ -48,10 +63,14 @@
             let that = this;
             const name = this.args[0];
             return function(evt){
-                /* TODO: suport path pointing to local variable whom value 
-                   would be an HMI_TREE index to jump to a relative page */
-                const index = that.indexes.length > 0 ? that.indexes[0] + that.offset : undefined;
-                switch_page(name, index);
+                /* TODO: in order to allow jumps to page selected through for exemple a dropdown,
+                   support path pointing to local variable whom value 
+                   would be an HMI_TREE index and then jump to a relative page not hard-coded in advance */
+
+                if(!that.disabled) {
+                    const index = that.indexes.length > 0 ? that.indexes[0] + that.offset : undefined;
+                    switch_page(name, index);
+                }
             }
         }
 
@@ -60,24 +79,13 @@
                 const ref_index = this.indexes.length > 0 ? this.indexes[0] + this.offset : undefined;
                 const ref_name = this.args[0];
                 this.active = ((ref_name == undefined || ref_name == page_name) && index == ref_index);
-                this.update_activity();
+                this.update_state();
             }
         }
 
         dispatch(value) {
             this.disabled = !Number(value);
-            if(this.disabled) {
-              /* show disabled */ 
-              this.disabled_elt.setAttribute("style", this.disabled_elt_style);
-              /* hide inactive */ 
-              this.inactive_elt.setAttribute("style", "display:none");
-              /* hide active */ 
-              this.active_elt.setAttribute("style", "display:none");
-            } else {
-              /* hide disabled */ 
-              this.disabled_elt.setAttribute("style", "display:none");
-              this.update_activity();
-            }
+            this.update_state();
         }
 ||
 }
@@ -95,19 +103,24 @@
     |     init: function() {
     |         this.element.onclick = this.make_on_click();
     if "$have_activity" {
-    |         this.active_elt_style = this.active_elt.getAttribute("style");
-    |         this.inactive_elt_style = this.inactive_elt.getAttribute("style");
     |         this.activable = true;
     }
+    if "not($have_disability)" {
+    |         this.unsubscribable = true;
+    }
+    >         this.update_state = 
     choose {
         when "$have_disability" {
-    |         this.disabled_elt_style = this.disabled_elt.getAttribute("style");
+            > this.update_disability
         }
-        otherwise {
-    |         this.unsubscribable = true;
+        when "$have_activity" {
+            > this.update_activity
         }
+        otherwise > null
     }
+    > ;\n
     |     },
+
 }
 
 widget_page("Jump"){
--- a/tests/svghmi/plc.xml	Tue May 18 09:22:17 2021 +0200
+++ b/tests/svghmi/plc.xml	Tue May 18 09:28:44 2021 +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="2020-12-01T09:52:25">
+  <contentHeader name="Unnamed" modificationDateTime="2021-05-13T10:44:29">
     <coordinateInfo>
       <fbd>
         <scaling x="5" y="5"/>
@@ -74,14 +74,21 @@
         </interface>
         <body>
           <FBD>
-            <block localId="4" typeName="PumpControl" instanceName="Pump0" executionOrderId="0" height="40" width="127">
+            <block localId="4" typeName="PumpControl" instanceName="Pump0" executionOrderId="0" height="60" width="127">
               <position x="595" y="50"/>
               <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="80"/>
+                      <position x="595" y="100"/>
+                      <position x="582" y="100"/>
+                      <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
                   </connectionPointIn>
@@ -97,12 +104,40 @@
               </connectionPointOut>
               <expression>TargetPressure</expression>
             </inVariable>
-            <block localId="1" typeName="PumpControl" instanceName="Pump1" executionOrderId="0" height="40" width="127">
-              <position x="595" y="180"/>
-              <inputVariables>
+            <block localId="1" typeName="PumpControl" instanceName="Pump1" executionOrderId="0" height="60" width="127">
+              <position x="595" y="280"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
+                    <connection refLocalId="5">
+                      <position x="595" y="330"/>
+                      <position x="582" y="330"/>
+                      <position x="582" y="80"/>
+                      <position x="570" y="80"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
+              </inputVariables>
+              <inOutVariables/>
+              <outputVariables/>
+            </block>
+            <block localId="2" typeName="PumpControl" instanceName="Pump2" executionOrderId="0" height="60" width="127">
+              <position x="595" y="160"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
+                <variable formalParameter="TargetPressure">
+                  <connectionPointIn>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
                       <position x="595" y="210"/>
                       <position x="582" y="210"/>
@@ -115,15 +150,20 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="2" typeName="PumpControl" instanceName="Pump2" executionOrderId="0" height="40" width="127">
-              <position x="595" y="110"/>
-              <inputVariables>
+            <block localId="3" typeName="PumpControl" instanceName="Pump3" executionOrderId="0" height="60" width="127">
+              <position x="595" y="395"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="140"/>
-                      <position x="582" y="140"/>
+                      <position x="595" y="445"/>
+                      <position x="582" y="445"/>
                       <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
@@ -133,15 +173,20 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="3" typeName="PumpControl" instanceName="Pump3" executionOrderId="0" height="40" width="127">
-              <position x="595" y="245"/>
-              <inputVariables>
+            <block localId="6" typeName="PumpControl" instanceName="Pump4" executionOrderId="0" height="60" width="127">
+              <position x="595" y="515"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="275"/>
-                      <position x="582" y="275"/>
+                      <position x="595" y="565"/>
+                      <position x="582" y="565"/>
                       <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
@@ -151,15 +196,24 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="6" typeName="PumpControl" instanceName="Pump4" executionOrderId="0" height="40" width="127">
-              <position x="595" y="315"/>
-              <inputVariables>
+            <block localId="7" typeName="PumpControl" instanceName="Pump5" executionOrderId="0" height="60" width="127">
+              <position x="595" y="645"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                    <connection refLocalId="11">
+                      <position x="595" y="675"/>
+                      <position x="570" y="675"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="345"/>
-                      <position x="582" y="345"/>
+                      <position x="595" y="695"/>
+                      <position x="582" y="695"/>
                       <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
@@ -169,15 +223,20 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="7" typeName="PumpControl" instanceName="Pump5" executionOrderId="0" height="40" width="127">
-              <position x="595" y="395"/>
-              <inputVariables>
+            <block localId="8" typeName="PumpControl" instanceName="Pump6" executionOrderId="0" height="60" width="127">
+              <position x="595" y="775"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="425"/>
-                      <position x="582" y="425"/>
+                      <position x="595" y="825"/>
+                      <position x="582" y="825"/>
                       <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
@@ -187,15 +246,24 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="8" typeName="PumpControl" instanceName="Pump6" executionOrderId="0" height="40" width="127">
-              <position x="595" y="475"/>
-              <inputVariables>
+            <block localId="9" typeName="PumpControl" instanceName="Pump7" executionOrderId="0" height="60" width="127">
+              <position x="595" y="895"/>
+              <inputVariables>
+                <variable formalParameter="Pump">
+                  <connectionPointIn>
+                    <relPosition x="0" y="30"/>
+                    <connection refLocalId="10">
+                      <position x="595" y="925"/>
+                      <position x="560" y="925"/>
+                    </connection>
+                  </connectionPointIn>
+                </variable>
                 <variable formalParameter="TargetPressure">
                   <connectionPointIn>
-                    <relPosition x="0" y="30"/>
+                    <relPosition x="0" y="50"/>
                     <connection refLocalId="5">
-                      <position x="595" y="505"/>
-                      <position x="582" y="505"/>
+                      <position x="595" y="945"/>
+                      <position x="582" y="945"/>
                       <position x="582" y="80"/>
                       <position x="570" y="80"/>
                     </connection>
@@ -205,35 +273,36 @@
               <inOutVariables/>
               <outputVariables/>
             </block>
-            <block localId="9" typeName="PumpControl" instanceName="Pump7" executionOrderId="0" height="40" width="127">
-              <position x="595" y="545"/>
-              <inputVariables>
-                <variable formalParameter="TargetPressure">
-                  <connectionPointIn>
-                    <relPosition x="0" y="30"/>
-                    <connection refLocalId="5">
-                      <position x="595" y="575"/>
-                      <position x="582" y="575"/>
-                      <position x="582" y="80"/>
-                      <position x="570" y="80"/>
-                    </connection>
-                  </connectionPointIn>
-                </variable>
-              </inputVariables>
-              <inOutVariables/>
-              <outputVariables/>
-            </block>
+            <inVariable localId="10" executionOrderId="0" height="25" width="20" negated="false">
+              <position x="540" y="915"/>
+              <connectionPointOut>
+                <relPosition x="20" y="10"/>
+              </connectionPointOut>
+              <expression>0</expression>
+            </inVariable>
+            <inVariable localId="11" executionOrderId="0" height="25" width="20" negated="false">
+              <position x="550" y="665"/>
+              <connectionPointOut>
+                <relPosition x="20" y="10"/>
+              </connectionPointOut>
+              <expression>0</expression>
+            </inVariable>
           </FBD>
         </body>
       </pou>
       <pou name="PumpControl" pouType="functionBlock">
         <interface>
+          <inputVars>
+            <variable name="Pump">
+              <type>
+                <derived name="HMI_NODE"/>
+              </type>
+              <initialValue>
+                <simpleValue value="1"/>
+              </initialValue>
+            </variable>
+          </inputVars>
           <localVars>
-            <variable name="Pump">
-              <type>
-                <derived name="HMI_NODE"/>
-              </type>
-            </variable>
             <variable name="Pressure">
               <type>
                 <derived name="HMI_INT"/>
--- a/tests/svghmi/svghmi_0@svghmi/svghmi.svg	Tue May 18 09:22:17 2021 +0200
+++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg	Tue May 18 09:28:44 2021 +0200
@@ -125,12 +125,12 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:document-units="px"
-     inkscape:current-layer="hmi0"
+     inkscape:current-layer="g1499-7"
      showgrid="false"
      units="px"
-     inkscape:zoom="1.1929688"
-     inkscape:cx="772.0138"
-     inkscape:cy="-68.272506"
+     inkscape:zoom="0.84355633"
+     inkscape:cx="1857.6296"
+     inkscape:cy="687.32797"
      inkscape:window-width="1600"
      inkscape:window-height="836"
      inkscape:window-x="0"
@@ -3465,8 +3465,9 @@
          inkscape:label="HMI:Jump:RelativePageTest@/PUMP0"
          id="g1458-8">
         <g
-           inkscape:label="button"
-           id="g1450-4">
+           inkscape:label="disabled"
+           id="g1450-4"
+           style="display:inline">
           <rect
              inkscape:label="button"
              ry="35.579063"
@@ -3475,6 +3476,34 @@
              height="95.723877"
              width="245.44583"
              id="rect1448-8"
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.28600003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+        </g>
+        <g
+           id="g1067"
+           inkscape:label="inactive"
+           style="display:inline">
+          <rect
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#3d3d3d;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+             id="rect1065"
+             width="245.44583"
+             height="95.723877"
+             x="971.96545"
+             y="594.82263"
+             ry="35.579063"
+             inkscape:label="button" />
+        </g>
+        <g
+           inkscape:label="active"
+           id="g1071"
+           style="display:inline">
+          <rect
+             inkscape:label="button"
+             ry="35.579063"
+             y="594.82263"
+             x="971.96545"
+             height="95.723877"
+             width="245.44583"
+             id="rect1069"
              style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
         </g>
         <g
@@ -3516,10 +3545,38 @@
          transform="matrix(0.57180538,0,0,0.57180538,1184.0513,-317.49049)">
         <g
            id="g1467-4"
-           inkscape:label="button">
+           inkscape:label="disabled">
+          <rect
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.28600003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+             id="rect1464-4"
+             width="245.44583"
+             height="95.723877"
+             x="971.96545"
+             y="594.82263"
+             ry="35.579063"
+             inkscape:label="button" />
+        </g>
+        <g
+           inkscape:label="inactive"
+           id="g1898"
+           style="display:inline">
+          <rect
+             inkscape:label="button"
+             ry="35.579063"
+             y="594.82263"
+             x="971.96545"
+             height="95.723877"
+             width="245.44583"
+             id="rect1896"
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#3d3d3d;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+        </g>
+        <g
+           id="g1902"
+           inkscape:label="active"
+           style="display:inline">
           <rect
              style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-             id="rect1464-4"
+             id="rect1900"
              width="245.44583"
              height="95.723877"
              x="971.96545"
@@ -3565,7 +3622,7 @@
          inkscape:label="HMI:Jump:RelativePageTest@/PUMP2"
          id="g1491-3">
         <g
-           inkscape:label="button"
+           inkscape:label="disabled"
            id="g1481-1">
           <rect
              inkscape:label="button"
@@ -3575,6 +3632,32 @@
              height="95.723877"
              width="245.44583"
              id="rect1479-7"
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.28600003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;font-variant-east_asian:normal" />
+        </g>
+        <g
+           id="g1906"
+           inkscape:label="inactive">
+          <rect
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#3d3d3d;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate;font-variant-east_asian:normal"
+             id="rect1904"
+             width="245.44583"
+             height="95.723877"
+             x="971.96545"
+             y="594.82263"
+             ry="35.579063"
+             inkscape:label="button" />
+        </g>
+        <g
+           inkscape:label="active"
+           id="g1910">
+          <rect
+             inkscape:label="button"
+             ry="35.579063"
+             y="594.82263"
+             x="971.96545"
+             height="95.723877"
+             width="245.44583"
+             id="rect1908"
              style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
         </g>
         <g
@@ -3616,9 +3699,10 @@
          transform="matrix(0.57180538,0,0,0.57180538,1504.0513,-317.49049)">
         <g
            id="g1499-7"
-           inkscape:label="button">
+           inkscape:label="disabled"
+           style="display:inline">
           <rect
-             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.28600003;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
              id="rect1497-8"
              width="245.44583"
              height="95.723877"
@@ -3628,6 +3712,34 @@
              inkscape:label="button" />
         </g>
         <g
+           id="g1918"
+           inkscape:label="inactive"
+           style="display:inline">
+          <rect
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#3d3d3d;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+             id="rect1916"
+             width="245.44583"
+             height="95.723877"
+             x="971.96545"
+             y="594.82263"
+             ry="35.579063"
+             inkscape:label="button" />
+        </g>
+        <g
+           inkscape:label="active"
+           id="g1914"
+           style="display:inline">
+          <rect
+             inkscape:label="button"
+             ry="35.579063"
+             y="594.82263"
+             x="971.96545"
+             height="95.723877"
+             width="245.44583"
+             id="rect1912"
+             style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#e6e6e6;fill-opacity:1;fill-rule:nonzero;stroke:#ff6600;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
+        </g>
+        <g
            id="g1507-5"
            inkscape:label="text">
           <text
--- a/util/paths.py	Tue May 18 09:22:17 2021 +0200
+++ b/util/paths.py	Tue May 18 09:28:44 2021 +0200
@@ -28,7 +28,6 @@
 import sys
 from builtins import str as text
 
-
 def AbsFile(file):
     if isinstance(file, str):
         file = text(file, sys.getfilesystemencoding())
@@ -49,3 +48,10 @@
     for dummy in range(0, level):
         path = os.path.dirname(path)
     return path
+
+def ThirdPartyPath(name):
+    """
+    Return folder where to find sibling projects like Modbus, CanFestival, BACnet
+    """
+    return os.path.join(AbsParentDir(__file__, 2), name)
+