# HG changeset patch # User Edouard Tisserant # Date 1597149453 -7200 # Node ID f6fe42b7ce60258e308532cae7eca4addf9444f8 # Parent 15e2df3e5610e5c73245ae4c8f78530ffd4fc80c SVGHMI: finished initial implementation of PAGE_LOCAL and HMI_LOCAL variables. diff -r 15e2df3e5610 -r f6fe42b7ce60 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Sat Aug 08 15:53:28 2020 +0200 +++ b/svghmi/gen_index_xhtml.xslt Tue Aug 11 14:37:33 2020 +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()"/> @@ -875,7 +875,7 @@ </xsl:when> <xsl:when test="@type = 'PAGE_LOCAL'"> <xsl:text>"</xsl:text> - <xsl:value-of select="substring(1,@value)"/> + <xsl:value-of select="substring(@value, 1)"/> <xsl:text>"</xsl:text> <xsl:if test="position()!=last()"> <xsl:text>,</xsl:text> @@ -959,36 +959,54 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text>var cache = hmitree_types.map(_ignored => undefined); +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text>function page_local_index(varname, pagename){ </xsl:text> <xsl:text> let pagevars = hmi_locals[pagename]; </xsl:text> + <xsl:text> let new_index; +</xsl:text> <xsl:text> if(pagevars == undefined){ </xsl:text> - <xsl:text> let new_index = next_available_index++; -</xsl:text> - <xsl:text> hmi_locals[pagename] = {varname:new_index} -</xsl:text> - <xsl:text> return new_index; + <xsl:text> new_index = next_available_index++; +</xsl:text> + <xsl:text> hmi_locals[pagename] = {[varname]:new_index} +</xsl:text> + <xsl:text> console.log("pagelocalindex insert",varname, pagename, new_index); </xsl:text> <xsl:text> } else { </xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> console.log("pagevars",pagevars); +</xsl:text> <xsl:text> let result = pagevars[varname]; </xsl:text> - <xsl:text> if(result==undefined){ -</xsl:text> - <xsl:text> let new_index = next_available_index++; -</xsl:text> - <xsl:text> pagevars[varname] = new_index; -</xsl:text> - <xsl:text> return new_index; + <xsl:text> if(result != undefined) { +</xsl:text> + <xsl:text> console.log("pagelocalindex reuse",varname, pagename, result); +</xsl:text> + <xsl:text> return result; </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> return result; + <xsl:text> +</xsl:text> + <xsl:text> new_index = next_available_index++; +</xsl:text> + <xsl:text> pagevars[varname] = new_index; +</xsl:text> + <xsl:text> console.log("pagelocalindex addwidget",varname, pagename, new_index); </xsl:text> <xsl:text> } </xsl:text> + <xsl:text> cache[new_index] = ""; +</xsl:text> + <xsl:text> return new_index; +</xsl:text> <xsl:text>} </xsl:text> <xsl:text> @@ -1050,7 +1068,7 @@ </xsl:text> <xsl:text> index += this.offset; </xsl:text> - <xsl:text> subscribers[index].delete(this); + <xsl:text> subscribers(index).delete(this); </xsl:text> <xsl:text> } </xsl:text> @@ -1078,9 +1096,7 @@ </xsl:text> <xsl:text> let index = this.get_variable_index(i); </xsl:text> - <xsl:text> if(index > last_remote_index) return; -</xsl:text> - <xsl:text> subscribers[index].add(this); + <xsl:text> subscribers(index).add(this); </xsl:text> <xsl:text> } </xsl:text> @@ -1116,8 +1132,6 @@ </xsl:text> <xsl:text> if(typeof(index) == "string"){ </xsl:text> - <xsl:text> let page = this.relativeness[varnum]; -</xsl:text> <xsl:text> index = page_local_index(index, this.container_id); </xsl:text> <xsl:text> } else { @@ -1152,7 +1166,7 @@ </xsl:text> <xsl:text> new_hmi_value(index, value, oldval) { </xsl:text> - <xsl:text> try { + <xsl:text> /* try {*/ </xsl:text> <xsl:text> // TODO avoid searching, store index at sub() </xsl:text> @@ -1192,11 +1206,11 @@ </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> } catch(err) { + <xsl:text> /* } catch(err) { </xsl:text> <xsl:text> console.log(err); </xsl:text> - <xsl:text> } + <xsl:text> }*/ </xsl:text> <xsl:text> } </xsl:text> @@ -1557,7 +1571,22 @@ <xsl:text>" is not a svg::text element</xsl:text> </xsl:message> </xsl:if> - <xsl:text> fields: [], + <xsl:variable name="field_initializer"> + <xsl:for-each select="path"> + <xsl:choose> + <xsl:when test="@type='HMI_STRING'"> + <xsl:text>""</xsl:text> + </xsl:when> + <xsl:otherwise>0</xsl:otherwise> + </xsl:choose> + <xsl:if test="position()!=last()"> + <xsl:text>,</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:variable> + <xsl:text> fields: [</xsl:text> + <xsl:value-of select="$field_initializer"/> + <xsl:text>], </xsl:text> </xsl:template> <preamble:display/> @@ -2955,14 +2984,6 @@ <xsl:text>" is not valid.</xsl:text> </xsl:message> </xsl:if> - <xsl:text> console.log("</xsl:text> - <xsl:value-of select="@id"/> - <xsl:text>", "</xsl:text> - <xsl:value-of select="$value_expr"/> - <xsl:text>", </xsl:text> - <xsl:value-of select="$value_expr"/> - <xsl:text>); -</xsl:text> <xsl:text> id("</xsl:text> <xsl:value-of select="@id"/> <xsl:text>").setAttribute("xlink:href", @@ -2976,14 +2997,6 @@ </xsl:template> <xsl:template mode="json_table_elt_render" match="svg:text"> <xsl:param name="value_expr"/> - <xsl:text> console.log("</xsl:text> - <xsl:value-of select="@id"/> - <xsl:text>", "</xsl:text> - <xsl:value-of select="$value_expr"/> - <xsl:text>", </xsl:text> - <xsl:value-of select="$value_expr"/> - <xsl:text>); -</xsl:text> <xsl:text> id("</xsl:text> <xsl:value-of select="@id"/> <xsl:text>").textContent = String(</xsl:text> @@ -3034,8 +3047,6 @@ <xsl:variable name="data_elt" select="$result_svg_ns//*[@id = $hmi_element/@id]/*[@inkscape:label = 'data']"/> <xsl:text> spread_json_data: function(jdata) { </xsl:text> - <xsl:text> console.log(jdata); -</xsl:text> <xsl:apply-templates mode="json_table_render" select="$data_elt/*"> <xsl:with-param name="objname" select="'jdata'"/> </xsl:apply-templates> @@ -3704,7 +3715,7 @@ <xsl:comment> <xsl:apply-templates select="document('')/*/debug:*"/> </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/> <body style="margin:0;overflow:hidden;"> <xsl:copy-of select="$result_svg"/> @@ -3745,8 +3756,6 @@ </xsl:text> <xsl:text> </xsl:text> - <xsl:text>var cache = hmitree_types.map(_ignored => undefined); -</xsl:text> <xsl:text>var updates = {}; </xsl:text> <xsl:text>var need_cache_apply = []; @@ -3757,7 +3766,7 @@ </xsl:text> <xsl:text>function dispatch_value(index, value) { </xsl:text> - <xsl:text> let widgets = subscribers[index]; + <xsl:text> let widgets = subscribers(index); </xsl:text> <xsl:text> </xsl:text> @@ -3857,6 +3866,8 @@ </xsl:text> <xsl:text> // -> pass Number(index) instead </xsl:text> + <xsl:text> console.log("apply updated local variable ",index, updates[index]); +</xsl:text> <xsl:text> dispatch_value(Number(index), updates[index]); </xsl:text> <xsl:text> delete updates[index]; @@ -4057,19 +4068,65 @@ </xsl:text> <xsl:text> </xsl:text> - <xsl:text>// subscription state, as it should be in hmi server -</xsl:text> - <xsl:text>// hmitree indexed array of integers -</xsl:text> - <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// subscription state as needed by widget now -</xsl:text> - <xsl:text>// hmitree indexed array of Sets of widgets objects -</xsl:text> - <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set()); + <xsl:text>var subscriptions = []; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function subscribers(index) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> let res; +</xsl:text> + <xsl:text> if(entry == undefined){ +</xsl:text> + <xsl:text> res = new Set(); +</xsl:text> + <xsl:text> subscriptions[index] = [res,0]; +</xsl:text> + <xsl:text> }else{ +</xsl:text> + <xsl:text> [res, _ign] = entry; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> return res +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function get_subscription_period(index) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> if(entry == undefined) +</xsl:text> + <xsl:text> return 0; +</xsl:text> + <xsl:text> let [_ign, period] = entry; +</xsl:text> + <xsl:text> return period; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function set_subscription_period(index, period) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> if(entry == undefined){ +</xsl:text> + <xsl:text> subscriptions[index] = [new Set(), period]; +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> entry[1] = period; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} </xsl:text> <xsl:text> </xsl:text> @@ -4079,7 +4136,7 @@ </xsl:text> <xsl:text>// PLC will periodically send variable at given frequency </xsl:text> - <xsl:text>subscribers[heartbeat_index].add({ + <xsl:text>subscribers(heartbeat_index).add({ </xsl:text> <xsl:text> /* type: "Watchdog", */ </xsl:text> @@ -4097,19 +4154,21 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text> +</xsl:text> <xsl:text>function update_subscriptions() { </xsl:text> <xsl:text> let delta = []; </xsl:text> - <xsl:text> for(let index = 0; index < subscribers.length; index++){ -</xsl:text> - <xsl:text> let widgets = subscribers[index]; + <xsl:text> for(let index in subscriptions){ +</xsl:text> + <xsl:text> let widgets = subscribers(index); </xsl:text> <xsl:text> </xsl:text> <xsl:text> // periods are in ms </xsl:text> - <xsl:text> let previous_period = subscriptions[index]; + <xsl:text> let previous_period = get_subscription_period(index); </xsl:text> <xsl:text> </xsl:text> @@ -4143,15 +4202,19 @@ </xsl:text> <xsl:text> if(previous_period != new_period) { </xsl:text> - <xsl:text> subscriptions[index] = new_period; -</xsl:text> - <xsl:text> delta.push( -</xsl:text> - <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ -</xsl:text> - <xsl:text> new Uint32Array([index]), -</xsl:text> - <xsl:text> new Uint16Array([new_period])); + <xsl:text> set_subscription_period(index, new_period); +</xsl:text> + <xsl:text> if(index <= last_remote_index){ +</xsl:text> + <xsl:text> delta.push( +</xsl:text> + <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ +</xsl:text> + <xsl:text> new Uint32Array([index]), +</xsl:text> + <xsl:text> new Uint16Array([new_period])); +</xsl:text> + <xsl:text> } </xsl:text> <xsl:text> } </xsl:text> @@ -4167,11 +4230,11 @@ </xsl:text> <xsl:text> if(index > last_remote_index){ </xsl:text> - <xsl:text> cache[index] = value; -</xsl:text> <xsl:text> console.log("updated local variable ",index,value); </xsl:text> - <xsl:text> /* TODO : dispatch value ASAP */ + <xsl:text> updates[index] = value; +</xsl:text> + <xsl:text> requestHMIAnimation(); </xsl:text> <xsl:text> return; </xsl:text> @@ -4375,7 +4438,7 @@ </xsl:text> <xsl:text> </xsl:text> - <xsl:text> container_id = String([page_name, page_index]); + <xsl:text> container_id = page_name + (page_index != undefined ? page_index : ""); </xsl:text> <xsl:text> </xsl:text> diff -r 15e2df3e5610 -r f6fe42b7ce60 svghmi/svghmi.js --- a/svghmi/svghmi.js Sat Aug 08 15:53:28 2020 +0200 +++ b/svghmi/svghmi.js Tue Aug 11 14:37:33 2020 +0200 @@ -1,12 +1,11 @@ // svghmi.js -var cache = hmitree_types.map(_ignored => undefined); var updates = {}; var need_cache_apply = []; function dispatch_value(index, value) { - let widgets = subscribers[index]; + let widgets = subscribers(index); let oldval = cache[index]; cache[index] = value; @@ -156,18 +155,41 @@ send_blob(new Uint8Array([1])); /* reset = 1 */ }; -// subscription state, as it should be in hmi server -// hmitree indexed array of integers -var subscriptions = hmitree_types.map(_ignored => 0); - -// subscription state as needed by widget now -// hmitree indexed array of Sets of widgets objects -var subscribers = hmitree_types.map(_ignored => new Set()); +var subscriptions = []; + +function subscribers(index) { + let entry = subscriptions[index]; + let res; + if(entry == undefined){ + res = new Set(); + subscriptions[index] = [res,0]; + }else{ + [res, _ign] = entry; + } + return res +} + +function get_subscription_period(index) { + let entry = subscriptions[index]; + if(entry == undefined) + return 0; + let [_ign, period] = entry; + return period; +} + +function set_subscription_period(index, period) { + let entry = subscriptions[index]; + if(entry == undefined){ + subscriptions[index] = [new Set(), period]; + } else { + entry[1] = period; + } +} // artificially subscribe the watchdog widget to "/heartbeat" hmi variable // Since dispatch directly calls change_hmi_value, // PLC will periodically send variable at given frequency -subscribers[heartbeat_index].add({ +subscribers(heartbeat_index).add({ /* type: "Watchdog", */ frequency: 1, indexes: [heartbeat_index], @@ -176,13 +198,14 @@ } }); + function update_subscriptions() { let delta = []; - for(let index = 0; index < subscribers.length; index++){ - let widgets = subscribers[index]; + for(let index in subscriptions){ + let widgets = subscribers(index); // periods are in ms - let previous_period = subscriptions[index]; + let previous_period = get_subscription_period(index); // subscribing with a zero period is unsubscribing let new_period = 0; @@ -199,11 +222,13 @@ } if(previous_period != new_period) { - subscriptions[index] = new_period; - delta.push( - new Uint8Array([2]), /* subscribe = 2 */ - new Uint32Array([index]), - new Uint16Array([new_period])); + set_subscription_period(index, new_period); + if(index <= last_remote_index){ + delta.push( + new Uint8Array([2]), /* subscribe = 2 */ + new Uint32Array([index]), + new Uint16Array([new_period])); + } } } send_blob(delta); @@ -211,9 +236,9 @@ function send_hmi_value(index, value) { if(index > last_remote_index){ - cache[index] = value; console.log("updated local variable ",index,value); - /* TODO : dispatch value ASAP */ + updates[index] = value; + requestHMIAnimation(); return; } @@ -315,7 +340,7 @@ } var new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; - container_id = String([page_name, page_index]); + container_id = page_name + (page_index != undefined ? page_index : ""); new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); diff -r 15e2df3e5610 -r f6fe42b7ce60 svghmi/widget_display.ysl2 --- a/svghmi/widget_display.ysl2 Sat Aug 08 15:53:28 2020 +0200 +++ b/svghmi/widget_display.ysl2 Tue Aug 11 14:37:33 2020 +0200 @@ -18,7 +18,14 @@ if "$hmi_element[not(self::svg:text)]" error > Display Widget id="«$hmi_element/@id»" is not a svg::text element - | fields: [], + const "field_initializer" foreach "path" { + choose{ + when "@type='HMI_STRING'" > "" + otherwise 0 + } + if "position()!=last()" > , + } + | fields: [«$field_initializer»], } emit "preamble:display" diff -r 15e2df3e5610 -r f6fe42b7ce60 svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Sat Aug 08 15:53:28 2020 +0200 +++ b/svghmi/widgets_common.ysl2 Tue Aug 11 14:37:33 2020 +0200 @@ -32,7 +32,7 @@ when "not(@type)" error > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree when "@type = 'PAGE_LOCAL'" - > "«substring(1,@value)»"`if "position()!=last()" > ,` + > "«substring(@value, 1)»"`if "position()!=last()" > ,` when "@type = 'HMI_LOCAL'" > hmi_local_index("«@value»")`if "position()!=last()" > ,` } @@ -75,21 +75,25 @@ var last_remote_index = hmitree_types.length - 1; var next_available_index = hmitree_types.length; + var cache = hmitree_types.map(_ignored => undefined); + function page_local_index(varname, pagename){ let pagevars = hmi_locals[pagename]; + let new_index; if(pagevars == undefined){ - let new_index = next_available_index++; - hmi_locals[pagename] = {varname:new_index} - return new_index; + new_index = next_available_index++; + hmi_locals[pagename] = {[varname]:new_index} } else { let result = pagevars[varname]; - if(result==undefined){ - let new_index = next_available_index++; - pagevars[varname] = new_index; - return new_index; - } - return result; - } + if(result != undefined) { + return result; + } + + new_index = next_available_index++; + pagevars[varname] = new_index; + } + cache[new_index] = ""; + return new_index; } function hmi_local_index(varname){ @@ -119,7 +123,7 @@ let index = this.indexes[i]; if(this.relativeness[i]) index += this.offset; - subscribers[index].delete(this); + subscribers(index).delete(this); } this.offset = 0; this.relativeness = undefined; @@ -133,8 +137,7 @@ if(!this.unsubscribable) for(let i = 0; i < this.indexes.length; i++) { let index = this.get_variable_index(i); - if(index > last_remote_index) return; - subscribers[index].add(this); + subscribers(index).add(this); } need_cache_apply.push(this); } @@ -152,7 +155,6 @@ get_variable_index(varnum) { let index = this.indexes[varnum]; if(typeof(index) == "string"){ - let page = this.relativeness[varnum]; index = page_local_index(index, this.container_id); } else { if(this.relativeness[varnum]){