SVGHMI : HMI_STRING now also supported from HMI to PLC svghmi
authorEdouard Tisserant
Fri, 17 Jan 2020 16:25:45 +0100
branchsvghmi
changeset 2829 4c2c50f60730
parent 2828 be947a338760
child 2830 15d7bd79d9e8
SVGHMI : HMI_STRING now also supported from HMI to PLC
svghmi/gen_index_xhtml.xslt
svghmi/gen_index_xhtml.ysl2
svghmi/svghmi.c
svghmi/svghmi.js
tests/svghmi/svghmi_0@svghmi/svghmi.svg
--- a/svghmi/gen_index_xhtml.xslt	Wed Jan 15 11:13:39 2020 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Fri Jan 17 16:25:45 2020 +0100
@@ -470,7 +470,7 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
-    <xsl:text>// Register message reception handler 
+    <xsl:text>// Register message reception handler
 </xsl:text>
     <xsl:text>ws.onmessage = function (evt) {
 </xsl:text>
@@ -564,9 +564,29 @@
 </xsl:text>
     <xsl:text>const typedarray_types = {
 </xsl:text>
-    <xsl:text>    INT: Int16Array,
-</xsl:text>
-    <xsl:text>    BOOL: Uint8Array
+    <xsl:text>    INT: (number) =&gt; new Int16Array([number]),
+</xsl:text>
+    <xsl:text>    BOOL: (truth) =&gt; new Int16Array([truth]),
+</xsl:text>
+    <xsl:text>    STRING: (str) =&gt; {
+</xsl:text>
+    <xsl:text>        // beremiz default string max size is 128
+</xsl:text>
+    <xsl:text>        str = str.slice(0,128);
+</xsl:text>
+    <xsl:text>        binary = new Uint8Array(str.length + 1);
+</xsl:text>
+    <xsl:text>        binary[0] = str.length;
+</xsl:text>
+    <xsl:text>        for(var i = 0; i &lt; str.length; i++){
+</xsl:text>
+    <xsl:text>            binary[i+1] = str.charCodeAt(i);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>        return binary;
+</xsl:text>
+    <xsl:text>    }
 </xsl:text>
     <xsl:text>    /* TODO */
 </xsl:text>
@@ -600,7 +620,7 @@
 </xsl:text>
     <xsl:text>// artificially subscribe the watchdog widget to "/heartbeat" hmi variable
 </xsl:text>
-    <xsl:text>// Since dispatch directly calls change_hmi_value, 
+    <xsl:text>// Since dispatch directly calls change_hmi_value,
 </xsl:text>
     <xsl:text>// PLC will periodically send variable at given frequency
 </xsl:text>
@@ -616,7 +636,7 @@
 </xsl:text>
     <xsl:text>        // console.log("Heartbeat" + value);
 </xsl:text>
-    <xsl:text>        change_hmi_value(this.indexes[0], "+1");
+    <xsl:text>        change_hmi_value(heartbeat_index, "+1");
 </xsl:text>
     <xsl:text>    }
 </xsl:text>
@@ -690,15 +710,15 @@
 </xsl:text>
     <xsl:text>    let iectype = hmitree_types[index];
 </xsl:text>
-    <xsl:text>    let jstype = typedarray_types[iectype];
+    <xsl:text>    let tobinary = typedarray_types[iectype];
 </xsl:text>
     <xsl:text>    send_blob([
 </xsl:text>
     <xsl:text>        new Uint8Array([0]),  /* setval = 0 */
 </xsl:text>
-    <xsl:text>        new Uint32Array([index]), 
-</xsl:text>
-    <xsl:text>        new jstype([value])]);
+    <xsl:text>        new Uint32Array([index]),
+</xsl:text>
+    <xsl:text>        tobinary(value)]);
 </xsl:text>
     <xsl:text>
 </xsl:text>
@@ -796,8 +816,6 @@
 </xsl:text>
     <xsl:text>                    dispatch_value_to_widget(widget, index, cached_val, cached_val);
 </xsl:text>
-    <xsl:text>                
-</xsl:text>
     <xsl:text>            }
 </xsl:text>
     <xsl:text>        }
@@ -967,6 +985,19 @@
     <xsl:text>},
 </xsl:text>
   </xsl:template>
+  <func:function name="func:escape_quotes">
+    <xsl:param name="txt"/>
+    <xsl:variable name="frst" select="substring-before($txt,'&quot;')"/>
+    <xsl:variable name="frstln" select="string-length($frst)"/>
+    <xsl:choose>
+      <xsl:when test="$frstln &gt; 0 and string-length($txt) &gt; $frstln">
+        <func:result select="concat($frst,'\&quot;',func:escape_quotes(substring-after($txt,'&quot;')))"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <func:result select="$txt"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </func:function>
   <xsl:template mode="widget_defs" match="widget[@type='Input']">
     <xsl:param name="hmi_element"/>
     <xsl:text>frequency: 5,
@@ -996,7 +1027,7 @@
       <xsl:text>        evt =&gt; alert('XXX TODO : Edit value'));
 </xsl:text>
     </xsl:if>
-    <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-][0-9]+')]">
+    <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]">
       <xsl:text>    document.getElementById("</xsl:text>
       <xsl:value-of select="@id"/>
       <xsl:text>").addEventListener(
@@ -1004,7 +1035,7 @@
       <xsl:text>        "click", 
 </xsl:text>
       <xsl:text>        evt =&gt; {let new_val = change_hmi_value(this.indexes[0], "</xsl:text>
-      <xsl:value-of select="@inkscape:label"/>
+      <xsl:value-of select="func:escape_quotes(@inkscape:label)"/>
       <xsl:text>");
 </xsl:text>
       <xsl:text>                this.value_elt.textContent = String(new_val);});
--- a/svghmi/gen_index_xhtml.ysl2	Wed Jan 15 11:13:39 2020 +0100
+++ b/svghmi/gen_index_xhtml.ysl2	Fri Jan 17 16:25:45 2020 +0100
@@ -373,6 +373,21 @@
         | },
     }
 
+    def "func:escape_quotes" {
+        param "txt";
+        // have to use a python string to enter escaped quote
+        const "frst", !"substring-before($txt,'\"')"!;
+        const "frstln", "string-length($frst)";
+        choose {
+            when "$frstln > 0 and string-length($txt) > $frstln" {
+                result !"concat($frst,'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!;
+            }
+            otherwise {
+                result "$txt";
+            }
+        }
+    }
+
     template "widget[@type='Input']", mode="widget_defs" {
         param "hmi_element";
         | frequency: 5,
@@ -387,10 +402,10 @@
         |         "click", 
         |         evt => alert('XXX TODO : Edit value'));
         }
-        foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-][0-9]+')]" {
+        foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-].+')]" {
         |     document.getElementById("«@id»").addEventListener(
         |         "click", 
-        |         evt => {let new_val = change_hmi_value(this.indexes[0], "«@inkscape:label»");
+        |         evt => {let new_val = change_hmi_value(this.indexes[0], "«func:escape_quotes(@inkscape:label)»");
         |                 this.value_elt.textContent = String(new_val);});
                           /* could gray out value until refreshed */
         }
--- a/svghmi/svghmi.c	Wed Jan 15 11:13:39 2020 +0100
+++ b/svghmi/svghmi.c	Fri Jan 17 16:25:45 2020 +0100
@@ -300,7 +300,10 @@
                     void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
                     void *dst_p = &rbuf[dsc->buf_index];
                     uint32_t sz = __get_type_enum_size(dsc->type);
-#warning TODO: size of string in recv
+
+                    if(__Is_a_string(dsc)){
+                        sz = ((STRING*)valptr)->len + 1;
+                    }
 
                     if((valptr + sz) <= end)
                     {
--- a/svghmi/svghmi.js	Wed Jan 15 11:13:39 2020 +0100
+++ b/svghmi/svghmi.js	Fri Jan 17 16:25:45 2020 +0100
@@ -59,7 +59,7 @@
     }
 };
 
-// Register message reception handler 
+// Register message reception handler
 ws.onmessage = function (evt) {
 
     let data = evt.data;
@@ -106,8 +106,18 @@
 };
 
 const typedarray_types = {
-    INT: Int16Array,
-    BOOL: Uint8Array
+    INT: (number) => new Int16Array([number]),
+    BOOL: (truth) => new Int16Array([truth]),
+    STRING: (str) => {
+        // beremiz default string max size is 128
+        str = str.slice(0,128);
+        binary = new Uint8Array(str.length + 1);
+        binary[0] = str.length;
+        for(var i = 0; i < str.length; i++){
+            binary[i+1] = str.charCodeAt(i);
+        }
+        return binary;
+    }
     /* TODO */
 };
 
@@ -124,7 +134,7 @@
 var subscribers = hmitree_types.map(_ignored => new Set());
 
 // artificially subscribe the watchdog widget to "/heartbeat" hmi variable
-// Since dispatch directly calls change_hmi_value, 
+// Since dispatch directly calls change_hmi_value,
 // PLC will periodically send variable at given frequency
 subscribers[heartbeat_index].add({
     /* type: "Watchdog", */
@@ -132,7 +142,7 @@
     indexes: [heartbeat_index],
     dispatch: function(value) {
         // console.log("Heartbeat" + value);
-        change_hmi_value(this.indexes[0], "+1");
+        change_hmi_value(heartbeat_index, "+1");
     }
 });
 
@@ -169,11 +179,11 @@
 
 function send_hmi_value(index, value) {
     let iectype = hmitree_types[index];
-    let jstype = typedarray_types[iectype];
+    let tobinary = typedarray_types[iectype];
     send_blob([
         new Uint8Array([0]),  /* setval = 0 */
-        new Uint32Array([index]), 
-        new jstype([value])]);
+        new Uint32Array([index]),
+        tobinary(value)]);
 
     cache[index] = value;
 };
@@ -222,7 +232,6 @@
                 let cached_val = cache[index];
                 if(cached_val != undefined)
                     dispatch_value_to_widget(widget, index, cached_val, cached_val);
-                
             }
         }
         svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
--- a/tests/svghmi/svghmi_0@svghmi/svghmi.svg	Wed Jan 15 11:13:39 2020 +0100
+++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg	Fri Jan 17 16:25:45 2020 +0100
@@ -31,6 +31,17 @@
   </metadata>
   <defs
      id="defs2">
+    <inkscape:tag
+       id="Set 1"
+       inkscape:label="HMI:AccessList@Admin"
+       inkscape:expanded="true">
+      <inkscape:tagref
+         xlink:href="#text995"
+         id="tagref192" />
+      <inkscape:tagref
+         xlink:href="#g991"
+         id="tagref194" />
+    </inkscape:tag>
     <linearGradient
        inkscape:collect="always"
        id="linearGradient962">
@@ -113,14 +124,14 @@
      inkscape:pageopacity="0"
      inkscape:pageshadow="2"
      inkscape:document-units="px"
-     inkscape:current-layer="hmi0"
+     inkscape:current-layer="g208"
      showgrid="false"
      units="px"
-     inkscape:zoom="1.2321777"
-     inkscape:cx="398.68209"
-     inkscape:cy="328.86048"
-     inkscape:window-width="1920"
-     inkscape:window-height="2105"
+     inkscape:zoom="2"
+     inkscape:cx="275.28708"
+     inkscape:cy="344.53292"
+     inkscape:window-width="1600"
+     inkscape:window-height="886"
      inkscape:window-x="0"
      inkscape:window-y="27"
      inkscape:window-maximized="0"
@@ -902,13 +913,13 @@
      xml:space="preserve"
      style="font-style:normal;font-weight:normal;font-size:80px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.5px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
      x="98.164062"
-     y="489.12109"
+     y="469.12109"
      id="text134"
      inkscape:label="HMI:Display@/PUMP0/STROUT"><tspan
        sodipodi:role="line"
        id="tspan132"
        x="98.164062"
-       y="489.12109"
+       y="469.12109"
        style="fill:#ffffff;fill-opacity:1;stroke-width:0.5px">8888</tspan></text>
   <text
      inkscape:label="HMI:Display@/PUMP0/BOOLOUT"
@@ -922,4 +933,194 @@
        x="98.164062"
        id="tspan136"
        sodipodi:role="line">8888</tspan></text>
+  <g
+     transform="matrix(0.5,0,0,0.5,90.110264,225.71623)"
+     id="g208"
+     inkscape:label="HMI:Input@/PUMP0/STRIN"
+     style="stroke-width:2">
+    <text
+       inkscape:label="value"
+       id="text164"
+       y="218.24219"
+       x="136.32812"
+       style="font-style:normal;font-weight:normal;font-size:160px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       xml:space="preserve"><tspan
+         style="stroke-width:2px"
+         y="218.24219"
+         x="136.32812"
+         id="tspan162"
+         sodipodi:role="line">8888</tspan></text>
+    <rect
+       style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ff00ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:10;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="rect166"
+       width="407.7037"
+       height="128"
+       x="139.85185"
+       y="95.40741"
+       onclick=""
+       inkscape:label="edit" />
+    <g
+       transform="translate(-416.52022,170.47452)"
+       inkscape:label="+&quot;dhu&quot;"
+       id="g174"
+       style="stroke-width:2">
+      <path
+         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:none;stroke-width:10;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"
+         inkscape:transform-center-y="-14.956361"
+         d="m 797.19546,145.18619 -80.62929,0.60214 -0.60215,-80.629288 80.6293,-0.60214 z"
+         id="path168"
+         inkscape:connector-curvature="0" />
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="733.58197"
+         y="111.05016"
+         id="text172"><tspan
+           sodipodi:role="line"
+           id="tspan170"
+           x="733.58197"
+           y="111.05016"
+           style="stroke-width:1px">dhu</tspan></text>
+    </g>
+    <g
+       transform="translate(-416.52022,170.47452)"
+       inkscape:label="=&quot;plop&quot;"
+       id="g182"
+       style="stroke-width:2">
+      <path
+         transform="matrix(0,-2.0000001,1.9999999,0,1034.195,1298.6541)"
+         sodipodi:type="star"
+         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:none;stroke-width:10;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="path176"
+         sodipodi:sides="3"
+         sodipodi:cx="596.74072"
+         sodipodi:cy="-184.98808"
+         sodipodi:r1="29.912722"
+         sodipodi:r2="14.956361"
+         sodipodi:arg1="0.52359878"
+         sodipodi:arg2="1.5707963"
+         inkscape:flatsided="true"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m 622.6459,-170.03172 -51.81035,0 25.90517,-44.86908 z"
+         inkscape:transform-center-y="-3.6154501e-05"
+         inkscape:transform-center-x="14.956371" />
+      <text
+         id="text180"
+         y="111.05016"
+         x="633.09552"
+         style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         xml:space="preserve"><tspan
+           style="stroke-width:1px"
+           y="111.05016"
+           x="633.09552"
+           id="tspan178"
+           sodipodi:role="line">plop</tspan></text>
+    </g>
+    <g
+       transform="translate(-416.52022,170.47452)"
+       inkscape:label="=&quot;mhoo&quot;"
+       id="g190"
+       style="stroke-width:2">
+      <path
+         inkscape:transform-center-y="-5.9989963e-06"
+         d="m 648.55108,-186.34718 -103.62071,0 51.81035,-89.73817 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="true"
+         sodipodi:arg2="1.5707963"
+         sodipodi:arg1="0.52359878"
+         sodipodi:r2="29.912722"
+         sodipodi:r1="59.825443"
+         sodipodi:cy="-216.2599"
+         sodipodi:cx="596.74072"
+         sodipodi:sides="3"
+         id="path184"
+         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:none;stroke-width:10;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"
+         sodipodi:type="star"
+         transform="rotate(-90,746.45698,-44.543641)"
+         inkscape:transform-center-x="14.956364" />
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="537.25018"
+         y="111.05016"
+         id="text188"><tspan
+           sodipodi:role="line"
+           id="tspan186"
+           x="537.25018"
+           y="111.05016"
+           style="stroke-width:1px">mhoo</tspan></text>
+    </g>
+    <g
+       transform="translate(-416.52022,170.47452)"
+       inkscape:label="=&quot;yodl&quot;"
+       id="g198"
+       style="stroke-width:2">
+      <path
+         sodipodi:type="star"
+         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:none;stroke-width:10;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="path192"
+         sodipodi:sides="3"
+         sodipodi:cx="596.74072"
+         sodipodi:cy="105.17262"
+         sodipodi:r1="59.825443"
+         sodipodi:r2="29.912722"
+         sodipodi:arg1="0.52359878"
+         sodipodi:arg2="1.5707963"
+         inkscape:flatsided="true"
+         inkscape:rounded="0"
+         inkscape:randomized="0"
+         d="m 648.55108,135.08534 -103.62071,0 51.81035,-89.738161 z"
+         inkscape:transform-center-y="-5.5023185e-06"
+         transform="matrix(0,-1,-1,0,1043.9134,701.91334)"
+         inkscape:transform-center-x="-14.956365" />
+      <text
+         xml:space="preserve"
+         style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         x="925.82605"
+         y="111.05016"
+         id="text196"><tspan
+           sodipodi:role="line"
+           id="tspan194"
+           x="925.82605"
+           y="111.05016"
+           style="stroke-width:1px">yodl</tspan></text>
+    </g>
+    <g
+       transform="translate(-416.52022,170.47452)"
+       inkscape:label="=&quot;mhe&quot;"
+       id="g206"
+       style="stroke-width:2">
+      <path
+         inkscape:transform-center-y="-3.3040441e-05"
+         d="m 622.6459,151.4008 -51.81035,0 25.90517,-44.86908 z"
+         inkscape:randomized="0"
+         inkscape:rounded="0"
+         inkscape:flatsided="true"
+         sodipodi:arg2="1.5707963"
+         sodipodi:arg1="0.52359878"
+         sodipodi:r2="14.956361"
+         sodipodi:r1="29.912722"
+         sodipodi:cy="136.44444"
+         sodipodi:cx="596.74072"
+         sodipodi:sides="3"
+         id="path200"
+         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:none;stroke-width:10;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"
+         sodipodi:type="star"
+         transform="matrix(0,-2.0000001,-1.9999999,0,1122.1514,1298.6541)"
+         inkscape:transform-center-x="-14.956349" />
+      <text
+         id="text204"
+         y="111.05016"
+         x="842.71497"
+         style="font-style:normal;font-weight:normal;font-size:20px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+         xml:space="preserve"><tspan
+           style="stroke-width:1px"
+           y="111.05016"
+           x="842.71497"
+           id="tspan202"
+           sodipodi:role="line">mhe</tspan></text>
+    </g>
+  </g>
 </svg>