SVGHMI: finished initial implementation of PAGE_LOCAL and HMI_LOCAL variables. svghmi
authorEdouard Tisserant
Tue, 11 Aug 2020 14:37:33 +0200
branchsvghmi
changeset 3022 f6fe42b7ce60
parent 3017 15e2df3e5610
child 3023 407a0205405a
SVGHMI: finished initial implementation of PAGE_LOCAL and HMI_LOCAL variables.
svghmi/gen_index_xhtml.xslt
svghmi/svghmi.js
svghmi/widget_display.ysl2
svghmi/widgets_common.ysl2
--- 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 =&gt; 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 &gt; 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 =&gt; 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>        // -&gt; 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 =&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>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 &lt; 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 &lt;= 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 &gt; 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>
--- 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));
 
--- 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"
--- 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]){