SVGHMI: Added init call to all widgets at startup to bind events. More features in Input widget : Edit and Change buttons. WIP HMI->PLC value update, incoherent data detected in C part on update.
--- a/svghmi/gen_index_xhtml.xslt Sat Oct 19 01:23:30 2019 +0200
+++ b/svghmi/gen_index_xhtml.xslt Tue Oct 22 17:06:31 2019 +0200
@@ -326,6 +326,10 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text> // TODO : value cache
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text> if(widgets.size > 0) {
</xsl:text>
<xsl:text> for(let widget of widgets){
@@ -364,6 +368,26 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>function init_widgets() {
+</xsl:text>
+ <xsl:text> Object.keys(hmi_widgets).forEach(function(id) {
+</xsl:text>
+ <xsl:text> let widget = hmi_widgets[id];
+</xsl:text>
+ <xsl:text> let init = widget.init;
+</xsl:text>
+ <xsl:text> if(typeof(init) == "function"){
+</xsl:text>
+ <xsl:text> return init.call(widget);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> });
+</xsl:text>
+ <xsl:text>};
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>// Open WebSocket to relative "/ws" address
</xsl:text>
<xsl:text>var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
@@ -582,11 +606,11 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text>function update_value(index, value) {
+ <xsl:text>function send_hmi_value(index, value) {
</xsl:text>
<xsl:text> iectype = hmitree_types[index];
</xsl:text>
- <xsl:text> jstype = typedarray_types[iectypes];
+ <xsl:text> jstype = typedarray_types[iectype];
</xsl:text>
<xsl:text> send_blob([
</xsl:text>
@@ -602,6 +626,22 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text>function change_hmi_value(index, opstr) {
+</xsl:text>
+ <xsl:text> let op = opstr[0];
+</xsl:text>
+ <xsl:text> if(op == "=")
+</xsl:text>
+ <xsl:text> return send_hmi_value(index, Number(opstr.slice(1)));
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> alert('Change '+opstr+" TODO !!! (index :"+index+")");
+</xsl:text>
+ <xsl:text>}
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text>var current_page;
</xsl:text>
<xsl:text>
@@ -660,6 +700,8 @@
</xsl:text>
<xsl:text>ws.onopen = function (evt) {
</xsl:text>
+ <xsl:text> init_widgets();
+</xsl:text>
<xsl:text> send_reset();
</xsl:text>
<xsl:text> // show main page
@@ -773,18 +815,73 @@
</xsl:text>
</xsl:when>
<xsl:otherwise>
- <xsl:message terminate="yes">Display widget as a group not implemented</xsl:message>
+ <xsl:message terminate="yes">
+ <xsl:text>Display widget as a group not implemented</xsl:text>
+ </xsl:message>
</xsl:otherwise>
</xsl:choose>
<xsl:text>},
</xsl:text>
</xsl:template>
<xsl:template mode="widget_defs" match="widget[@type='Meter']">
- <xsl:text> frequency: 10,
+ <xsl:text>frequency: 10,
</xsl:text>
</xsl:template>
<xsl:template mode="widget_defs" match="widget[@type='Input']">
+ <xsl:param name="hmi_element"/>
+ <xsl:text>frequency: 5,
+</xsl:text>
+ <xsl:variable name="value_elt_id" select="$hmi_element//*[self::svg:text][@inkscape:label='value'][1]/@id"/>
+ <xsl:if test="not($value_elt_id)">
+ <xsl:message terminate="yes">
+ <xsl:text>Input widget must have a text element</xsl:text>
+ </xsl:message>
+ </xsl:if>
+ <xsl:text>value_elt: document.getElementById("</xsl:text>
+ <xsl:value-of select="$value_elt_id"/>
+ <xsl:text>"),
+</xsl:text>
+ <xsl:text>dispatch: function(value) {
+</xsl:text>
+ <xsl:text> this.value_elt.textContent = String(value);
+</xsl:text>
+ <xsl:text>},
+</xsl:text>
+ <xsl:variable name="edit_elt_id" select="$hmi_element/*[@inkscape:label='edit'][1]/@id"/>
+ <xsl:text>init: function() {
+</xsl:text>
+ <xsl:if test="$edit_elt_id">
+ <xsl:text> document.getElementById("</xsl:text>
+ <xsl:value-of select="$edit_elt_id"/>
+ <xsl:text>").addEventListener(
+</xsl:text>
+ <xsl:text> "click",
+</xsl:text>
+ <xsl:text> evt => alert('XXX TODO : Edit value'));
+</xsl:text>
+ </xsl:if>
+ <xsl:for-each select="$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-][0-9]+')]">
+ <xsl:text> document.getElementById("</xsl:text>
+ <xsl:value-of select="@id"/>
+ <xsl:text>").addEventListener(
+</xsl:text>
+ <xsl:text> "click",
+</xsl:text>
+ <xsl:text> evt => change_hmi_value(this.indexes[0], "</xsl:text>
+ <xsl:value-of select="@inkscape:label"/>
+ <xsl:text>"));
+</xsl:text>
+ </xsl:for-each>
+ <xsl:text>},
+</xsl:text>
+ </xsl:template>
+ <xsl:template mode="widget_defs" match="widget[@type='Button']"/>
+ <xsl:template mode="widget_defs" match="widget[@type='Toggle']">
<xsl:text> frequency: 5,
</xsl:text>
</xsl:template>
+ <xsl:template mode="widget_defs" match="widget[@type='Change']">
+ <xsl:text> frequency: 5,
+</xsl:text>
+ </xsl:template>
</xsl:stylesheet>
--- a/svghmi/gen_index_xhtml.ysl2 Sat Oct 19 01:23:30 2019 +0200
+++ b/svghmi/gen_index_xhtml.ysl2 Tue Oct 22 17:06:31 2019 +0200
@@ -317,16 +317,47 @@
| this.element.textContent = String(value);
}
otherwise {
- error "Display widget as a group not implemented";
+ error > Display widget as a group not implemented
}
}
| },
}
template "widget[@type='Meter']", mode="widget_defs" {
- | frequency: 10,
+ | frequency: 10,
}
template "widget[@type='Input']", mode="widget_defs" {
+ param "hmi_element";
+ | frequency: 5,
+ const "value_elt_id","$hmi_element//*[self::svg:text][@inkscape:label='value'][1]/@id";
+ if "not($value_elt_id)" error > Input widget must have a text element
+ | value_elt: document.getElementById("«$value_elt_id»"),
+ | dispatch: function(value) {
+ | this.value_elt.textContent = String(value);
+ | },
+ const "edit_elt_id","$hmi_element/*[@inkscape:label='edit'][1]/@id";
+ | init: function() {
+ if "$edit_elt_id" {
+ | document.getElementById("«$edit_elt_id»").addEventListener(
+ | "click",
+ | evt => alert('XXX TODO : Edit value'));
+ }
+ foreach "$hmi_element/*[regexp:test(@inkscape:label,'^[=+\-][0-9]+')]" {
+ | document.getElementById("«@id»").addEventListener(
+ | "click",
+ | evt => change_hmi_value(this.indexes[0], "«@inkscape:label»"));
+ }
+ | },
+ }
+ template "widget[@type='Button']", mode="widget_defs" {
+ }
+ template "widget[@type='Toggle']", mode="widget_defs" {
+ | frequency: 5,
+ }
+ template "widget[@type='Change']", mode="widget_defs" {
+ // HMI:Change:-10@/PUMP/VALUE
+ // HMI:Change:+1@/PUMP/VALUE
+ // HMI:Change:=42@/PUMP/VALUE
| frequency: 5,
}
// | frequency: 10`apply ".", mode="refresh_frequency"`,
--- a/svghmi/svghmi.js Sat Oct 19 01:23:30 2019 +0200
+++ b/svghmi/svghmi.js Tue Oct 22 17:06:31 2019 +0200
@@ -3,6 +3,8 @@
function dispatch_value(index, value) {
let widgets = subscribers[index];
+ // TODO : value cache
+
if(widgets.size > 0) {
for(let widget of widgets){
let idxidx = widget.indexes.indexOf(index);
@@ -22,6 +24,16 @@
}
};
+function init_widgets() {
+ Object.keys(hmi_widgets).forEach(function(id) {
+ let widget = hmi_widgets[id];
+ let init = widget.init;
+ if(typeof(init) == "function"){
+ return init.call(widget);
+ }
+ });
+};
+
// Open WebSocket to relative "/ws" address
var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
ws.binaryType = 'arraybuffer';
@@ -127,9 +139,9 @@
send_blob(delta);
};
-function update_value(index, value) {
+function send_hmi_value(index, value) {
iectype = hmitree_types[index];
- jstype = typedarray_types[iectypes];
+ jstype = typedarray_types[iectype];
send_blob([
new Uint8Array([0]), /* setval = 0 */
new jstype([value])
@@ -137,6 +149,14 @@
};
+function change_hmi_value(index, opstr) {
+ let op = opstr[0];
+ if(op == "=")
+ return send_hmi_value(index, Number(opstr.slice(1)));
+
+ alert('Change '+opstr+" TODO !!! (index :"+index+")");
+}
+
var current_page;
function switch_page(page_name) {
@@ -166,6 +186,7 @@
// Once connection established
ws.onopen = function (evt) {
+ init_widgets();
send_reset();
// show main page
switch_page(default_page);
--- a/tests/svghmi/beremiz.xml Sat Oct 19 01:23:30 2019 +0200
+++ b/tests/svghmi/beremiz.xml Tue Oct 22 17:06:31 2019 +0200
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
-<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://">
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="PYRO://127.0.0.1:61284">
<TargetType/>
<Libraries Enable_SVGHMI_Library="true"/>
</BeremizRoot>
--- a/tests/svghmi/svghmi_0@svghmi/svghmi.svg Sat Oct 19 01:23:30 2019 +0200
+++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg Tue Oct 22 17:06:31 2019 +0200
@@ -76,12 +76,12 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:document-units="px"
- inkscape:current-layer="hmi0"
+ inkscape:current-layer="g84"
showgrid="false"
units="px"
- inkscape:zoom="0.421875"
- inkscape:cx="486.11352"
- inkscape:cy="131.25685"
+ inkscape:zoom="0.84375"
+ inkscape:cx="-12.406671"
+ inkscape:cy="380.60108"
inkscape:window-width="1600"
inkscape:window-height="886"
inkscape:window-x="0"
@@ -174,18 +174,118 @@
sodipodi:role="line">Settings</tspan></text>
</g>
</g>
- <text
- xml:space="preserve"
- 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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- x="136.32812"
- y="218.24219"
- id="text5151"
- inkscape:label="HMI:Input@/TARGETPRESSURE"><tspan
- sodipodi:role="line"
- id="tspan5149"
+ <g
+ id="g84"
+ inkscape:label="HMI:Input@/TARGETPRESSURE">
+ <text
+ inkscape:label="value"
+ id="text5151"
+ y="218.24219"
x="136.32812"
- y="218.24219"
- style="stroke-width:1px">8888</tspan></text>
+ 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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ style="stroke-width:1px"
+ y="218.24219"
+ x="136.32812"
+ id="tspan5149"
+ sodipodi:role="line">8888</tspan></text>
+ <path
+ transform="scale(1,-1)"
+ 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:#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="path89"
+ sodipodi:sides="3"
+ sodipodi:cx="580.74072"
+ sodipodi:cy="-236.2599"
+ 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 632.55108,-206.34718 -103.62071,0 51.81035,-89.73817 z"
+ inkscape:transform-center-y="14.956363"
+ inkscape:label="-100" />
+ <path
+ inkscape:label="-10"
+ inkscape:transform-center-y="7.4781812"
+ d="m 606.6459,-170.03172 -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="-184.98808"
+ sodipodi:cx="580.74072"
+ sodipodi:sides="3"
+ id="path88"
+ 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"
+ sodipodi:type="star"
+ transform="scale(1,-1)" />
+ <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: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="rect85"
+ width="407.7037"
+ height="128"
+ x="139.85185"
+ y="95.40741"
+ onclick=""
+ inkscape:label="edit" />
+ <path
+ inkscape:label="+100"
+ inkscape:transform-center-y="-14.956361"
+ d="m 632.55108,115.08534 -103.62071,0 51.81035,-89.738161 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="85.172623"
+ sodipodi:cx="580.74072"
+ sodipodi:sides="3"
+ id="path87"
+ 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"
+ sodipodi:type="star" />
+ <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:#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="path86"
+ sodipodi:sides="3"
+ sodipodi:cx="580.74072"
+ sodipodi:cy="136.44444"
+ 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 606.6459,151.4008 -51.81035,0 25.90517,-44.86908 z"
+ inkscape:transform-center-y="-7.4781804"
+ inkscape:label="+10" />
+ <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:#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="path91"
+ sodipodi:sides="4"
+ sodipodi:cx="80.740723"
+ sodipodi:cy="165.17262"
+ sodipodi:r1="57.015106"
+ sodipodi:r2="29.912722"
+ sodipodi:arg1="0.77793027"
+ sodipodi:arg2="1.5633284"
+ inkscape:flatsided="true"
+ inkscape:rounded="-0.65084865"
+ inkscape:randomized="0"
+ d="M 121.35644,205.1862 C 158.18649,167.80191 3.342862,168.95829 40.72715,205.78834 78.111437,242.61839 76.95506,87.774762 40.125008,125.15905 3.2949549,162.54334 158.13858,161.38696 120.7543,124.55691 83.370008,87.726855 84.526385,242.57048 121.35644,205.1862 Z"
+ inkscape:transform-center-y="-14.956361"
+ inkscape:label="=0" />
+ </g>
<text
inkscape:label="HMI:Display@/PUMP/PRESSURE"
id="text823"