SVGHMI: Add inhibition to widget's apply_hmi_value() so that it does not change variable more frquently than given widget's frequency. This prevents flooding network with many update if browser is producing events at high rate, as for exemple when dragging ScrollBar's cursor.
// widget_button.ysl2
// Finite state machine
decl fsm(name);
decl state(name);
decl on_mouse(position);
decl on_dispatch(value);
decl jump(state);
decl show(eltname);
decl hmi_value(value);
// State machine to drive HMI_BOOL on a potentially laggy connection
const "_button_fsm" fsm {
state "init" {
on_dispatch "false" jump "released";
on_dispatch "true" jump "pressed";
}
state "pressing" {
// show "waitactive";
hmi_value "true";
on_dispatch "true" jump "pressed";
on_mouse "up" jump "shortpress";
}
state "pressed" {
show "active";
on_mouse "up" jump "releasing";
on_dispatch "false" jump "released";
}
state "shortpress" {
on_dispatch "true" jump "releasing";
on_mouse "down" jump "pressing";
}
state "releasing" {
// show "waitinactive";
hmi_value "false";
on_dispatch "false" jump "released";
on_mouse "down" jump "shortrelease";
}
state "released" {
show "inactive";
on_mouse "down" jump "pressing";
on_dispatch "true" jump "pressed";
}
state "shortrelease" {
on_dispatch "false" jump "pressing";
on_mouse "up" jump "releasing";
}
}
template "fsm", mode="dispatch_transition" {
| switch (this.state) {
apply "state", mode="dispatch_transition";
| }
}
template "state", mode="dispatch_transition" {
| case "«@name»":
apply "on-dispatch";
| break;
}
template "on-dispatch" {
| if(value == «@value») {
apply "jump", mode="transition";
| }
}
template "fsm", mode="mouse_transition" {
param "position";
| switch (this.state) {
apply "state", mode="mouse_transition" with "position", "$position";
| }
}
template "state", mode="mouse_transition" {
param "position";
| case "«@name»":
apply "on-mouse[@position = $position]";
| break;
}
template "on-mouse" {
// up or down state is already assumed because apply statement filters it
apply "jump", mode="transition";
}
template "jump", mode="transition" {
| this.state = "«@state»";
| this.«@state»_action();
}
template "fsm", mode="actions" {
apply "state", mode="actions";
}
template "state", mode="actions" {
| «@name»_action(){
//| console.log("Entering state «@name»");
apply "*", mode="actions";
| }
}
template "show", mode="actions" {
| this.display = "«@eltname»";
| this.request_animate();
}
template "hmi-value", mode="actions" {
| this.apply_hmi_value(0, «@value»);
}
template "widget[@type='Button']", mode="widget_class"{
const "fsm","exsl:node-set($_button_fsm)";
| class ButtonWidget extends Widget{
| frequency = 5;
| display = "inactive";
| state = "init";
| dispatch(value) {
// | console.log("dispatch"+value);
apply "$fsm", mode="dispatch_transition";
| }
| onmouseup(evt) {
| svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
// | console.log("onmouseup");
apply "$fsm", mode="mouse_transition" with "position", "'up'";
| }
| onmousedown(evt) {
| svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
// | console.log("onmousedown");
apply "$fsm", mode="mouse_transition" with "position", "'down'";
| }
apply "$fsm", mode="actions";
| animate(){
| if (this.active_elt && this.inactive_elt) {
foreach "str:split('active inactive')" {
| if(this.display == "«.»")
| this.«.»_elt.style.display = "";
| else
| this.«.»_elt.style.display = "none";
}
| }
| }
| init() {
| this.bound_onmouseup = this.onmouseup.bind(this);
| this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
| }
| }
}
template "widget[@type='Button']", mode="widget_defs" {
param "hmi_element";
optional_labels("active inactive");
}