beremiz

SVGHMI: Refactor the way JsonTable generate javascript code to access json data. Now support multiple assignments, used in the case of text to change both content and style on the same element.
// 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";
}
}
}