# HG changeset patch # User Edouard Tisserant # Date 1571756791 -7200 # Node ID 390acff12755097cde8b256e4f0dbf19c0a13f4c # Parent 68cee1366b9c7b7c8365b007e279d57a48b75960 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. diff -r 68cee1366b9c -r 390acff12755 svghmi/gen_index_xhtml.xslt --- 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> diff -r 68cee1366b9c -r 390acff12755 svghmi/gen_index_xhtml.ysl2 --- 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"`, diff -r 68cee1366b9c -r 390acff12755 svghmi/svghmi.js --- 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); diff -r 68cee1366b9c -r 390acff12755 tests/svghmi/beremiz.xml --- 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> diff -r 68cee1366b9c -r 390acff12755 tests/svghmi/svghmi_0@svghmi/svghmi.svg --- 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"