beremiz

SVGHMI: JsonTable now use intermediate variables again to address JSON data without duplicating code or referencing. Using intermediate variables also alows to check for availability of data and stop evaluating early if data is missing. Finally added complete roundtrip example to illustrate use of JSonTable to display "alarms" collected in python from changes on PLC boolean variables.
// widget_circuralslider.ysl2
template "widget[@type='CircularSlider']", mode="widget_class"
||
class CircularSliderWidget extends Widget{
frequency = 5;
range = undefined;
circle = undefined;
handle_pos = undefined;
drag = false;
enTimer = false;
dispatch(value) {
if(!this.drag){
if(this.value_elt)
this.value_elt.textContent = String(value);
this.handle_position(value);
}
}
handle_position(value){
let [min,max,totalDistance] = this.range;
let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance)));
let tip = this.range_elt.getPointAtLength(length);
this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
}
on_release(evt) {
if(this.drag){
this.drag = false;
}
}
update_position(evt){
if(this.drag && this.enTimer){
var svg_dist = 0;
//calculate center of widget in html
// --TODO maybe it would be better to bind this part to window change size event ???
let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox;
let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle;
let htmlCirc = this.range_elt.getBoundingClientRect();
let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left;
let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top;
//get mouse coordinates
let mouseX = undefined;
let mouseY = undefined;
if (evt.type.startsWith("touch")){
mouseX = Math.ceil(evt.touches[0].clientX);
mouseY = Math.ceil(evt.touches[0].clientY);
}
else{
mouseX = evt.pageX;
mouseY = evt.pageY;
}
//calculate angle
let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml);
// transform from 0 to 2PI
if (fi > 0){
fi = 2*Math.PI-fi;
}
else{
fi = -fi;
}
//offset it to 0
fi = fi - fiStart;
if (fi < 0){
fi = fi + 2*Math.PI;
}
//get handle distance from mouse position
if(fi<fiEnd){
svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
}
else if(fiEnd<fi && fi<fiEnd+minMax){
svg_dist = this.range[1];
}
else{
svg_dist = this.range[0];
}
//redraw handle --TODO is it fast enough if I just call change_hmi_value???
this.handle_position(svg_dist);
if(this.value_elt)
this.value_elt.textContent = String(Math.ceil(svg_dist));
this.apply_hmi_value(0, Math.ceil(svg_dist));
//reset timer
this.enTimer = false;
setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
}
}
on_select(evt){
this.drag = true;
this.enTimer = true;
this.update_position(evt);
}
init() {
//get min max
let min = this.min_elt ?
Number(this.min_elt.textContent) :
this.args.length >= 1 ? this.args[0] : 0;
let max = this.max_elt ?
Number(this.max_elt.textContent) :
this.args.length >= 2 ? this.args[1] : 100;
//fiStart ==> offset
let fiStart = Number(this.range_elt.getAttribute('sodipodi:start'));
let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end'));
fiEnd = fiEnd - fiStart;
//fiEnd ==> size of angle
if (fiEnd < 0){
fiEnd = 2*Math.PI + fiEnd;
}
//min max barrier angle
let minMax = (2*Math.PI - fiEnd)/2;
//get parameters from svg
let cX = Number(this.range_elt.getAttribute('sodipodi:cx'));
let cY = Number(this.range_elt.getAttribute('sodipodi:cy'));
this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object
this.range = [min, max,this.range_elt.getTotalLength()];
let cPos = this.range_elt.getBBox();
this.handle_pos = this.range_elt.getPointAtLength(0);
this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height];
//init events
this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this));
this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this));
window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this));
window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this));
window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this))
window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this));
window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this));
}
}
||
template "widget[@type='CircularSlider']", mode="widget_defs" {
param "hmi_element";
labels("handle range");
optional_labels("value min max");
|,
}