OPCUA: Add browsing of variables exposed by client when selecting location from variable panel.
// widget_circuralslider.ysl2
widget_desc("CircularSlider") {
longdesc
||
CircularSlider - DEPRECATED, to be replaced by PathSlider
This widget moves "handle" labeled group along "range" labeled
arc, according to value of the single accepted variable.
If "min" a "max" labeled texts are provided, or if first and second
argument are given, then they are used as respective minimum and maximum
value. Otherwise, value is expected to be in between 0 and 100.
If "value" labeled text is found, then its content is replaced by value.
During drag, "setpoint" labeled group is moved to position defined by user
while "handle" reflects current value from variable.
||
shortdesc > CircularSlider - DEPRECATED
arg name="min" count="optional" accepts="int,real" > minimum value
arg name="min" count="optional" accepts="int,real" > maximum value
// TODO: add printf-like format
path name="value" accepts="HMI_INT,HMI_REAL" > Value to display
}
widget_class("CircularSlider")
||
frequency = 5;
range = undefined;
circle = undefined;
handle_pos = undefined;
curr_value = 0;
drag = false;
enTimer = false;
last_drag = false;
dispatch(value) {
let [min,max,start,totallength] = this.range;
//save current value inside widget
this.curr_value = value;
//check if in range
if (this.curr_value > max){
this.curr_value = max;
this.apply_hmi_value(0, this.curr_value);
}
else if (this.curr_value < min){
this.curr_value = min;
this.apply_hmi_value(0, this.curr_value);
}
if(this.value_elt)
this.value_elt.textContent = String(value);
//don't update if draging and setpoint ghost doesn't exist
if(!this.drag || (this.setpoint_elt != undefined)){
this.update_DOM(value, this.handle_elt);
}
}
update_DOM(value, elt){
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);
elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")");
// show or hide ghost if exists
if(this.setpoint_elt != undefined){
if(this.last_drag!= this.drag){
if(this.drag){
this.setpoint_elt.setAttribute("style", this.setpoint_style);
}else{
this.setpoint_elt.setAttribute("style", "display:none");
}
this.last_drag = this.drag;
}
}
}
on_release(evt) {
//unbind events
window.removeEventListener("touchmove", this.on_bound_drag, true);
window.removeEventListener("mousemove", this.on_bound_drag, true);
window.removeEventListener("mouseup", this.bound_on_release, true)
window.removeEventListener("touchend", this.bound_on_release, true);
window.removeEventListener("touchcancel", this.bound_on_release, true);
//reset drag flag
if(this.drag){
this.drag = false;
}
// get final position
this.update_position(evt);
}
on_drag(evt){
//ignore drag event for X amount of time and if not selected
if(this.enTimer && this.drag){
this.update_position(evt);
//reset timer
this.enTimer = false;
setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100);
}
}
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){
this.curr_value=(fi)/(fiEnd)*(this.range[1]-this.range[0]);
}
else if(fiEnd<fi && fi<fiEnd+minMax){
this.curr_value = this.range[1];
}
else{
this.curr_value = this.range[0];
}
//apply value to hmi
this.apply_hmi_value(0, Math.ceil(this.curr_value));
//redraw handle
this.request_animate();
}
}
animate(){
// redraw handle on screen refresh
// check if setpoint(ghost) handle exsist otherwise update main handle
if(this.setpoint_elt != undefined){
this.update_DOM(this.curr_value, this.setpoint_elt);
}
else{
this.update_DOM(this.curr_value, this.handle_elt);
}
}
on_select(evt){
//enable drag flag and timer
this.drag = true;
this.enTimer = true;
//bind events
window.addEventListener("touchmove", this.on_bound_drag, true);
window.addEventListener("mousemove", this.on_bound_drag, true);
window.addEventListener("mouseup", this.bound_on_release, true);
window.addEventListener("touchend", this.bound_on_release, true);
window.addEventListener("touchcancel", this.bound_on_release, true);
//update postion on mouse press
this.update_position(evt);
//prevent next events
evt.stopPropagation();
}
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];
//bind functions
this.bound_on_select = this.on_select.bind(this);
this.bound_on_release = this.on_release.bind(this);
this.on_bound_drag = this.on_drag.bind(this);
this.handle_elt.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("mousedown", this.bound_on_select);
this.element.addEventListener("touchstart", this.bound_on_select);
//touch recognised as page drag without next command
document.body.addEventListener("touchstart", function(e){}, false);
//save ghost style
//save ghost style
if(this.setpoint_elt != undefined){
this.setpoint_style = this.setpoint_elt.getAttribute("style");
this.setpoint_elt.setAttribute("style", "display:none");
}
}
||
widget_defs("CircularSlider") {
labels("handle range");
optional_labels("value min max setpoint");
|,
}