--- 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 @@
-<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 test="@type = 'PAGE_LOCAL'">
- <xsl:value-of select="substring(1,@value)"/>
+ <xsl:value-of select="substring(@value, 1)"/> <xsl:if test="position()!=last()">
@@ -959,36 +959,54 @@
+ <xsl:text>var cache = hmitree_types.map(_ignored => undefined); <xsl:text>function page_local_index(varname, pagename){
<xsl:text> let pagevars = hmi_locals[pagename];
+ <xsl:text> let new_index; <xsl:text> if(pagevars == undefined){
- <xsl:text> let new_index = next_available_index++;
- <xsl:text> hmi_locals[pagename] = {varname:new_index}
- <xsl:text> return new_index;
+ <xsl:text> new_index = next_available_index++; + <xsl:text> hmi_locals[pagename] = {[varname]:new_index} + <xsl:text> console.log("pagelocalindex insert",varname, pagename, new_index); + <xsl:text> console.log("pagevars",pagevars); <xsl:text> let result = pagevars[varname];
- <xsl:text> if(result==undefined){
- <xsl:text> let new_index = next_available_index++;
- <xsl:text> pagevars[varname] = new_index;
- <xsl:text> return new_index;
+ <xsl:text> if(result != undefined) { + <xsl:text> console.log("pagelocalindex reuse",varname, pagename, result); + <xsl:text> return result; - <xsl:text> return result;
+ <xsl:text> new_index = next_available_index++; + <xsl:text> pagevars[varname] = new_index; + <xsl:text> console.log("pagelocalindex addwidget",varname, pagename, new_index); + <xsl:text> cache[new_index] = ""; + <xsl:text> return new_index; @@ -1050,7 +1068,7 @@
<xsl:text> index += this.offset;
- <xsl:text> subscribers[index].delete(this);
+ <xsl:text> subscribers(index).delete(this); @@ -1078,9 +1096,7 @@
<xsl:text> let index = this.get_variable_index(i);
- <xsl:text> if(index > last_remote_index) return;
- <xsl:text> subscribers[index].add(this);
+ <xsl:text> subscribers(index).add(this); @@ -1116,8 +1132,6 @@
<xsl:text> if(typeof(index) == "string"){
- <xsl:text> let page = this.relativeness[varnum];
<xsl:text> index = page_local_index(index, this.container_id);
@@ -1152,7 +1166,7 @@
<xsl:text> new_hmi_value(index, value, oldval) {
<xsl:text> // TODO avoid searching, store index at sub()
@@ -1192,11 +1206,11 @@
- <xsl:text> } catch(err) {
+ <xsl:text> /* } catch(err) { <xsl:text> console.log(err);
@@ -1557,7 +1571,22 @@
<xsl:text>" is not a svg::text element</xsl:text>
+ <xsl:variable name="field_initializer"> + <xsl:for-each select="path"> + <xsl:when test="@type='HMI_STRING'"> + <xsl:text>""</xsl:text> + <xsl:otherwise>0</xsl:otherwise> + <xsl:if test="position()!=last()"> + <xsl:text> fields: [</xsl:text> + <xsl:value-of select="$field_initializer"/> @@ -2955,14 +2984,6 @@
<xsl:text>" is not valid.</xsl:text>
- <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> id("</xsl:text>
<xsl:value-of select="@id"/>
<xsl:text>").setAttribute("xlink:href",
@@ -2976,14 +2997,6 @@
<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> 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> console.log(jdata);
<xsl:apply-templates mode="json_table_render" select="$data_elt/*">
<xsl:with-param name="objname" select="'jdata'"/>
@@ -3704,7 +3715,7 @@
<xsl:apply-templates select="document('')/*/debug:*"/>
- <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"> <body style="margin:0;overflow:hidden;">
<xsl:copy-of select="$result_svg"/>
@@ -3745,8 +3756,6 @@
- <xsl:text>var cache = hmitree_types.map(_ignored => undefined);
<xsl:text>var updates = {};
<xsl:text>var need_cache_apply = [];
@@ -3757,7 +3766,7 @@
<xsl:text>function dispatch_value(index, value) {
- <xsl:text> let widgets = subscribers[index];
+ <xsl:text> let widgets = subscribers(index); @@ -3857,6 +3866,8 @@
<xsl:text> // -> pass Number(index) instead
+ <xsl:text> console.log("apply updated local variable ",index, updates[index]); <xsl:text> dispatch_value(Number(index), updates[index]);
<xsl:text> delete updates[index];
@@ -4057,19 +4068,65 @@
- <xsl:text>// subscription state, as it should be in hmi server
- <xsl:text>// hmitree indexed array of integers
- <xsl:text>var subscriptions = hmitree_types.map(_ignored => 0);
+ <xsl:text>var subscriptions = []; + <xsl:text>function subscribers(index) { + <xsl:text> let entry = subscriptions[index]; + <xsl:text> if(entry == undefined){ + <xsl:text> res = new Set(); + <xsl:text> subscriptions[index] = [res,0]; + <xsl:text> [res, _ign] = entry; - <xsl:text>// subscription state as needed by widget now
- <xsl:text>// hmitree indexed array of Sets of widgets objects
- <xsl:text>var subscribers = hmitree_types.map(_ignored => new Set());
+ <xsl:text>function get_subscription_period(index) { + <xsl:text> let entry = subscriptions[index]; + <xsl:text> if(entry == undefined) + <xsl:text> let [_ign, period] = entry; + <xsl:text> return period; + <xsl:text>function set_subscription_period(index, period) { + <xsl:text> let entry = subscriptions[index]; + <xsl:text> if(entry == undefined){ + <xsl:text> subscriptions[index] = [new Set(), period]; + <xsl:text> entry[1] = period; @@ -4079,7 +4136,7 @@
<xsl:text>// PLC will periodically send variable at given frequency
- <xsl:text>subscribers[heartbeat_index].add({
+ <xsl:text>subscribers(heartbeat_index).add({ <xsl:text> /* type: "Watchdog", */
@@ -4097,19 +4154,21 @@
<xsl:text>function update_subscriptions() {
<xsl:text> let delta = [];
- <xsl:text> for(let index = 0; index < subscribers.length; index++){
- <xsl:text> let widgets = subscribers[index];
+ <xsl:text> for(let index in subscriptions){ + <xsl:text> let widgets = subscribers(index); <xsl:text> // periods are in ms
- <xsl:text> let previous_period = subscriptions[index];
+ <xsl:text> let previous_period = get_subscription_period(index); @@ -4143,15 +4202,19 @@
<xsl:text> if(previous_period != new_period) {
- <xsl:text> subscriptions[index] = new_period;
- <xsl:text> new Uint8Array([2]), /* subscribe = 2 */
- <xsl:text> new Uint32Array([index]),
- <xsl:text> new Uint16Array([new_period]));
+ <xsl:text> set_subscription_period(index, new_period); + <xsl:text> if(index <= last_remote_index){ + <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ + <xsl:text> new Uint32Array([index]), + <xsl:text> new Uint16Array([new_period])); @@ -4167,11 +4230,11 @@
<xsl:text> if(index > last_remote_index){
- <xsl:text> cache[index] = value;
<xsl:text> console.log("updated local variable ",index,value);
- <xsl:text> /* TODO : dispatch value ASAP */
+ <xsl:text> updates[index] = value; + <xsl:text> requestHMIAnimation(); @@ -4375,7 +4438,7 @@
- <xsl:text> container_id = String([page_name, page_index]);
+ <xsl:text> container_id = page_name + (page_index != undefined ? page_index : ""); --- 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 @@
-var cache = hmitree_types.map(_ignored => undefined);
var need_cache_apply = [];
function dispatch_value(index, value) {
- let widgets = subscribers[index];
+ let widgets = subscribers(index); let oldval = cache[index];
@@ -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);
+function subscribers(index) { + let entry = subscriptions[index]; + if(entry == undefined){ + subscriptions[index] = [res,0]; -// subscription state as needed by widget now
-// hmitree indexed array of Sets of widgets objects
-var subscribers = hmitree_types.map(_ignored => new Set());
+function get_subscription_period(index) { + let entry = subscriptions[index]; + let [_ign, period] = entry; +function set_subscription_period(index, period) { + let entry = subscriptions[index]; + if(entry == undefined){ + subscriptions[index] = [new Set(), 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({ indexes: [heartbeat_index],
@@ -176,13 +198,14 @@
function update_subscriptions() {
- for(let index = 0; index < subscribers.length; index++){
- let widgets = subscribers[index];
+ for(let index in subscriptions){ + let widgets = subscribers(index); - let previous_period = subscriptions[index];
+ let previous_period = get_subscription_period(index); // subscribing with a zero period is unsubscribing
@@ -199,11 +222,13 @@
if(previous_period != new_period) {
- subscriptions[index] = new_period;
- new Uint8Array([2]), /* subscribe = 2 */
- new Uint32Array([index]),
- new Uint16Array([new_period]));
+ set_subscription_period(index, new_period); + if(index <= last_remote_index){ + new Uint8Array([2]), /* subscribe = 2 */ + new Uint32Array([index]), + new Uint16Array([new_period])); @@ -211,9 +236,9 @@
function send_hmi_value(index, value) {
if(index > last_remote_index){
console.log("updated local variable ",index,value);
- /* TODO : dispatch value ASAP */
+ updates[index] = value; @@ -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));
--- 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 @@
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];
if(pagevars == undefined){
- let new_index = next_available_index++;
- hmi_locals[pagename] = {varname:new_index}
+ new_index = next_available_index++; + hmi_locals[pagename] = {[varname]:new_index} let result = pagevars[varname];
- let new_index = next_available_index++;
- pagevars[varname] = new_index;
+ if(result != undefined) {
+ new_index = next_available_index++; + pagevars[varname] = new_index; function hmi_local_index(varname){
@@ -119,7 +123,7 @@
let index = this.indexes[i];
- subscribers[index].delete(this);
+ subscribers(index).delete(this); this.relativeness = undefined;
@@ -133,8 +137,7 @@
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);
if(this.relativeness[varnum]){