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@3413: | console.log("Entering state «@name»"); 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"; Edouard@3085: | frequency = 5; 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@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@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: