SVGHMI - prepare page with cached data when switching. This prevents values that do not change and that was already subscribed in previous page from keeping undefined. svghmi
authorEdouard Tisserant
Wed, 30 Oct 2019 15:17:05 +0100
branchsvghmi
changeset 2811 4a81cec5f786
parent 2810 63b9a37b73c7
child 2812 68ac5bf43525
SVGHMI - prepare page with cached data when switching. This prevents values that do not change and that was already subscribed in previous page from keeping undefined.
svghmi/gen_index_xhtml.xslt
svghmi/svghmi.js
--- a/svghmi/gen_index_xhtml.xslt	Tue Oct 29 11:18:58 2019 +0100
+++ b/svghmi/gen_index_xhtml.xslt	Wed Oct 30 15:17:05 2019 +0100
@@ -344,6 +344,38 @@
 </xsl:text>
     <xsl:text>
 </xsl:text>
+    <xsl:text>function dispatch_value_to_widget(widget, index, value, oldval) {
+</xsl:text>
+    <xsl:text>    let idxidx = widget.indexes.indexOf(index);
+</xsl:text>
+    <xsl:text>    if(idxidx == -1){
+</xsl:text>
+    <xsl:text>        throw new Error("Dispatching to widget not interested, should not happen.");
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>    let d = widget.dispatch;
+</xsl:text>
+    <xsl:text>    if(typeof(d) == "function" &amp;&amp; idxidx == 0){
+</xsl:text>
+    <xsl:text>        return d.call(widget, value, oldval);
+</xsl:text>
+    <xsl:text>    }else if(typeof(d) == "object" &amp;&amp; d.length &gt;= idxidx){
+</xsl:text>
+    <xsl:text>        return d[idxidx].call(widget, value, oldval);
+</xsl:text>
+    <xsl:text>    }/* else dispatch_0, ..., dispatch_n ? */
+</xsl:text>
+    <xsl:text>    /*else {
+</xsl:text>
+    <xsl:text>        throw new Error("Dunno how to dispatch to widget at index = " + index);
+</xsl:text>
+    <xsl:text>    }*/
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+    <xsl:text>
+</xsl:text>
     <xsl:text>function dispatch_value(index, value) {
 </xsl:text>
     <xsl:text>    let widgets = subscribers[index];
@@ -360,378 +392,364 @@
 </xsl:text>
     <xsl:text>        for(let widget of widgets){
 </xsl:text>
-    <xsl:text>            let idxidx = widget.indexes.indexOf(index);
-</xsl:text>
-    <xsl:text>            if(idxidx == -1){
-</xsl:text>
-    <xsl:text>                throw new Error("Dispatching to widget not interested, should not happen.");
+    <xsl:text>            dispatch_value_to_widget(widget, index, value, oldval);
+</xsl:text>
+    <xsl:text>        }
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>};
+</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'));
+</xsl:text>
+    <xsl:text>ws.binaryType = 'arraybuffer';
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>const dvgetters = {
+</xsl:text>
+    <xsl:text>    INT: [DataView.prototype.getInt16, 2],
+</xsl:text>
+    <xsl:text>    BOOL: [DataView.prototype.getInt8, 1]
+</xsl:text>
+    <xsl:text>    /* TODO */
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>// Register message reception handler 
+</xsl:text>
+    <xsl:text>ws.onmessage = function (evt) {
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    let data = evt.data;
+</xsl:text>
+    <xsl:text>    let dv = new DataView(data);
+</xsl:text>
+    <xsl:text>    let i = 0;
+</xsl:text>
+    <xsl:text>    try {
+</xsl:text>
+    <xsl:text>        for(let hash_int of hmi_hash) {
+</xsl:text>
+    <xsl:text>            if(hash_int != dv.getUint8(i)){
+</xsl:text>
+    <xsl:text>                throw new Error("Hash doesn't match");
+</xsl:text>
+    <xsl:text>            };
+</xsl:text>
+    <xsl:text>            i++;
+</xsl:text>
+    <xsl:text>        };
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        while(i &lt; data.byteLength){
+</xsl:text>
+    <xsl:text>            let index = dv.getUint32(i, true);
+</xsl:text>
+    <xsl:text>            i += 4;
+</xsl:text>
+    <xsl:text>            let iectype = hmitree_types[index];
+</xsl:text>
+    <xsl:text>            if(iectype != undefined){
+</xsl:text>
+    <xsl:text>                let [dvgetter, bytesize] = dvgetters[iectype];
+</xsl:text>
+    <xsl:text>                let value = dvgetter.call(dv,i,true);
+</xsl:text>
+    <xsl:text>                dispatch_value(index, value);
+</xsl:text>
+    <xsl:text>                i += bytesize;
+</xsl:text>
+    <xsl:text>            } else {
+</xsl:text>
+    <xsl:text>                throw new Error("Unknown index "+index)
 </xsl:text>
     <xsl:text>            }
 </xsl:text>
-    <xsl:text>            let d = widget.dispatch;
-</xsl:text>
-    <xsl:text>            if(typeof(d) == "function" &amp;&amp; idxidx == 0){
-</xsl:text>
-    <xsl:text>                return d.call(widget, value, oldval);
-</xsl:text>
-    <xsl:text>            }else if(typeof(d) == "object" &amp;&amp; d.length &gt;= idxidx){
-</xsl:text>
-    <xsl:text>                return d[idxidx].call(widget, value, oldval);
-</xsl:text>
-    <xsl:text>            }/* else dispatch_0, ..., dispatch_n ? */
-</xsl:text>
-    <xsl:text>            /*else {
-</xsl:text>
-    <xsl:text>                throw new Error("Dunno how to dispatch to widget at index = " + index);
-</xsl:text>
-    <xsl:text>            }*/
+    <xsl:text>        };
+</xsl:text>
+    <xsl:text>    } catch(err) {
+</xsl:text>
+    <xsl:text>        // 1003 is for "Unsupported Data"
+</xsl:text>
+    <xsl:text>        // ws.close(1003, err.message);
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        // TODO : remove debug alert ?
+</xsl:text>
+    <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        // force reload ignoring cache
+</xsl:text>
+    <xsl:text>        location.reload(true);
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>function send_blob(data) {
+</xsl:text>
+    <xsl:text>    if(data.length &gt; 0) {
+</xsl:text>
+    <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
+</xsl:text>
+    <xsl:text>    };
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>const typedarray_types = {
+</xsl:text>
+    <xsl:text>    INT: Int16Array,
+</xsl:text>
+    <xsl:text>    BOOL: Uint8Array
+</xsl:text>
+    <xsl:text>    /* TODO */
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>function send_reset() {
+</xsl:text>
+    <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
+</xsl:text>
+    <xsl:text>};
+</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 =&gt; 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 =&gt; new Set());
+</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 &lt; subscribers.length; index++){
+</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>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>        let new_period = 0;
+</xsl:text>
+    <xsl:text>        if(widgets.size &gt; 0) {
+</xsl:text>
+    <xsl:text>            let maxfreq = 0;
+</xsl:text>
+    <xsl:text>            for(let widget of widgets)
+</xsl:text>
+    <xsl:text>                if(maxfreq &lt; widget.frequency)
+</xsl:text>
+    <xsl:text>                    maxfreq = widget.frequency;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>            if(maxfreq != 0)
+</xsl:text>
+    <xsl:text>                new_period = 1000/maxfreq;
 </xsl:text>
     <xsl:text>        }
 </xsl:text>
+    <xsl:text>
+</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>
+    <xsl:text>        }
+</xsl:text>
     <xsl:text>    }
 </xsl:text>
-    <xsl:text>};
-</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>    send_blob(delta);
+</xsl:text>
+    <xsl:text>};
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>function send_hmi_value(index, value) {
+</xsl:text>
+    <xsl:text>    let iectype = hmitree_types[index];
+</xsl:text>
+    <xsl:text>    let jstype = 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>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    cache[index] = value;
+</xsl:text>
+    <xsl:text>};
+</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>    let given_val = opstr.slice(1);
+</xsl:text>
+    <xsl:text>    let old_val = cache[index]
+</xsl:text>
+    <xsl:text>    let new_val;
+</xsl:text>
+    <xsl:text>    switch(op){
+</xsl:text>
+    <xsl:text>      case "=":
+</xsl:text>
+    <xsl:text>        eval("new_val"+opstr);
+</xsl:text>
+    <xsl:text>        break;
+</xsl:text>
+    <xsl:text>      case "+":
+</xsl:text>
+    <xsl:text>      case "-":
+</xsl:text>
+    <xsl:text>      case "*":
+</xsl:text>
+    <xsl:text>      case "/":
+</xsl:text>
+    <xsl:text>        if(old_val != undefined)
+</xsl:text>
+    <xsl:text>            new_val = eval("old_val"+opstr);
+</xsl:text>
+    <xsl:text>        break;
+</xsl:text>
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
+</xsl:text>
+    <xsl:text>        send_hmi_value(index, new_val);
+</xsl:text>
+    <xsl:text>    return new_val;
+</xsl:text>
+    <xsl:text>}
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>var current_page;
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>function switch_page(page_name) {
+</xsl:text>
+    <xsl:text>    let old_desc = page_desc[current_page];
+</xsl:text>
+    <xsl:text>    let new_desc = page_desc[page_name];
+</xsl:text>
+    <xsl:text>    /* TODO hide / show widgets */
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    /* remove subsribers of previous page if any */
+</xsl:text>
+    <xsl:text>    if(old_desc) for(let widget of old_desc.widgets){
+</xsl:text>
+    <xsl:text>        for(let index of widget.indexes){
+</xsl:text>
+    <xsl:text>            subscribers[index].delete(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'));
-</xsl:text>
-    <xsl:text>ws.binaryType = 'arraybuffer';
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>const dvgetters = {
-</xsl:text>
-    <xsl:text>    INT: [DataView.prototype.getInt16, 2],
-</xsl:text>
-    <xsl:text>    BOOL: [DataView.prototype.getInt8, 1]
-</xsl:text>
-    <xsl:text>    /* TODO */
-</xsl:text>
-    <xsl:text>};
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>// Register message reception handler 
-</xsl:text>
-    <xsl:text>ws.onmessage = function (evt) {
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    let data = evt.data;
-</xsl:text>
-    <xsl:text>    let dv = new DataView(data);
-</xsl:text>
-    <xsl:text>    let i = 0;
-</xsl:text>
-    <xsl:text>    try {
-</xsl:text>
-    <xsl:text>        for(let hash_int of hmi_hash) {
-</xsl:text>
-    <xsl:text>            if(hash_int != dv.getUint8(i)){
-</xsl:text>
-    <xsl:text>                throw new Error("Hash doesn't match");
-</xsl:text>
-    <xsl:text>            };
-</xsl:text>
-    <xsl:text>            i++;
-</xsl:text>
-    <xsl:text>        };
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        while(i &lt; data.byteLength){
-</xsl:text>
-    <xsl:text>            let index = dv.getUint32(i, true);
-</xsl:text>
-    <xsl:text>            i += 4;
-</xsl:text>
-    <xsl:text>            let iectype = hmitree_types[index];
-</xsl:text>
-    <xsl:text>            if(iectype != undefined){
-</xsl:text>
-    <xsl:text>                let [dvgetter, bytesize] = dvgetters[iectype];
-</xsl:text>
-    <xsl:text>                let value = dvgetter.call(dv,i,true);
-</xsl:text>
-    <xsl:text>                dispatch_value(index, value);
-</xsl:text>
-    <xsl:text>                i += bytesize;
-</xsl:text>
-    <xsl:text>            } else {
-</xsl:text>
-    <xsl:text>                throw new Error("Unknown index "+index)
+    <xsl:text>    }
+</xsl:text>
+    <xsl:text>
+</xsl:text>
+    <xsl:text>    if(new_desc) {
+</xsl:text>
+    <xsl:text>        /* add new subsribers if any */
+</xsl:text>
+    <xsl:text>        for(let widget of new_desc.widgets){
+</xsl:text>
+    <xsl:text>            for(let index of widget.indexes){
+</xsl:text>
+    <xsl:text>                subscribers[index].add(widget);
+</xsl:text>
+    <xsl:text>                let cached_val = cache[index];
+</xsl:text>
+    <xsl:text>                if(cached_val != undefined)
+</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>        };
-</xsl:text>
-    <xsl:text>    } catch(err) {
-</xsl:text>
-    <xsl:text>        // 1003 is for "Unsupported Data"
-</xsl:text>
-    <xsl:text>        // ws.close(1003, err.message);
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        // TODO : remove debug alert ?
-</xsl:text>
-    <xsl:text>        alert("Error : "+err.message+"\nHMI will be reloaded.");
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        // force reload ignoring cache
-</xsl:text>
-    <xsl:text>        location.reload(true);
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>};
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>function send_blob(data) {
-</xsl:text>
-    <xsl:text>    if(data.length &gt; 0) {
-</xsl:text>
-    <xsl:text>        ws.send(new Blob([new Uint8Array(hmi_hash)].concat(data)));
-</xsl:text>
-    <xsl:text>    };
-</xsl:text>
-    <xsl:text>};
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>const typedarray_types = {
-</xsl:text>
-    <xsl:text>    INT: Int16Array,
-</xsl:text>
-    <xsl:text>    BOOL: Uint8Array
-</xsl:text>
-    <xsl:text>    /* TODO */
-</xsl:text>
-    <xsl:text>};
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>function send_reset() {
-</xsl:text>
-    <xsl:text>    send_blob(new Uint8Array([1])); /* reset = 1 */
-</xsl:text>
-    <xsl:text>};
-</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 =&gt; 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 =&gt; new Set());
-</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 &lt; subscribers.length; index++){
-</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>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>        let new_period = 0;
-</xsl:text>
-    <xsl:text>        if(widgets.size &gt; 0) {
-</xsl:text>
-    <xsl:text>            let maxfreq = 0;
-</xsl:text>
-    <xsl:text>            for(let widget of widgets)
-</xsl:text>
-    <xsl:text>                if(maxfreq &lt; widget.frequency)
-</xsl:text>
-    <xsl:text>                    maxfreq = widget.frequency;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>            if(maxfreq != 0)
-</xsl:text>
-    <xsl:text>                new_period = 1000/maxfreq;
-</xsl:text>
     <xsl:text>        }
 </xsl:text>
-    <xsl:text>
-</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>
-    <xsl:text>        }
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    send_blob(delta);
-</xsl:text>
-    <xsl:text>};
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>function send_hmi_value(index, value) {
-</xsl:text>
-    <xsl:text>    let iectype = hmitree_types[index];
-</xsl:text>
-    <xsl:text>    let jstype = 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>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    cache[index] = value;
-</xsl:text>
-    <xsl:text>};
-</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>    let given_val = opstr.slice(1);
-</xsl:text>
-    <xsl:text>    let old_val = cache[index]
-</xsl:text>
-    <xsl:text>    let new_val;
-</xsl:text>
-    <xsl:text>    switch(op){
-</xsl:text>
-    <xsl:text>      case "=":
-</xsl:text>
-    <xsl:text>        eval("new_val"+opstr);
-</xsl:text>
-    <xsl:text>        break;
-</xsl:text>
-    <xsl:text>      case "+":
-</xsl:text>
-    <xsl:text>      case "-":
-</xsl:text>
-    <xsl:text>      case "*":
-</xsl:text>
-    <xsl:text>      case "/":
-</xsl:text>
-    <xsl:text>        if(old_val != undefined)
-</xsl:text>
-    <xsl:text>            new_val = eval("old_val"+opstr);
-</xsl:text>
-    <xsl:text>        break;
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>    if(new_val != undefined &amp;&amp; old_val != new_val)
-</xsl:text>
-    <xsl:text>        send_hmi_value(index, new_val);
-</xsl:text>
-    <xsl:text>    return new_val;
-</xsl:text>
-    <xsl:text>}
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>var current_page;
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>function switch_page(page_name) {
-</xsl:text>
-    <xsl:text>    let old_desc = page_desc[current_page];
-</xsl:text>
-    <xsl:text>    let new_desc = page_desc[page_name];
-</xsl:text>
-    <xsl:text>    /* TODO hide / show widgets */
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    /* remove subsribers of previous page if any */
-</xsl:text>
-    <xsl:text>    if(old_desc) for(let widget of old_desc.widgets){
-</xsl:text>
-    <xsl:text>        for(let index of widget.indexes){
-</xsl:text>
-    <xsl:text>            subscribers[index].delete(widget);
-</xsl:text>
-    <xsl:text>        }
-</xsl:text>
-    <xsl:text>    }
-</xsl:text>
-    <xsl:text>
-</xsl:text>
-    <xsl:text>    if(new_desc) {
-</xsl:text>
-    <xsl:text>        /* add new subsribers if any */
-</xsl:text>
-    <xsl:text>        for(let widget of new_desc.widgets){
-</xsl:text>
-    <xsl:text>            for(let index of widget.indexes){
-</xsl:text>
-    <xsl:text>                subscribers[index].add(widget);
-</xsl:text>
-    <xsl:text>            }
-</xsl:text>
-    <xsl:text>        }
-</xsl:text>
     <xsl:text>        svg_root.setAttribute('viewBox',new_desc.bbox.join(" "));
 </xsl:text>
+    <xsl:text>        // TODO dispatch current cache in newly opened page
+</xsl:text>
     <xsl:text>    }
 </xsl:text>
     <xsl:text>    current_page = page_name;
--- a/svghmi/svghmi.js	Tue Oct 29 11:18:58 2019 +0100
+++ b/svghmi/svghmi.js	Wed Oct 30 15:17:05 2019 +0100
@@ -2,6 +2,22 @@
 
 var cache = hmitree_types.map(_ignored => undefined);
 
+function dispatch_value_to_widget(widget, index, value, oldval) {
+    let idxidx = widget.indexes.indexOf(index);
+    if(idxidx == -1){
+        throw new Error("Dispatching to widget not interested, should not happen.");
+    }
+    let d = widget.dispatch;
+    if(typeof(d) == "function" && idxidx == 0){
+        return d.call(widget, value, oldval);
+    }else if(typeof(d) == "object" && d.length >= idxidx){
+        return d[idxidx].call(widget, value, oldval);
+    }/* else dispatch_0, ..., dispatch_n ? */
+    /*else {
+        throw new Error("Dunno how to dispatch to widget at index = " + index);
+    }*/
+}
+
 function dispatch_value(index, value) {
     let widgets = subscribers[index];
 
@@ -10,19 +26,7 @@
 
     if(widgets.size > 0) {
         for(let widget of widgets){
-            let idxidx = widget.indexes.indexOf(index);
-            if(idxidx == -1){
-                throw new Error("Dispatching to widget not interested, should not happen.");
-            }
-            let d = widget.dispatch;
-            if(typeof(d) == "function" && idxidx == 0){
-                return d.call(widget, value, oldval);
-            }else if(typeof(d) == "object" && d.length >= idxidx){
-                return d[idxidx].call(widget, value, oldval);
-            }/* else dispatch_0, ..., dispatch_n ? */
-            /*else {
-                throw new Error("Dunno how to dispatch to widget at index = " + index);
-            }*/
+            dispatch_value_to_widget(widget, index, value, oldval);
         }
     }
 };
@@ -193,6 +197,10 @@
         for(let widget of new_desc.widgets){
             for(let index of widget.indexes){
                 subscribers[index].add(widget);
+                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(" "));