svghmi/widget_jump.ysl2
author Edouard Tisserant
Wed, 01 Mar 2023 10:54:54 +0100
changeset 3740 ac0e6de439b5
parent 3685 570a738239f4
permissions -rw-r--r--
Linux runtime: overrun detection for real-time timers and for plc execution.

If real-time timer wakes-up PLC thread too late (10% over period), then
warning is logged.

If PLC code (IO retreive, execution, IO publish) takes longer than requested
PLC execution cycle, then warning is logged, and CPU hoogging is mitigated
by delaying next PLC execution a few cylces more until having at least
1ms minimal idle time.
// widget_jump.ysl2

widget_desc("Jump") {
    longdesc
    ||
    Jump widget brings focus to a different page. Mandatory first argument
    gives name of the page.

    If first path is pointint to HMI_NODE variable is used as new reference
    when jumping to a relative page.

    Additional arguments are unordered options:

    - Absolute: force page jump to be not relative even if first path is of type HMI_NODE

    - name=value: Notify PLC about jump by setting variable with path having same name assigned

    "active"+"inactive" labeled elements can be provided and reflect current
    page being shown.

    Exemples:

    Relative jump:

    HMI:Jump:RelativePage@/PUMP9
    HMI:Jump:RelativePage@/PUMP9@role=.userrole#role=="admin"

    Absolute jump:

    HMI:Jump:AbsolutePage
    HMI:Jump:AbsolutePage@role=.userrole#role=="admin"

    Forced absolute jump:

    HMI:Jump:AbsolutePage:Absolute@/PUMP9
    HMI:Jump:AbsolutePage:Absolute:notify=1@notify=/PUMP9

    Jump with feedback

    HMI:Jump:AbsolutePage:notify=1@notify=.did_jump

    ||

    shortdesc > Jump to given page

    arg name="page" accepts="string" > name of page to jump to

    path name="reference" count="optional" accepts="HMI_NODE" > reference for relative jump
}

widget_class("Jump") {
||
        activable = false;
        frequency = 2;
        target_page_is_current_page = false;
        button_beeing_pressed = false;

        onmouseup(evt) {
            svg_root.removeEventListener("pointerup", this.bound_onmouseup, true);
            if(this.enable_state) {
                const index =
                    (this.is_relative && this.indexes.length > 0) ?
                    this.indexes[0] + this.offset : undefined;
                this.button_beeing_pressed = false;
                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
                fading_page_switch(this.args[0], index);
                this.notify();
            }
        }

        onmousedown(){
            if(this.enable_state) {
                svg_root.addEventListener("pointerup", this.bound_onmouseup, true);
                this.button_beeing_pressed = true;
                this.activity_state = true;
                this.request_animate();
            }
        }

        notify_page_change(page_name, index) {
            // called from animate()
            if(this.activable) {
                const ref_index = this.indexes.length > 0 ? this.indexes[0] + this.offset : undefined;
                const ref_name = this.args[0];
                this.target_page_is_current_page = ((ref_name == undefined || ref_name == page_name) && index == ref_index);
                this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed;
                // Since called from animate, update activity directly
                if(this.enable_displayed_state && this.has_activity) {
                    this.animate_activity();
                }
            }
        }
||
}

def "func:is_relative_jump" {
    param "widget";
    result "$widget/path and $widget/path[1]/@type='HMI_NODE' and not($widget/arg[position()>1 and @value = 'Absolute'])";
}

widget_defs("Jump") {
    optional_activable();

    const "jump_disability","$has_activity and $has_disability";

    |     init: function() {
    |         this.bound_onmouseup = this.onmouseup.bind(this);
    |         this.element.addEventListener("pointerdown", this.onmousedown.bind(this));
    if "$has_activity" {
    |         this.activable = true;
    }

    >         this.is_relative = 
    choose{
        when "func:is_relative_jump(.)" > true
        otherwise > false
    }
    > ;\n
    |     },

    |     notify: function() {
    const "paths","path";
    foreach "arg[position()>1 and contains(@value,'=')]"{
        const "name","substring-before(@value,'=')";
        const "value","substring-after(@value,'=')";
        const "index" foreach "$paths" if "@assign = $name" value "position()-1";
    |         // «@value»
    |         this.apply_hmi_value(«$index», «$value»);
    }
    |     },
}

widget_page("Jump"){
    param "page_desc";
    /* jump is considered relative jump if first path points to HMI_NODE
       but a jump can be forced Absolute by adding a "Absolute" argument */
    if "func:is_relative_jump(.)" {
        /* if relative check that given path is compatible with page's reference path */

        /* when no page name provided, check for same page */
        const "target_page_name" choose {
            when "arg" value "arg[1]/@value";
            otherwise value "$page_desc/arg[1]/@value";
        }
        const "target_page_path" choose {
            when "arg" value "$hmi_pages_descs[arg[1]/@value = $target_page_name]/path[not(@assign)]/@value";
            otherwise value "$page_desc/path[not(@assign)]/@value";
        }

        if "not(func:same_class_paths($target_page_path, path[1]/@value))"
            error > Jump id="«@id»" to page "«$target_page_name»" with incompatible path "«path[1]/@value» (must be same class as "«$target_page_path»")

    }
}



/* TODO: move to detachable pages ysl2 */
emit "cssdefs:jump"
||
.fade-out-page {
    animation: cubic-bezier(0, 0.8, 0.6, 1) fadeOut 0.6s both;
}

@keyframes fadeOut {
    0% { opacity: 1; }
    100% { opacity: 0; }
}

||

emit "declarations:jump"
||
var jumps_need_update = false;
var jump_history = [[default_page, undefined]];

function update_jumps() {
    // called from animate()
    page_desc[current_visible_page].jumps.map(w=>w.notify_page_change(current_visible_page,current_page_index));
    jumps_need_update = false;
};

||