// widgets_common.ysl2 in xsl decl labels(*ptr, name="defs_by_labels") alias call-template { with "hmi_element", "$hmi_element"; with "labels"{text *ptr}; content; }; decl optional_labels(*ptr) alias - { /* TODO add some per label xslt variable to check if exist */ labels(*ptr){ with "mandatory","'no'"; content; } }; decl activable_labels(*ptr) alias - { optional_labels(*ptr) { with "subelements","'active inactive'"; content; } }; template "svg:*", mode="hmi_widgets" { const "widget", "func:widget(@id)"; const "eltid","@id"; const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,` const "indexes" foreach "$widget/path" { choose { when "not(@index)" { choose { when "not(@type)" error > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree when "@type = 'PAGE_LOCAL'" > "«@value»"`if "position()!=last()" > ,` when "@type = 'HMI_LOCAL'" > hmi_local_index("«@value»")`if "position()!=last()" > ,` } } otherwise { > «@index»`if "position()!=last()" > ,` } } } | "«@id»": new «$widget/@type»Widget ("«@id»",[«$args»],[«$indexes»],{ apply "$widget", mode="widget_defs" with "hmi_element","."; | })`if "position()!=last()" > ,` } def "func:unique_types" { param "elts_with_type"; choose { when "count($elts_with_type) > 1" { const "prior_results","func:unique_types($elts_with_type[position()!=last()])"; choose { when "$elts_with_type[last()][@type = $prior_results/@type]"{ // type already in result "$prior_results"; } otherwise { result "$prior_results | $elts_with_type[last()]"; } } } otherwise { result "$elts_with_type"; } } } emit "preamble:local-variable-indexes" { || let hmi_locals = {}; var last_remote_index = hmitree_types.length - 1; var next_available_index = hmitree_types.length; const local_defaults = { || foreach "$parsed_widgets/widget[@type = 'VarInit']"{ if "count(path) != 1" error > VarInit «@id» must have only one variable given. if "path/@type != 'PAGE_LOCAL' and path/@type != 'HMI_LOCAL'" error > VarInit «@id» only applies to HMI variable. | "«path/@value»":«arg[1]/@value»`if "position()!=last()" > ,` } || }; var cache = hmitree_types.map(_ignored => undefined); function page_local_index(varname, pagename){ let pagevars = hmi_locals[pagename]; let new_index; if(pagevars == undefined){ new_index = next_available_index++; hmi_locals[pagename] = {[varname]:new_index} } else { let result = pagevars[varname]; if(result != undefined) { return result; } new_index = next_available_index++; pagevars[varname] = new_index; } let defaultval = local_defaults[varname]; if(defaultval != undefined) cache[new_index] = defaultval; return new_index; } function hmi_local_index(varname){ return page_local_index(varname, "HMI_LOCAL"); } || } emit "preamble:widget-base-class" { || var pending_widget_animates = []; class Widget { offset = 0; frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */ unsubscribable = false; pending_animate = false; constructor(elt_id,args,indexes,members){ this.element_id = elt_id; this.element = id(elt_id); this.args = args; this.indexes = indexes; Object.keys(members).forEach(prop => this[prop]=members[prop]); } unsub(){ /* remove subsribers */ if(!this.unsubscribable) for(let i = 0; i < this.indexes.length; i++) { let index = this.indexes[i]; if(this.relativeness[i]) index += this.offset; subscribers(index).delete(this); } this.offset = 0; this.relativeness = undefined; } sub(new_offset=0, relativeness, container_id){ this.offset = new_offset; this.relativeness = relativeness; this.container_id = container_id ; /* add this's subsribers */ if(!this.unsubscribable) for(let i = 0; i < this.indexes.length; i++) { let index = this.get_variable_index(i); subscribers(index).add(this); } need_cache_apply.push(this); } apply_cache() { if(!this.unsubscribable) for(let index in this.indexes){ /* dispatch current cache in newly opened page widgets */ let realindex = this.get_variable_index(index); let cached_val = cache[realindex]; if(cached_val != undefined) this._dispatch(cached_val, cached_val, index); } } get_variable_index(varnum) { let index = this.indexes[varnum]; if(typeof(index) == "string"){ index = page_local_index(index, this.container_id); } else { if(this.relativeness[varnum]){ index += this.offset; } } return index; } change_hmi_value(index,opstr) { return change_hmi_value(this.get_variable_index(index), opstr); } apply_hmi_value(index, new_val) { return apply_hmi_value(this.get_variable_index(0), new_val); } new_hmi_value(index, value, oldval) { // TODO avoid searching, store index at sub() for(let i = 0; i < this.indexes.length; i++) { let refindex = this.get_variable_index(i); if(index == refindex) { this._dispatch(value, oldval, i); break; } } } _dispatch(value, oldval, varnum) { let dispatch = this.dispatch; if(dispatch != undefined){ try { dispatch.call(this, value, oldval, varnum); } catch(err) { console.log(err); } } } _animate(){ this.animate(); this.pending_animate = false; } request_animate(){ if(!this.pending_animate){ pending_widget_animates.push(this); this.pending_animate = true; requestHMIAnimation(); } } } || } emit "declarations:hmi-classes" { const "used_widget_types", "func:unique_types($parsed_widgets/widget)"; apply "$used_widget_types", mode="widget_class"; } template "widget", mode="widget_class" || class «@type»Widget extends Widget{ /* empty class, as «@type» widget didn't provide any */ } || const "excluded_types", "str:split('Page Lang VarInit')"; const "included_ids","$parsed_widgets/widget[not(@type = $excluded_types)]/@id"; emit "declarations:hmi-elements" { | var hmi_widgets = { apply "$hmi_elements[@id = $included_ids]", mode="hmi_widgets"; | } } function "defs_by_labels" { param "labels","''"; param "mandatory","'yes'"; param "subelements","/.."; param "hmi_element"; const "widget_type","@type"; foreach "str:split($labels)" { const "name","."; const "elt","$result_svg_ns//*[@id = $hmi_element/@id]//*[@inkscape:label=$name][1]"; choose { when "not($elt/@id)" { if "$mandatory='yes'" { error > «$widget_type» widget must have a «$name» element } // otherwise produce nothing } otherwise { | «$name»_elt: id("«$elt/@id»"), if "$subelements" { | «$name»_sub: { foreach "str:split($subelements)" { const "subname","."; const "subelt","$elt/*[@inkscape:label=$subname][1]"; choose { when "not($subelt/@id)" { if "$mandatory='yes'" { error > «$widget_type» widget must have a «$name»/«$subname» element } | /* missing «$name»/«$subname» element */ } otherwise { | "«$subname»": id("«$subelt/@id»")`if "position()!=last()" > ,` } } } | }, } } } } } def "func:escape_quotes" { param "txt"; // have to use a python string to enter escaped quote // const "frstln", "string-length($frst)"; choose { when !"contains($txt,'\"')"! { result !"concat(substring-before($txt,'\"'),'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!; } otherwise { result "$txt"; } } }