dgaberscek@2944: // widget_button.ysl2
dgaberscek@2944: 
edouard@3241: widget_desc("Button") {
edouard@3241:     longdesc
edouard@3241:     ||
edouard@3241:     Button widget takes one boolean variable path, and reflect current true
edouard@3241:     or false value by showing "active" or "inactive" labeled element
edouard@3241:     respectively. Pressing and releasing button changes variable to true and
edouard@3241:     false respectively. Potential inconsistency caused by quick consecutive
edouard@3241:     presses on the button is mitigated by using a state machine that wait for
edouard@3241:     previous state change to be reflected on variable before applying next one.
edouard@3241:     ||
edouard@3241: 
edouard@3241:     shortdesc > Push button reflecting consistently given boolean variable
edouard@3241: 
edouard@3241:     path name="value" accepts="HMI_BOOL" > Boolean variable
edouard@3241:     
edouard@3241: }
edouard@3241: 
Edouard@3085: // Finite state machine
Edouard@3085: decl fsm(name);
Edouard@3085: decl state(name);
Edouard@3085: decl on_mouse(position);
Edouard@3085: decl on_dispatch(value);
Edouard@3085: decl jump(state);
Edouard@3085: decl show(eltname);
Edouard@3085: decl hmi_value(value);
Edouard@3085: 
edouard@3232: gen_index_xhtml {
edouard@3232: 
edouard@3413: const "_push_button_fsm" fsm {
edouard@3413:     state "init" {
edouard@3413:         on_dispatch "false" jump "reflect_off";
edouard@3413:         on_dispatch "true" jump "reflect_on";
edouard@3413:     }
edouard@3413: 
edouard@3413:     state "reflect_on" {
edouard@3413:         show "active";
edouard@3413:         on_mouse "down" jump "on";
edouard@3413:         on_mouse "up" jump "off";
edouard@3413:         on_dispatch "false" jump "reflect_off";
edouard@3413:     }
edouard@3413: 
edouard@3413:     state "on" {
edouard@3413:         hmi_value "true";
edouard@3413:         show "active";
edouard@3413:         on_mouse "up" jump "off";
edouard@3413:         on_dispatch "false" jump "reflect_off";
edouard@3413:     }
edouard@3413: 
edouard@3413:     state "reflect_off" {
edouard@3413:         show "inactive";
edouard@3413:         on_mouse "down" jump "on";
edouard@3413:         on_mouse "up" jump "off";
edouard@3413:         on_dispatch "true" jump "reflect_on";
edouard@3413:     }
edouard@3413: 
edouard@3413:     state "off" {
edouard@3413:         hmi_value "false";
edouard@3413:         show "inactive";
edouard@3413:         on_mouse "down" jump "on";
edouard@3413:         on_dispatch "true" jump "reflect_on";
edouard@3413:     }
edouard@3413: }
edouard@3413: 
Edouard@3085: // State machine to drive HMI_BOOL on a potentially laggy connection
Edouard@3085: const "_button_fsm" fsm {
Edouard@3085:     state "init" {
Edouard@3085:         on_dispatch "false" jump "released";
Edouard@3085:         on_dispatch "true" jump "pressed";
Edouard@3085:     }
Edouard@3085: 
Edouard@3085:     state "pressing" {
Edouard@3085:         // show "waitactive";
Edouard@3085:         hmi_value "true";
Edouard@3085:         on_dispatch "true" jump "pressed";
Edouard@3085:         on_mouse "up" jump "shortpress";
Edouard@3085:     }
Edouard@3085:     state "pressed" {
Edouard@3085:         show "active";
Edouard@3085:         on_mouse "up" jump "releasing";
Edouard@3085:         on_dispatch "false" jump "released";
Edouard@3085:     }
Edouard@3085:     state "shortpress" {
Edouard@3085:         on_dispatch "true" jump "releasing";
Edouard@3085:         on_mouse "down" jump "pressing";
Edouard@3085:     }
Edouard@3085: 
Edouard@3085:     state "releasing" {
Edouard@3085:         // show "waitinactive";
Edouard@3085:         hmi_value "false";
Edouard@3085:         on_dispatch "false" jump "released";
Edouard@3085:         on_mouse "down" jump "shortrelease";
Edouard@3085:     }
Edouard@3085:     state "released" {
Edouard@3085:         show "inactive";
Edouard@3085:         on_mouse "down" jump "pressing";
Edouard@3085:         on_dispatch "true" jump "pressed";
Edouard@3085:     }
Edouard@3085:     state "shortrelease" {
Edouard@3085:         on_dispatch "false" jump "pressing";
Edouard@3085:         on_mouse "up" jump "releasing";
Edouard@3085:     }
Edouard@3085: }
Edouard@3085: 
Edouard@3085: template "fsm", mode="dispatch_transition" {
Edouard@3085:     |         switch (this.state) {
Edouard@3085:     apply "state", mode="dispatch_transition";
Edouard@3085:     |         }
Edouard@3085: }
Edouard@3085: template "state", mode="dispatch_transition" {
Edouard@3085:     |           case "«@name»":
Edouard@3086:        apply "on-dispatch";
Edouard@3085:     |             break;
Edouard@3085: }
Edouard@3085: template "on-dispatch" {
Edouard@3086:     |             if(value ==  «@value») {
Edouard@3086:     apply "jump", mode="transition";
Edouard@3086:     |             }
Edouard@3085: }
Edouard@3085: 
Edouard@3085: template "fsm", mode="mouse_transition" {
Edouard@3085:     param "position";
Edouard@3085:     |         switch (this.state) {
Edouard@3085:     apply "state", mode="mouse_transition" with "position", "$position";
Edouard@3085:     |         }
Edouard@3085: }
Edouard@3085: template "state", mode="mouse_transition" {
Edouard@3085:     param "position";
Edouard@3085:     |           case "«@name»":
Edouard@3085:     apply "on-mouse[@position = $position]";
Edouard@3085:     |             break;
Edouard@3085: }
Edouard@3085: template "on-mouse" {
Edouard@3086:     // up or down state is already assumed because apply statement filters it
Edouard@3086:     apply "jump", mode="transition";
Edouard@3085: }
Edouard@3085: 
Edouard@3085: template "jump", mode="transition" {
Edouard@3085:     |             this.state = "«@state»";
Edouard@3085:     |             this.«@state»_action();
Edouard@3085: }
Edouard@3085: 
Edouard@3085: template "fsm", mode="actions" {
Edouard@3085:     apply "state", mode="actions";
Edouard@3085: }
Edouard@3085: template "state", mode="actions" {
Edouard@3086:     |     «@name»_action(){
Edouard@3419:     // | console.log("Entering state «@name»", this.frequency);
Edouard@3085:     apply "*", mode="actions";
Edouard@3086:     |     }
Edouard@3085: }
Edouard@3085: template "show", mode="actions" {
Edouard@3085:     |         this.display = "«@eltname»";
Edouard@3085:     |         this.request_animate();
Edouard@3085: }
Edouard@3085: template "hmi-value", mode="actions" {
Edouard@3086:     |         this.apply_hmi_value(0, «@value»);
Edouard@3085: }
Edouard@3085: 
edouard@3232: }
edouard@3232: 
edouard@3413: 
edouard@3413: function "generated_button_class" {
edouard@3413:     param "fsm";
usveticic@3009: 
Edouard@3085:     |     display = "inactive";
Edouard@3085:     |     state = "init";
usveticic@3059: 
Edouard@3085:     |     dispatch(value) {
Edouard@3085:     apply "$fsm", mode="dispatch_transition";
Edouard@3085:     |     }
usveticic@3056: 
Edouard@3085:     |     onmouseup(evt) {
Edouard@3085:     |         svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
Edouard@3085:     apply "$fsm", mode="mouse_transition" with "position", "'up'";
Edouard@3085:     |     }
Edouard@3085:     |     onmousedown(evt) {
Edouard@3085:     |         svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
Edouard@3085:     apply "$fsm", mode="mouse_transition" with "position", "'down'";
Edouard@3085:     |     }
usveticic@3009: 
Edouard@3085:     apply "$fsm", mode="actions";
usveticic@3056: 
Edouard@3085:     |     animate(){
Edouard@3085:     |         if (this.active_elt && this.inactive_elt) {
Edouard@3085:     foreach "str:split('active inactive')" {
Edouard@3085:     |             if(this.display == "«.»")
Edouard@3085:     |                 this.«.»_elt.style.display = "";
Edouard@3085:     |             else
Edouard@3085:     |                 this.«.»_elt.style.display = "none";
Edouard@3085:     }
Edouard@3085:     |         }
Edouard@3085:     |     }
usveticic@3009: 
Edouard@3085:     |     init() {
Edouard@3085:     |         this.bound_onmouseup = this.onmouseup.bind(this);
Edouard@3085:     |         this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
Edouard@3085:     |     }
Edouard@3024: }
usveticic@3009: 
edouard@3413: 
edouard@3413: widget_class("Button"){
Edouard@3417:     |     frequency = 5;
edouard@3413:     const "fsm","exsl:node-set($_button_fsm)";
edouard@3413:     call "generated_button_class" with "fsm", "$fsm";
edouard@3413: }
edouard@3413: 
edouard@3232: widget_defs("Button") {
dgaberscek@2976:     optional_labels("active inactive");
edouard@3000: }
edouard@3413: 
edouard@3413: widget_class("PushButton"){
Edouard@3417:     |     frequency = 20;
edouard@3413:     const "fsm","exsl:node-set($_push_button_fsm)";
edouard@3413:     call "generated_button_class" with "fsm", "$fsm";
edouard@3413: }
edouard@3413: 
edouard@3413: widget_defs("PushButton") {
edouard@3413:     optional_labels("active inactive");
edouard@3413: }
edouard@3413: