svghmi/widgets_common.ysl2
branchwxPython4
changeset 3609 51a3d6f39944
parent 3603 f1a00aa8cb3b
child 3623 0237c28cd172
equal deleted inserted replaced
3608:02229133df43 3609:51a3d6f39944
    19         with "mandatory","'warn'";
    19         with "mandatory","'warn'";
    20         content;
    20         content;
    21     }
    21     }
    22 };
    22 };
    23 
    23 
       
    24 decl _activable(*level) alias - {
       
    25     |     activable_sub:{
       
    26     const "activity" labels("/active /inactive") {
       
    27         with "mandatory"{text *level};
       
    28         content;
       
    29     }
       
    30     value "$activity";
       
    31     const "has_activity","string-length($activity)>0";
       
    32     |     },
       
    33     |     has_activity: «$has_activity»,
       
    34 };
       
    35 
    24 decl activable() alias - {
    36 decl activable() alias - {
    25     |     activable_sub:{
    37     _activable("warn")
    26     warning_labels("/active /inactive") {
    38 };
    27         content;
    39 decl optional_activable() alias - {
    28     }
    40     _activable("no")
    29     |     }
    41 };
    30 };
    42 
    31 decl activable_labels(*ptr) alias - {
    43 decl activable_labels(*ptr) alias - {
    32     optional_labels(*ptr) {
    44     optional_labels(*ptr) {
    33         with "subelements","'active inactive'";
    45         with "subelements","'active inactive'";
    34         content;
    46         content;
    35     }
    47     }
    46     | }
    58     | }
    47 };
    59 };
    48 
    60 
    49 in xsl decl widget_defs(%name, match="widget[@type='%name']", mode="widget_defs") alias template {
    61 in xsl decl widget_defs(%name, match="widget[@type='%name']", mode="widget_defs") alias template {
    50     param "hmi_element";
    62     param "hmi_element";
       
    63     // all widget potentially has a "disabled" labeled element
       
    64     const "disability" optional_labels("/disabled");
       
    65     value "$disability";
       
    66     const "has_disability","string-length($disability)>0";
    51     content;
    67     content;
    52 };
    68 };
    53 
    69 
    54 in xsl decl widget_page(%name, match="widget[@type='%name']", mode="widget_page") alias template {
    70 in xsl decl widget_page(%name, match="widget[@type='%name']", mode="widget_page") alias template {
    55     param "page_desc";
    71     param "page_desc";
    63 template "svg:*", mode="hmi_widgets" {
    79 template "svg:*", mode="hmi_widgets" {
    64     const "widget", "func:widget(@id)";
    80     const "widget", "func:widget(@id)";
    65     const "eltid","@id";
    81     const "eltid","@id";
    66     const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,`
    82     const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,`
    67     const "indexes" foreach "$widget/path" {
    83     const "indexes" foreach "$widget/path" {
       
    84         if "position()!=last()" > ,
       
    85     }
       
    86 
       
    87     const "variables" foreach "$widget/path" {
       
    88         > [
    68         choose {
    89         choose {
    69             when "not(@index)" {
    90             when "not(@index)" {
    70                 choose {
    91                 choose {
    71                     when "not(@type)" {
    92                     when "not(@type)" {
    72                         warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
    93                         warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
    82             }
   103             }
    83             otherwise {
   104             otherwise {
    84                 > «@index»
   105                 > «@index»
    85             }
   106             }
    86         }
   107         }
    87         if "position()!=last()" > ,
   108         > , {
    88     }
   109         if "@min and @max"{
    89 
   110                 > minmax:[«@min», «@max»]
    90     const "minmaxes" foreach "$widget/path" {
   111                 if "@assign"
    91         choose {
   112                     > ,
    92             when "@min and @max"
   113         }
    93                 > [«@min»,«@max»]
   114         if "@assign"
    94             otherwise
   115                 > assign:"«@assign»"
    95                 > undefined
   116         > }]
    96         }
       
    97         if "position()!=last()" > ,
   117         if "position()!=last()" > ,
    98     }
   118     }
    99 
   119 
   100     const "freq" choose {
   120     const "freq" choose {
   101         when "$widget/@freq"
   121         when "$widget/@freq"
   102             > "«$widget/@freq»"
   122             > "«$widget/@freq»"
   103         otherwise
   123         otherwise
   104             > undefined
   124             > undefined
   105     }
   125     }
   106 
   126 
   107     |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$indexes»],[«$minmaxes»],{
   127     const "enable_expr" choose{
       
   128         when "$widget/@enable_expr"
       
   129             > true
       
   130         otherwise
       
   131             > false
       
   132     }
       
   133 
       
   134     |   "«@id»": new «$widget/@type»Widget ("«@id»",«$freq»,[«$args»],[«$variables»],«$enable_expr»,{
       
   135     if "$widget/@enable_expr" {
       
   136 
       
   137     |       assignments: [],
       
   138     |       compute_enable: function(value, oldval, varnum) {
       
   139     |         let result = false;
       
   140     |         do {
       
   141         foreach "$widget/path" {
       
   142             const "varid","generate-id()";
       
   143             const "varnum","position()-1";
       
   144             if "@assign" foreach "$widget/path[@assign]" if "$varid = generate-id()" {
       
   145     |           if(varnum == «$varnum») this.assignments[«position()-1»] = value;
       
   146     |           let «@assign» = this.assignments[«position()-1»];
       
   147     |           if(«@assign» == undefined) break;
       
   148             }
       
   149         }
       
   150     |           result = «$widget/@enable_expr»;
       
   151     |         } while(0);
       
   152     |         this.enable(result);
       
   153     |       },
       
   154     }
   108     apply "$widget", mode="widget_defs" with "hmi_element",".";
   155     apply "$widget", mode="widget_defs" with "hmi_element",".";
   109     |   })`if "position()!=last()" > ,`
   156     |   })`if "position()!=last()" > ,`
   110 }
   157 }
   111 
   158 
   112 emit "preamble:local-variable-indexes" {
   159 emit "preamble:local-variable-indexes" {
   185     }
   232     }
   186     function _show(elt, placeholder){
   233     function _show(elt, placeholder){
   187         placeholder.parentNode.insertBefore(elt, placeholder);
   234         placeholder.parentNode.insertBefore(elt, placeholder);
   188     }
   235     }
   189 
   236 
   190     function set_activation_state(eltsub, state){
   237     function set_activity_state(eltsub, state){
   191         if(eltsub.active_elt != undefined){
   238         if(eltsub.active_elt != undefined){
   192             if(eltsub.active_elt_placeholder == undefined){
   239             if(eltsub.active_elt_placeholder == undefined){
   193                 eltsub.active_elt_placeholder = document.createComment("");
   240                 eltsub.active_elt_placeholder = document.createComment("");
   194                 eltsub.active_elt.parentNode.insertBefore(eltsub.active_elt_placeholder, eltsub.active_elt);
   241                 eltsub.active_elt.parentNode.insertBefore(eltsub.active_elt_placeholder, eltsub.active_elt);
   195             }
   242             }
   200                 eltsub.inactive_elt_placeholder = document.createComment("");
   247                 eltsub.inactive_elt_placeholder = document.createComment("");
   201                 eltsub.inactive_elt.parentNode.insertBefore(eltsub.inactive_elt_placeholder, eltsub.inactive_elt);
   248                 eltsub.inactive_elt.parentNode.insertBefore(eltsub.inactive_elt_placeholder, eltsub.inactive_elt);
   202             }
   249             }
   203             ((state || state==undefined)?_hide:_show)(eltsub.inactive_elt, eltsub.inactive_elt_placeholder);
   250             ((state || state==undefined)?_hide:_show)(eltsub.inactive_elt, eltsub.inactive_elt_placeholder);
   204         }
   251         }
   205     }
       
   206 
       
   207     function activate_activable(eltsub) {
       
   208         set_activation_state(eltsub, true);
       
   209     }
       
   210 
       
   211     function inactivate_activable(eltsub) {
       
   212         set_activation_state(eltsub, false);
       
   213     }
   252     }
   214 
   253 
   215     class Widget {
   254     class Widget {
   216         offset = 0;
   255         offset = 0;
   217         frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */
   256         frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */
   218         unsubscribable = false;
   257         unsubscribable = false;
   219         pending_animate = false;
   258         pending_animate = false;
   220 
   259 
   221         constructor(elt_id, freq, args, indexes, minmaxes, members){
   260         constructor(elt_id, freq, args, variables, enable_expr, members){
   222             this.element_id = elt_id;
   261             this.element_id = elt_id;
   223             this.element = id(elt_id);
   262             this.element = id(elt_id);
   224             this.args = args;
   263             this.args = args;
   225             this.indexes = indexes;
   264             
   226             this.minmaxes = minmaxes;
   265             [this.indexes, this.variables_options] = (variables.length>0) ? zip(...variables) : [[],[]];
       
   266             this.indexes_length = this.indexes.length;
       
   267 
       
   268             this.enable_expr = enable_expr;
       
   269             this.enable_state = true;
       
   270             this.enable_displayed_state = true;
       
   271             this.enabled_elts = [];
       
   272 
   227             Object.keys(members).forEach(prop => this[prop]=members[prop]);
   273             Object.keys(members).forEach(prop => this[prop]=members[prop]);
   228             this.lastapply = indexes.map(() => undefined);
   274             this.lastapply = this.indexes.map(() => undefined);
   229             this.inhibit = indexes.map(() => undefined);
   275             this.inhibit = this.indexes.map(() => undefined);
   230             this.pending = indexes.map(() => undefined);
   276             this.pending = this.indexes.map(() => undefined);
   231             this.bound_uninhibit = this.uninhibit.bind(this);
   277             this.bound_uninhibit = this.uninhibit.bind(this);
   232 
   278 
   233             this.lastdispatch = indexes.map(() => undefined);
   279             this.lastdispatch = this.indexes.map(() => undefined);
   234             this.deafen = indexes.map(() => undefined);
   280             this.deafen = this.indexes.map(() => undefined);
   235             this.incoming = indexes.map(() => undefined);
   281             this.incoming = this.indexes.map(() => undefined);
   236             this.bound_undeafen = this.undeafen.bind(this);
   282             this.bound_undeafen = this.undeafen.bind(this);
   237 
   283 
   238             this.forced_frequency = freq;
   284             this.forced_frequency = freq;
   239             this.clip = true;
   285             this.clip = true;
   240         }
   286         }
   265                     init.call(this);
   311                     init.call(this);
   266                 } catch(err) {
   312                 } catch(err) {
   267                     console.log(err);
   313                     console.log(err);
   268                 }
   314                 }
   269             }
   315             }
       
   316 
       
   317             if(this.enable_expr){
       
   318                 this.enable_state = false;
       
   319                 this.enable_displayed_state = false;
       
   320                 for(let child of Array.from(this.element.children)){
       
   321                     let label = child.getAttribute("inkscape:label");
       
   322                     if(label!="disabled"){
       
   323                         this.enabled_elts.push(child);
       
   324                         this.element.removeChild(child);
       
   325                     }
       
   326                 }
       
   327             }
   270         }
   328         }
   271 
   329 
   272         unsub(){
   330         unsub(){
   273             /* remove subsribers */
   331             /* remove subsribers */
   274             if(!this.unsubscribable)
   332             for(let i = 0; i < this.indexes_length; i++) {
   275                 for(let i = 0; i < this.indexes.length; i++) {
   333                 /* flush updates pending because of inhibition */
   276                     /* flush updates pending because of inhibition */
   334                 let inhibition = this.inhibit[i];
   277                     let inhibition = this.inhibit[i];
   335                 if(inhibition != undefined){
   278                     if(inhibition != undefined){
   336                     clearTimeout(inhibition);
   279                         clearTimeout(inhibition);
   337                     this.lastapply[i] = undefined;
   280                         this.lastapply[i] = undefined;
   338                     this.uninhibit(i);
   281                         this.uninhibit(i);
   339                 }
   282                     }
   340                 let deafened = this.deafen[i];
   283                     let deafened = this.deafen[i];
   341                 if(deafened != undefined){
   284                     if(deafened != undefined){
   342                     clearTimeout(deafened);
   285                         clearTimeout(deafened);
   343                     this.lastdispatch[i] = undefined;
   286                         this.lastdispatch[i] = undefined;
   344                     this.undeafen(i);
   287                         this.undeafen(i);
   345                 }
   288                     }
   346                 let index = this.get_variable_index(i);
   289                     let index = this.indexes[i];
   347                 subscribers(index).delete(this);
   290                     if(this.relativeness[i])
   348             }
   291                         index += this.offset;
       
   292                     subscribers(index).delete(this);
       
   293                 }
       
   294             this.offset = 0;
   349             this.offset = 0;
   295             this.relativeness = undefined;
   350             this.relativeness = undefined;
   296         }
   351         }
   297 
   352 
   298         sub(new_offset=0, relativeness, container_id){
   353         sub(new_offset=0, relativeness, container_id){
   299             this.offset = new_offset;
   354             this.offset = new_offset;
   300             this.relativeness = relativeness;
   355             this.relativeness = relativeness;
   301             this.container_id = container_id ;
   356             this.container_id = container_id ;
   302             /* add this's subsribers */
   357             /* add this's subsribers */
   303             if(!this.unsubscribable)
   358             for(let i = 0; i < this.indexes_length; i++) {
   304                 for(let i = 0; i < this.indexes.length; i++) {
   359                 let index = this.get_variable_index(i);
   305                     let index = this.get_variable_index(i);
   360                 if(index == undefined) continue;
   306                     if(index == undefined) continue;
   361                 subscribers(index).add(this);
   307                     subscribers(index).add(this);
   362             }
   308                 }
   363             this.apply_cache(); 
   309             need_cache_apply.push(this); 
       
   310         }
   364         }
   311 
   365 
   312         apply_cache() {
   366         apply_cache() {
   313             if(!this.unsubscribable) for(let index in this.indexes){
   367             for(let i = 0; i < this.indexes_length; i++) {
   314                 /* dispatch current cache in newly opened page widgets */
   368                 /* dispatch current cache in newly opened page widgets */
   315                 let realindex = this.get_variable_index(index);
   369                 let realindex = this.get_variable_index(i);
   316                 if(realindex == undefined) continue;
   370                 if(realindex == undefined) continue;
   317                 let cached_val = cache[realindex];
   371                 let cached_val = cache[realindex];
   318                 if(cached_val != undefined)
   372                 if(cached_val != undefined)
   319                     this._dispatch(cached_val, cached_val, index);
   373                     this.feed_data_for_dispatch(cached_val, cached_val, i);
   320             }
   374             }
   321         }
   375         }
   322 
   376 
   323         get_variable_index(varnum) {
   377         get_variable_index(varnum) {
   324             let index = this.indexes[varnum];
   378             let index = this.indexes[varnum];
   337 
   391 
   338         undershot(new_val, min) {
   392         undershot(new_val, min) {
   339         }
   393         }
   340 
   394 
   341         clip_min_max(index, new_val) {
   395         clip_min_max(index, new_val) {
   342             let minmax = this.minmaxes[index];
   396             let minmax = this.variables_options[index].minmax;
   343             if(minmax !== undefined && typeof new_val == "number") {
   397             if(minmax !== undefined && typeof new_val == "number") {
   344                 let [min,max] = minmax;
   398                 let [min,max] = minmax;
   345                 if(new_val < min){
   399                 if(new_val < min){
   346                     this.undershot(new_val, min);
   400                     this.undershot(new_val, min);
   347                     return min;
   401                     return min;
   400             }
   454             }
   401         }
   455         }
   402 
   456 
   403         new_hmi_value(index, value, oldval) {
   457         new_hmi_value(index, value, oldval) {
   404             // TODO avoid searching, store index at sub()
   458             // TODO avoid searching, store index at sub()
   405             for(let i = 0; i < this.indexes.length; i++) {
   459             for(let i = 0; i < this.indexes_length; i++) {
   406                 let refindex = this.get_variable_index(i);
   460                 let refindex = this.get_variable_index(i);
   407                 if(refindex == undefined) continue;
   461                 if(refindex == undefined) continue;
   408 
   462 
   409                 if(index == refindex) {
   463                 if(index == refindex) {
   410                     this._dispatch(value, oldval, i);
   464                     this.feed_data_for_dispatch(value, oldval, i);
   411                     break;
   465                     break;
   412                 }
   466                 }
   413             }
   467             }
   414         }
   468         }
   415         
   469         
   416         undeafen(index){
   470         undeafen(index){
   417             this.deafen[index] = undefined;
   471             this.deafen[index] = undefined;
   418             let [new_val, old_val] = this.incoming[index];
   472             let [new_val, old_val] = this.incoming[index];
   419             this.incoming[index] = undefined;
   473             this.incoming[index] = undefined;
   420             this.dispatch(new_val, old_val, index);
   474             this.do_dispatch(new_val, old_val, index);
   421         }
   475         }
   422 
   476 
   423         _dispatch(value, oldval, varnum) {
   477         enable(enabled){
   424             let dispatch = this.dispatch;
   478             if(this.enable_state != enabled){
   425             if(dispatch != undefined){
   479                 this.enable_state = enabled;
       
   480                 this.request_animate();
       
   481             }
       
   482         }
       
   483 
       
   484         animate_enable(){
       
   485             if(this.enable_state && !this.enable_displayed_state){
       
   486                 //show widget
       
   487                 for(let child of this.enabled_elts){
       
   488                     this.element.appendChild(child);
       
   489                 }
       
   490 
       
   491                 //hide disabled content
       
   492                 if(this.disabled_elt && this.disabled_elt.parentNode != null)
       
   493                     this.element.removeChild(this.disabled_elt);
       
   494 
       
   495                 this.enable_displayed_state = true;
       
   496 
       
   497             }else if(!this.enable_state && this.enable_displayed_state){
       
   498 
       
   499                 //hide widget
       
   500                 for(let child of this.enabled_elts){
       
   501                     if(child.parentNode != null)
       
   502                         this.element.removeChild(child);
       
   503                 }
       
   504 
       
   505                 //show disabled content
       
   506                 if(this.disabled_elt)
       
   507                     this.element.appendChild(this.disabled_elt);
       
   508 
       
   509                 this.enable_displayed_state = false;
       
   510 
       
   511                 // once disabled activity display is lost
       
   512                 this.activity_displayed_state = undefined;
       
   513             }
       
   514         }
       
   515 
       
   516         feed_data_for_dispatch(value, oldval, varnum) {
       
   517             if(this.dispatch || this.enable_expr){
   426                 if(this.deafen[varnum] == undefined){
   518                 if(this.deafen[varnum] == undefined){
   427                     let now = Date.now();
   519                     let now = Date.now();
   428                     let min_interval = 1000/this.frequency;
   520                     let min_interval = 1000/this.frequency;
   429                     let lastdispatch = this.lastdispatch[varnum];
   521                     let lastdispatch = this.lastdispatch[varnum];
   430                     if(lastdispatch == undefined || now > lastdispatch + min_interval){
   522                     if(lastdispatch == undefined || now > lastdispatch + min_interval){
   431                         this.lastdispatch[varnum] = now;
   523                         this.lastdispatch[varnum] = now;
   432                         try {
   524                         this.do_dispatch(value, oldval, varnum)
   433                             dispatch.call(this, value, oldval, varnum);
       
   434                         } catch(err) {
       
   435                             console.log(err);
       
   436                         }
       
   437                     }
   525                     }
   438                     else {
   526                     else {
   439                         let elapsed = now - lastdispatch;
   527                         let elapsed = now - lastdispatch;
   440                         this.incoming[varnum] = [value, oldval];
   528                         this.incoming[varnum] = [value, oldval];
   441                         this.deafen[varnum] = setTimeout(this.bound_undeafen, min_interval - elapsed, varnum);
   529                         this.deafen[varnum] = setTimeout(this.bound_undeafen, min_interval - elapsed, varnum);
   445                     this.incoming[varnum] = [value, oldval];
   533                     this.incoming[varnum] = [value, oldval];
   446                 }
   534                 }
   447             }
   535             }
   448         }
   536         }
   449 
   537 
       
   538         do_dispatch(value, oldval, varnum) {
       
   539             if(this.dispatch) try {
       
   540                 this.dispatch(value, oldval, varnum);
       
   541             } catch(err) {
       
   542                 console.log(err);
       
   543             }
       
   544             if(this.enable_expr) try {
       
   545                 this.compute_enable(value, oldval, varnum);
       
   546             } catch(err) {
       
   547                 console.log(err);
       
   548             }
       
   549         }
       
   550 
   450         _animate(){
   551         _animate(){
   451             this.animate();
   552             if(this.enable_expr)
       
   553                 this.animate_enable();
       
   554             // inhibit widget animation when disabled
       
   555             if(!this.enable_expr || this.enable_state){
       
   556                 if(this.has_activity)
       
   557                     this.animate_activity();
       
   558                 if(this.animate != undefined)
       
   559                     this.animate();
       
   560             }
   452             this.pending_animate = false;
   561             this.pending_animate = false;
   453         }
   562         }
   454 
   563 
   455         request_animate(){
   564         request_animate(){
   456             if(!this.pending_animate){
   565             if(!this.pending_animate){
   458                 this.pending_animate = true;
   567                 this.pending_animate = true;
   459                 requestHMIAnimation();
   568                 requestHMIAnimation();
   460             }
   569             }
   461         }
   570         }
   462 
   571 
   463         set_activation_state(state){
   572         animate_activity(){
   464             set_activation_state(this.activable_sub, state);
   573             if(this.activity_displayed_state != this.activity_state){
       
   574                 set_activity_state(this.activable_sub, this.activity_state);
       
   575                 this.activity_displayed_state = this.activity_state;
       
   576             }
   465         }
   577         }
   466     }
   578     }
   467     ||
   579     ||
   468 }
   580 }
   469 
   581