svghmi/widgets_common.ysl2
branchwxPython4
changeset 3609 51a3d6f39944
parent 3603 f1a00aa8cb3b
child 3623 0237c28cd172
--- a/svghmi/widgets_common.ysl2	Tue Sep 13 16:51:54 2022 +0200
+++ b/svghmi/widgets_common.ysl2	Tue Sep 13 16:53:15 2022 +0200
@@ -21,13 +21,25 @@
     }
 };
 
+decl _activable(*level) alias - {
+    |     activable_sub:{
+    const "activity" labels("/active /inactive") {
+        with "mandatory"{text *level};
+        content;
+    }
+    value "$activity";
+    const "has_activity","string-length($activity)>0";
+    |     },
+    |     has_activity: «$has_activity»,
+};
+
 decl activable() alias - {
-    |     activable_sub:{
-    warning_labels("/active /inactive") {
-        content;
-    }
-    |     }
-};
+    _activable("warn")
+};
+decl optional_activable() alias - {
+    _activable("no")
+};
+
 decl activable_labels(*ptr) alias - {
     optional_labels(*ptr) {
         with "subelements","'active inactive'";
@@ -48,6 +60,10 @@
 
 in xsl decl widget_defs(%name, match="widget[@type='%name']", mode="widget_defs") alias template {
     param "hmi_element";
+    // all widget potentially has a "disabled" labeled element
+    const "disability" optional_labels("/disabled");
+    value "$disability";
+    const "has_disability","string-length($disability)>0";
     content;
 };
 
@@ -65,6 +81,11 @@
     const "eltid","@id";
     const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,`
     const "indexes" foreach "$widget/path" {
+        if "position()!=last()" > ,
+    }
+
+    const "variables" foreach "$widget/path" {
+        > [
         choose {
             when "not(@index)" {
                 choose {
@@ -84,16 +105,15 @@
                 > «@index»
             }
         }
-        if "position()!=last()" > ,
-    }
-
-    const "minmaxes" foreach "$widget/path" {
-        choose {
-            when "@min and @max"
-                > [«@min»,«@max»]
-            otherwise
-                > undefined
-        }
+        > , {
+        if "@min and @max"{
+                > minmax:[«@min», «@max»]
+                if "@assign"
+                    > ,
+        }
+        if "@assign"
+                > assign:"«@assign»"
+        > }]
         if "position()!=last()" > ,
     }
 
@@ -104,7 +124,34 @@
             > undefined
     }
 
-    |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$indexes»],[«$minmaxes»],{
+    const "enable_expr" choose{
+        when "$widget/@enable_expr"
+            > true
+        otherwise
+            > false
+    }
+
+    |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$variables»],«$enable_expr»,{
+    if "$widget/@enable_expr" {
+
+    |       assignments: [],
+    |       compute_enable: function(value, oldval, varnum) {
+    |         let result = false;
+    |         do {
+        foreach "$widget/path" {
+            const "varid","generate-id()";
+            const "varnum","position()-1";
+            if "@assign" foreach "$widget/path[@assign]" if "$varid = generate-id()" {
+    |           if(varnum == «$varnum») this.assignments[«position()-1»] = value;
+    |           let «@assign» = this.assignments[«position()-1»];
+    |           if(«@assign» == undefined) break;
+            }
+        }
+    |           result = «$widget/@enable_expr»;
+    |         } while(0);
+    |         this.enable(result);
+    |       },
+    }
     apply "$widget", mode="widget_defs" with "hmi_element",".";
     |   })`if "position()!=last()" > ,`
 }
@@ -187,7 +234,7 @@
         placeholder.parentNode.insertBefore(elt, placeholder);
     }
 
-    function set_activation_state(eltsub, state){
+    function set_activity_state(eltsub, state){
         if(eltsub.active_elt != undefined){
             if(eltsub.active_elt_placeholder == undefined){
                 eltsub.active_elt_placeholder = document.createComment("");
@@ -204,35 +251,34 @@
         }
     }
 
-    function activate_activable(eltsub) {
-        set_activation_state(eltsub, true);
-    }
-
-    function inactivate_activable(eltsub) {
-        set_activation_state(eltsub, false);
-    }
-
     class Widget {
         offset = 0;
         frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */
         unsubscribable = false;
         pending_animate = false;
 
-        constructor(elt_id, freq, args, indexes, minmaxes, members){
+        constructor(elt_id, freq, args, variables, enable_expr, members){
             this.element_id = elt_id;
             this.element = id(elt_id);
             this.args = args;
-            this.indexes = indexes;
-            this.minmaxes = minmaxes;
+            
+            [this.indexes, this.variables_options] = (variables.length>0) ? zip(...variables) : [[],[]];
+            this.indexes_length = this.indexes.length;
+
+            this.enable_expr = enable_expr;
+            this.enable_state = true;
+            this.enable_displayed_state = true;
+            this.enabled_elts = [];
+
             Object.keys(members).forEach(prop => this[prop]=members[prop]);
-            this.lastapply = indexes.map(() => undefined);
-            this.inhibit = indexes.map(() => undefined);
-            this.pending = indexes.map(() => undefined);
+            this.lastapply = this.indexes.map(() => undefined);
+            this.inhibit = this.indexes.map(() => undefined);
+            this.pending = this.indexes.map(() => undefined);
             this.bound_uninhibit = this.uninhibit.bind(this);
 
-            this.lastdispatch = indexes.map(() => undefined);
-            this.deafen = indexes.map(() => undefined);
-            this.incoming = indexes.map(() => undefined);
+            this.lastdispatch = this.indexes.map(() => undefined);
+            this.deafen = this.indexes.map(() => undefined);
+            this.incoming = this.indexes.map(() => undefined);
             this.bound_undeafen = this.undeafen.bind(this);
 
             this.forced_frequency = freq;
@@ -267,30 +313,39 @@
                     console.log(err);
                 }
             }
+
+            if(this.enable_expr){
+                this.enable_state = false;
+                this.enable_displayed_state = false;
+                for(let child of Array.from(this.element.children)){
+                    let label = child.getAttribute("inkscape:label");
+                    if(label!="disabled"){
+                        this.enabled_elts.push(child);
+                        this.element.removeChild(child);
+                    }
+                }
+            }
         }
 
         unsub(){
             /* remove subsribers */
-            if(!this.unsubscribable)
-                for(let i = 0; i < this.indexes.length; i++) {
-                    /* flush updates pending because of inhibition */
-                    let inhibition = this.inhibit[i];
-                    if(inhibition != undefined){
-                        clearTimeout(inhibition);
-                        this.lastapply[i] = undefined;
-                        this.uninhibit(i);
-                    }
-                    let deafened = this.deafen[i];
-                    if(deafened != undefined){
-                        clearTimeout(deafened);
-                        this.lastdispatch[i] = undefined;
-                        this.undeafen(i);
-                    }
-                    let index = this.indexes[i];
-                    if(this.relativeness[i])
-                        index += this.offset;
-                    subscribers(index).delete(this);
-                }
+            for(let i = 0; i < this.indexes_length; i++) {
+                /* flush updates pending because of inhibition */
+                let inhibition = this.inhibit[i];
+                if(inhibition != undefined){
+                    clearTimeout(inhibition);
+                    this.lastapply[i] = undefined;
+                    this.uninhibit(i);
+                }
+                let deafened = this.deafen[i];
+                if(deafened != undefined){
+                    clearTimeout(deafened);
+                    this.lastdispatch[i] = undefined;
+                    this.undeafen(i);
+                }
+                let index = this.get_variable_index(i);
+                subscribers(index).delete(this);
+            }
             this.offset = 0;
             this.relativeness = undefined;
         }
@@ -300,23 +355,22 @@
             this.relativeness = relativeness;
             this.container_id = container_id ;
             /* add this's subsribers */
-            if(!this.unsubscribable)
-                for(let i = 0; i < this.indexes.length; i++) {
-                    let index = this.get_variable_index(i);
-                    if(index == undefined) continue;
-                    subscribers(index).add(this);
-                }
-            need_cache_apply.push(this); 
+            for(let i = 0; i < this.indexes_length; i++) {
+                let index = this.get_variable_index(i);
+                if(index == undefined) continue;
+                subscribers(index).add(this);
+            }
+            this.apply_cache(); 
         }
 
         apply_cache() {
-            if(!this.unsubscribable) for(let index in this.indexes){
+            for(let i = 0; i < this.indexes_length; i++) {
                 /* dispatch current cache in newly opened page widgets */
-                let realindex = this.get_variable_index(index);
+                let realindex = this.get_variable_index(i);
                 if(realindex == undefined) continue;
                 let cached_val = cache[realindex];
                 if(cached_val != undefined)
-                    this._dispatch(cached_val, cached_val, index);
+                    this.feed_data_for_dispatch(cached_val, cached_val, i);
             }
         }
 
@@ -339,7 +393,7 @@
         }
 
         clip_min_max(index, new_val) {
-            let minmax = this.minmaxes[index];
+            let minmax = this.variables_options[index].minmax;
             if(minmax !== undefined && typeof new_val == "number") {
                 let [min,max] = minmax;
                 if(new_val < min){
@@ -402,12 +456,12 @@
 
         new_hmi_value(index, value, oldval) {
             // TODO avoid searching, store index at sub()
-            for(let i = 0; i < this.indexes.length; i++) {
+            for(let i = 0; i < this.indexes_length; i++) {
                 let refindex = this.get_variable_index(i);
                 if(refindex == undefined) continue;
 
                 if(index == refindex) {
-                    this._dispatch(value, oldval, i);
+                    this.feed_data_for_dispatch(value, oldval, i);
                     break;
                 }
             }
@@ -417,23 +471,57 @@
             this.deafen[index] = undefined;
             let [new_val, old_val] = this.incoming[index];
             this.incoming[index] = undefined;
-            this.dispatch(new_val, old_val, index);
-        }
-
-        _dispatch(value, oldval, varnum) {
-            let dispatch = this.dispatch;
-            if(dispatch != undefined){
+            this.do_dispatch(new_val, old_val, index);
+        }
+
+        enable(enabled){
+            if(this.enable_state != enabled){
+                this.enable_state = enabled;
+                this.request_animate();
+            }
+        }
+
+        animate_enable(){
+            if(this.enable_state && !this.enable_displayed_state){
+                //show widget
+                for(let child of this.enabled_elts){
+                    this.element.appendChild(child);
+                }
+
+                //hide disabled content
+                if(this.disabled_elt && this.disabled_elt.parentNode != null)
+                    this.element.removeChild(this.disabled_elt);
+
+                this.enable_displayed_state = true;
+
+            }else if(!this.enable_state && this.enable_displayed_state){
+
+                //hide widget
+                for(let child of this.enabled_elts){
+                    if(child.parentNode != null)
+                        this.element.removeChild(child);
+                }
+
+                //show disabled content
+                if(this.disabled_elt)
+                    this.element.appendChild(this.disabled_elt);
+
+                this.enable_displayed_state = false;
+
+                // once disabled activity display is lost
+                this.activity_displayed_state = undefined;
+            }
+        }
+
+        feed_data_for_dispatch(value, oldval, varnum) {
+            if(this.dispatch || this.enable_expr){
                 if(this.deafen[varnum] == undefined){
                     let now = Date.now();
                     let min_interval = 1000/this.frequency;
                     let lastdispatch = this.lastdispatch[varnum];
                     if(lastdispatch == undefined || now > lastdispatch + min_interval){
                         this.lastdispatch[varnum] = now;
-                        try {
-                            dispatch.call(this, value, oldval, varnum);
-                        } catch(err) {
-                            console.log(err);
-                        }
+                        this.do_dispatch(value, oldval, varnum)
                     }
                     else {
                         let elapsed = now - lastdispatch;
@@ -447,8 +535,29 @@
             }
         }
 
+        do_dispatch(value, oldval, varnum) {
+            if(this.dispatch) try {
+                this.dispatch(value, oldval, varnum);
+            } catch(err) {
+                console.log(err);
+            }
+            if(this.enable_expr) try {
+                this.compute_enable(value, oldval, varnum);
+            } catch(err) {
+                console.log(err);
+            }
+        }
+
         _animate(){
-            this.animate();
+            if(this.enable_expr)
+                this.animate_enable();
+            // inhibit widget animation when disabled
+            if(!this.enable_expr || this.enable_state){
+                if(this.has_activity)
+                    this.animate_activity();
+                if(this.animate != undefined)
+                    this.animate();
+            }
             this.pending_animate = false;
         }
 
@@ -460,8 +569,11 @@
             }
         }
 
-        set_activation_state(state){
-            set_activation_state(this.activable_sub, state);
+        animate_activity(){
+            if(this.activity_displayed_state != this.activity_state){
+                set_activity_state(this.activable_sub, this.activity_state);
+                this.activity_displayed_state = this.activity_state;
+            }
         }
     }
     ||