Merge + fix side effects of making warning instead of errors in case of missing HMI variable
--- a/svghmi/gen_index_xhtml.xslt Tue Sep 15 13:57:06 2020 +0200
+++ b/svghmi/gen_index_xhtml.xslt Thu Sep 17 11:30:22 2020 +0200
@@ -915,7 +915,7 @@
<xsl:when test="not(@index)">
<xsl:choose>
<xsl:when test="not(@type)">
- <xsl:message terminate="yes">
+ <xsl:message terminate="no">
<xsl:text>Widget </xsl:text>
<xsl:value-of select="$widget/@type"/>
<xsl:text> id="</xsl:text>
@@ -924,6 +924,10 @@
<xsl:value-of select="@value"/>
<xsl:text>" in HMI tree</xsl:text>
</xsl:message>
+ <xsl:text>undefined</xsl:text>
+ <xsl:if test="position()!=last()">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
</xsl:when>
<xsl:when test="@type = 'PAGE_LOCAL'">
<xsl:text>"</xsl:text>
@@ -1179,6 +1183,8 @@
</xsl:text>
<xsl:text> let index = this.get_variable_index(i);
</xsl:text>
+ <xsl:text> if(index == undefined) continue;
+</xsl:text>
<xsl:text> subscribers(index).add(this);
</xsl:text>
<xsl:text> }
@@ -1197,6 +1203,8 @@
</xsl:text>
<xsl:text> let realindex = this.get_variable_index(index);
</xsl:text>
+ <xsl:text> if(realindex == undefined) continue;
+</xsl:text>
<xsl:text> let cached_val = cache[realindex];
</xsl:text>
<xsl:text> if(cached_val != undefined)
@@ -1231,9 +1239,13 @@
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> change_hmi_value(index,opstr) {
-</xsl:text>
- <xsl:text> return change_hmi_value(this.get_variable_index(index), opstr);
+ <xsl:text> change_hmi_value(index, opstr) {
+</xsl:text>
+ <xsl:text> let realindex = this.get_variable_index(index);
+</xsl:text>
+ <xsl:text> if(realindex == undefined) return undefined;
+</xsl:text>
+ <xsl:text> return change_hmi_value(realindex, opstr);
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -1241,7 +1253,11 @@
</xsl:text>
<xsl:text> apply_hmi_value(index, new_val) {
</xsl:text>
- <xsl:text> return apply_hmi_value(this.get_variable_index(index), new_val);
+ <xsl:text> let realindex = this.get_variable_index(index);
+</xsl:text>
+ <xsl:text> if(realindex == undefined) return undefined;
+</xsl:text>
+ <xsl:text> return apply_hmi_value(realindex, new_val);
</xsl:text>
<xsl:text> }
</xsl:text>
@@ -1255,6 +1271,8 @@
</xsl:text>
<xsl:text> let refindex = this.get_variable_index(i);
</xsl:text>
+ <xsl:text> if(refindex == undefined) continue;
+</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> if(index == refindex) {
@@ -1490,83 +1508,101 @@
</xsl:text>
<xsl:text> state = 0;
</xsl:text>
+ <xsl:text> plc_lock = false;
+</xsl:text>
<xsl:text> active_style = undefined;
</xsl:text>
<xsl:text> inactive_style = undefined;
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> // TODO decouple update of DOM from event (i.e use animate())
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> // TODO State of the button should distinguish UI feedbak from current PLC value
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> on_mouse_down(evt) {
+ <xsl:text> dispatch(value) {
+</xsl:text>
+ <xsl:text> if(value){
+</xsl:text>
+ <xsl:text> this.button_release();
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> on_mouse_down(evt) {
+</xsl:text>
+ <xsl:text> if (this.active_style && this.inactive_style) {
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", this.active_style);
+</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> this.apply_hmi_value(0, 1);
+</xsl:text>
+ <xsl:text> this.plc_lock = false;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> on_mouse_up(evt) {
+</xsl:text>
+ <xsl:text> this.button_release();
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> button_release(){
+</xsl:text>
+ <xsl:text> if(!this.plc_lock){
+</xsl:text>
+ <xsl:text> this.plc_lock = true;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> if (this.active_style && this.inactive_style) {
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> this.apply_hmi_value(0, 0);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> init() {
+</xsl:text>
+ <xsl:text> this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
+</xsl:text>
+ <xsl:text> this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
+</xsl:text>
+ <xsl:text>
</xsl:text>
<xsl:text> if (this.active_style && this.inactive_style) {
</xsl:text>
- <xsl:text> this.active_elt.setAttribute("style", this.active_style);
-</xsl:text>
- <xsl:text> this.inactive_elt.setAttribute("style", "display:none");
+ <xsl:text> this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> this.apply_hmi_value(0, 1);
-</xsl:text>
- <xsl:text> // TODO inhibit all mouse/touch events except mouse up (in other word grab cursor)
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> on_mouse_up(evt) {
-</xsl:text>
- <xsl:text> if (this.active_style && this.inactive_style) {
-</xsl:text>
- <xsl:text> this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
- <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> this.apply_hmi_value(0, 0);
-</xsl:text>
- <xsl:text> // TODO release inhibited events
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> init() {
-</xsl:text>
- <xsl:text> // TODO : move to widget_defs so that we can have generated string literals directly
-</xsl:text>
- <xsl:text> this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
-</xsl:text>
- <xsl:text> this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> if (this.active_style && this.inactive_style) {
-</xsl:text>
- <xsl:text> this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
- <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> this.element.setAttribute("onmousedown", "hmi_widgets[\""+this.element_id+"\"].on_mouse_down(evt)");
-</xsl:text>
- <xsl:text> this.element.setAttribute("onmouseup", "hmi_widgets[\""+this.element_id+"\"].on_mouse_up(evt)");
-</xsl:text>
- <xsl:text> }
+ <xsl:text>
+</xsl:text>
+ <xsl:text> this.element.setAttribute("onmousedown", "hmi_widgets["+this.element_id+"].on_mouse_down(evt)");
+</xsl:text>
+ <xsl:text> this.element.setAttribute("onmouseup", "hmi_widgets["+this.element_id+"].on_mouse_up(evt)");
+</xsl:text>
+ <xsl:text> }
</xsl:text>
<xsl:text>}
</xsl:text>
@@ -4718,47 +4754,163 @@
</xsl:text>
<xsl:text> range = undefined;
</xsl:text>
+ <xsl:text> handle_orig = undefined;
+</xsl:text>
+ <xsl:text> scroll_size = 10;
+</xsl:text>
+ <xsl:text> min_size = 0.07;
+</xsl:text>
<xsl:text> fi = undefined;
</xsl:text>
- <xsl:text> svg_dist = undefined;
+ <xsl:text> curr_value = 0;
</xsl:text>
<xsl:text> drag = false;
</xsl:text>
<xsl:text> enTimer = false;
</xsl:text>
+ <xsl:text> handle_click = undefined;
+</xsl:text>
+ <xsl:text> last_drag = false;
+</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> dispatch(value) {
</xsl:text>
+ <xsl:text> //save current value inside widget
+</xsl:text>
+ <xsl:text> this.curr_value = value;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text> if(this.value_elt)
</xsl:text>
<xsl:text> this.value_elt.textContent = String(value);
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> this.update_DOM(value, this.handle_elt);
-</xsl:text>
- <xsl:text>
+ <xsl:text> //don't update if draging and setpoint ghost doesn't exist
+</xsl:text>
+ <xsl:text> if(!this.drag || (this.setpoint_elt != undefined)){
+</xsl:text>
+ <xsl:text> this.update_DOM(value, this.handle_elt);
+</xsl:text>
+ <xsl:text> }
</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> last_drag = false;
-</xsl:text>
- <xsl:text>
-</xsl:text>
<xsl:text> update_DOM(value, elt){
</xsl:text>
<xsl:text> let [min,max,start,totallength] = this.range;
</xsl:text>
- <xsl:text> let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
-</xsl:text>
- <xsl:text> let tip = this.range_elt.getPointAtLength(length);
-</xsl:text>
- <xsl:text> elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
-</xsl:text>
- <xsl:text>
+ <xsl:text> // check if handle is resizeable
+</xsl:text>
+ <xsl:text> if (this.scroll_size != undefined){ //size changes
+</xsl:text>
+ <xsl:text> //get parameters
+</xsl:text>
+ <xsl:text> let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
+</xsl:text>
+ <xsl:text> let tip = this.range_elt.getPointAtLength(length);
+</xsl:text>
+ <xsl:text> let handle_min = totallength*this.min_size;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> let step = 1;
+</xsl:text>
+ <xsl:text> //check if range is bigger than max displayed and recalculate step
+</xsl:text>
+ <xsl:text> if ((totallength/handle_min) < (max-min+1)){
+</xsl:text>
+ <xsl:text> step = (max-min+1)/(totallength/handle_min-1);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> let kx,ky,offseY,offseX = undefined;
+</xsl:text>
+ <xsl:text> //scale on x or y axes
+</xsl:text>
+ <xsl:text> if (this.fi > 0.75){
+</xsl:text>
+ <xsl:text> //get scale factor
+</xsl:text>
+ <xsl:text> if(step > 1){
+</xsl:text>
+ <xsl:text> ky = handle_min/this.handle_orig.height;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> kx = 1;
+</xsl:text>
+ <xsl:text> //get 0 offset to stay inside range
+</xsl:text>
+ <xsl:text> offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
+</xsl:text>
+ <xsl:text> offseX = 0;
+</xsl:text>
+ <xsl:text> //get distance from value
+</xsl:text>
+ <xsl:text> tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> //get scale factor
+</xsl:text>
+ <xsl:text> if(step > 1){
+</xsl:text>
+ <xsl:text> kx = handle_min/this.handle_orig.width;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> ky = 1;
+</xsl:text>
+ <xsl:text> //get 0 offset to stay inside range
+</xsl:text>
+ <xsl:text> offseX = start.x - (this.handle_orig.x * kx);
+</xsl:text>
+ <xsl:text> offseY = 0;
+</xsl:text>
+ <xsl:text> //get distance from value
+</xsl:text>
+ <xsl:text> tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{ //size stays the same
+</xsl:text>
+ <xsl:text> let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
+</xsl:text>
+ <xsl:text> let tip = this.range_elt.getPointAtLength(length);
+</xsl:text>
+ <xsl:text> elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // show or hide ghost if exists
</xsl:text>
<xsl:text> if(this.setpoint_elt != undefined){
</xsl:text>
@@ -4786,6 +4938,8 @@
</xsl:text>
<xsl:text> on_release(evt) {
</xsl:text>
+ <xsl:text> //unbind events
+</xsl:text>
<xsl:text> window.removeEventListener("touchmove", this.on_bound_drag, true);
</xsl:text>
<xsl:text> window.removeEventListener("mousemove", this.on_bound_drag, true);
@@ -4798,26 +4952,38 @@
</xsl:text>
<xsl:text> window.removeEventListener("touchcancel", this.bound_on_release, true);
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //reset drag flag
+</xsl:text>
<xsl:text> if(this.drag){
</xsl:text>
<xsl:text> this.drag = false;
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // get final position
+</xsl:text>
<xsl:text> this.update_position(evt);
</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text>
-</xsl:text>
<xsl:text> on_drag(evt){
</xsl:text>
+ <xsl:text> //ignore drag event for X amount of time and if not selected
+</xsl:text>
<xsl:text> if(this.enTimer && this.drag){
</xsl:text>
<xsl:text> this.update_position(evt);
</xsl:text>
+ <xsl:text>
+</xsl:text>
<xsl:text> //reset timer
</xsl:text>
<xsl:text> this.enTimer = false;
@@ -4834,16 +5000,18 @@
</xsl:text>
<xsl:text> var html_dist = 0;
</xsl:text>
+ <xsl:text> let [min,max,start,totallength] = this.range;
+</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> //calculate size of widget in html
</xsl:text>
<xsl:text> var range_borders = this.range_elt.getBoundingClientRect();
</xsl:text>
+ <xsl:text> var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
+</xsl:text>
<xsl:text> var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
</xsl:text>
- <xsl:text> var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
-</xsl:text>
<xsl:text>
</xsl:text>
<xsl:text> //get range and mouse coordinates
@@ -4870,112 +5038,240 @@
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> //get handle distance from mouse position
-</xsl:text>
- <xsl:text> if (minX > mouseX && minY < mouseY){
-</xsl:text>
- <xsl:text> html_dist = 0;
+ <xsl:text> // calculate position
+</xsl:text>
+ <xsl:text> if (this.handle_click){ //if clicked on handle
+</xsl:text>
+ <xsl:text> let moveDist = 0, resizeAdd = 0;
+</xsl:text>
+ <xsl:text> let range_percent = 1;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //set paramters for resizeable handle
+</xsl:text>
+ <xsl:text> if (this.scroll_size != undefined){
+</xsl:text>
+ <xsl:text> // add one more object to stay inside range
+</xsl:text>
+ <xsl:text> resizeAdd = 1;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //chack if range is bigger than display option and
+</xsl:text>
+ <xsl:text> // calculate percent of range with out handle
+</xsl:text>
+ <xsl:text> if(((max/(max*this.min_size)) < (max-min+1))){
+</xsl:text>
+ <xsl:text> range_percent = 1-this.min_size;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> range_percent = 1-(max-max*this.min_size*(max-min))/max;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //calculate value difference on x or y axis
+</xsl:text>
+ <xsl:text> if(this.fi > 0.7){
+</xsl:text>
+ <xsl:text> moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text> else if (maxX < mouseX && maxY > mouseY){
-</xsl:text>
- <xsl:text> html_dist = range_length;
+ <xsl:text> else{ //if clicked on widget
+</xsl:text>
+ <xsl:text> //get handle distance from mouse position
+</xsl:text>
+ <xsl:text> if (minX > mouseX && minY < mouseY){
+</xsl:text>
+ <xsl:text> html_dist = 0;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else if (maxX < mouseX && maxY > mouseY){
+</xsl:text>
+ <xsl:text> html_dist = range_length;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> if(this.fi > 0.7){
+</xsl:text>
+ <xsl:text> html_dist = (minY - mouseY)/Math.sin(this.fi);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> html_dist = (mouseX - minX)/Math.cos(this.fi);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> //calculate distance
+</xsl:text>
+ <xsl:text> this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //check if in range
+</xsl:text>
+ <xsl:text> if (this.curr_value > max){
+</xsl:text>
+ <xsl:text> this.curr_value = max;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> else if (this.curr_value < min){
+</xsl:text>
+ <xsl:text> this.curr_value = min;
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> this.apply_hmi_value(0, this.curr_value);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //redraw handle
+</xsl:text>
+ <xsl:text> this.request_animate();
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> animate(){
+</xsl:text>
+ <xsl:text> // redraw handle on screen refresh
+</xsl:text>
+ <xsl:text> // check if setpoint(ghost) handle exsist otherwise update main handle
+</xsl:text>
+ <xsl:text> if(this.setpoint_elt != undefined){
+</xsl:text>
+ <xsl:text> this.update_DOM(this.curr_value, this.setpoint_elt);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
<xsl:text> else{
</xsl:text>
- <xsl:text> // calculate distace
-</xsl:text>
- <xsl:text> if(this.fi > 0.7){
-</xsl:text>
- <xsl:text> html_dist = (minY - mouseY)/Math.sin(this.fi);
+ <xsl:text> this.update_DOM(this.curr_value, this.handle_elt);
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> on_select(evt){
+</xsl:text>
+ <xsl:text> //enable drag flag and timer
+</xsl:text>
+ <xsl:text> this.drag = true;
+</xsl:text>
+ <xsl:text> this.enTimer = true;
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //bind events
+</xsl:text>
+ <xsl:text> window.addEventListener("touchmove", this.on_bound_drag, true);
+</xsl:text>
+ <xsl:text> window.addEventListener("mousemove", this.on_bound_drag, true);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> window.addEventListener("mouseup", this.bound_on_release, true)
+</xsl:text>
+ <xsl:text> window.addEventListener("touchend", this.bound_on_release, true);
+</xsl:text>
+ <xsl:text> window.addEventListener("touchcancel", this.bound_on_release, true);
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> // check if handle was pressed
+</xsl:text>
+ <xsl:text> if (evt.currentTarget == this.handle_elt){
+</xsl:text>
+ <xsl:text> //get mouse position on the handle
+</xsl:text>
+ <xsl:text> let mouseX = undefined;
+</xsl:text>
+ <xsl:text> let mouseY = undefined;
+</xsl:text>
+ <xsl:text> if (evt.type.startsWith("touch")){
+</xsl:text>
+ <xsl:text> mouseX = Math.ceil(evt.touches[0].clientX);
+</xsl:text>
+ <xsl:text> mouseY = Math.ceil(evt.touches[0].clientY);
</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text> else{
</xsl:text>
- <xsl:text> html_dist = (mouseX - minX)/Math.cos(this.fi);
+ <xsl:text> mouseX = evt.pageX;
+</xsl:text>
+ <xsl:text> mouseY = evt.pageY;
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> //check if in range
-</xsl:text>
- <xsl:text> if (html_dist > range_length){
-</xsl:text>
- <xsl:text> html_dist = range_length;
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text> else if (html_dist < 0){
-</xsl:text>
- <xsl:text> html_dist = 0;
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
+ <xsl:text> //save coordinates and orig value
+</xsl:text>
+ <xsl:text> this.handle_click = [mouseX,mouseY,this.curr_value];
</xsl:text>
<xsl:text> }
</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> this.svg_dist=Math.ceil((html_dist/range_length)*this.range[1]);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> this.apply_hmi_value(0, this.svg_dist);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> // update ghost cursor
-</xsl:text>
- <xsl:text> if(this.setpoint_elt != undefined){
-</xsl:text>
- <xsl:text> this.request_animate();
+ <xsl:text> else{
+</xsl:text>
+ <xsl:text> // get new handle position and reset if handle was not pressed
+</xsl:text>
+ <xsl:text> this.handle_click = undefined;
+</xsl:text>
+ <xsl:text> this.update_position(evt);
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //prevent next events
+</xsl:text>
+ <xsl:text> evt.stopPropagation();
+</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> animate(){
-</xsl:text>
- <xsl:text> this.update_DOM(this.svg_dist, this.setpoint_elt);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> on_select(evt){
-</xsl:text>
- <xsl:text> this.drag = true;
-</xsl:text>
- <xsl:text> this.enTimer = true;
-</xsl:text>
- <xsl:text> window.addEventListener("touchmove", this.on_bound_drag, true);
-</xsl:text>
- <xsl:text> window.addEventListener("mousemove", this.on_bound_drag, true);
-</xsl:text>
- <xsl:text>
-</xsl:text>
- <xsl:text> window.addEventListener("mouseup", this.bound_on_release, true)
-</xsl:text>
- <xsl:text> window.addEventListener("touchend", this.bound_on_release, true);
-</xsl:text>
- <xsl:text> window.addEventListener("touchcancel", this.bound_on_release, true);
-</xsl:text>
- <xsl:text> this.update_position(evt);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
<xsl:text> init() {
</xsl:text>
+ <xsl:text> //set min max value if not defined
+</xsl:text>
<xsl:text> let min = this.min_elt ?
</xsl:text>
<xsl:text> Number(this.min_elt.textContent) :
@@ -4990,6 +5286,10 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text> // save initial parameters
+</xsl:text>
+ <xsl:text> this.range_elt.style.strokeMiterlimit="0";
+</xsl:text>
<xsl:text> this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
</xsl:text>
<xsl:text> let start = this.range_elt.getPointAtLength(0);
@@ -4998,7 +5298,11 @@
</xsl:text>
<xsl:text> this.fi = Math.atan2(start.y-end.y, end.x-start.x);
</xsl:text>
- <xsl:text>
+ <xsl:text> this.handle_orig = this.handle_elt.getBBox();
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> //bind functions
</xsl:text>
<xsl:text> this.bound_on_select = this.on_select.bind(this);
</xsl:text>
@@ -5008,6 +5312,8 @@
</xsl:text>
<xsl:text>
</xsl:text>
+ <xsl:text> this.handle_elt.addEventListener("mousedown", this.bound_on_select);
+</xsl:text>
<xsl:text> this.element.addEventListener("mousedown", this.bound_on_select);
</xsl:text>
<xsl:text> this.element.addEventListener("touchstart", this.bound_on_select);
@@ -5119,38 +5425,56 @@
</xsl:text>
<xsl:text> dispatch(value) {
</xsl:text>
- <xsl:text> this.state = value;
+ <xsl:text> if(this.state != value){
+</xsl:text>
+ <xsl:text> this.state = value;
+</xsl:text>
+ <xsl:text> if (this.state) {
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", this.active_style);
+</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> } else {
+</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text> }
+</xsl:text>
+ <xsl:text>
+</xsl:text>
+ <xsl:text> on_click(evt) {
</xsl:text>
<xsl:text> if (this.state) {
</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
+ <xsl:text> this.state = 0;
+</xsl:text>
+ <xsl:text> } else {
+</xsl:text>
<xsl:text> this.active_elt.setAttribute("style", this.active_style);
</xsl:text>
<xsl:text> this.inactive_elt.setAttribute("style", "display:none");
</xsl:text>
- <xsl:text> this.state = 0;
-</xsl:text>
- <xsl:text> } else {
-</xsl:text>
- <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
-</xsl:text>
- <xsl:text> this.active_elt.setAttribute("style", "display:none");
-</xsl:text>
<xsl:text> this.state = 1;
</xsl:text>
<xsl:text> }
</xsl:text>
+ <xsl:text> this.apply_hmi_value(0, this.state);
+</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text>
</xsl:text>
- <xsl:text> on_click(evt) {
-</xsl:text>
- <xsl:text> this.apply_hmi_value(0, this.state);
-</xsl:text>
- <xsl:text> }
-</xsl:text>
- <xsl:text>
-</xsl:text>
<xsl:text> init() {
</xsl:text>
<xsl:text> this.active_style = this.active_elt.style.cssText;
@@ -5159,6 +5483,10 @@
</xsl:text>
<xsl:text> this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
</xsl:text>
+ <xsl:text> this.inactive_elt.setAttribute("style", this.inactive_style);
+</xsl:text>
+ <xsl:text> this.active_elt.setAttribute("style", "display:none");
+</xsl:text>
<xsl:text> }
</xsl:text>
<xsl:text>}
--- a/svghmi/widget_button.ysl2 Tue Sep 15 13:57:06 2020 +0200
+++ b/svghmi/widget_button.ysl2 Thu Sep 17 11:30:22 2020 +0200
@@ -5,45 +5,54 @@
class ButtonWidget extends Widget{
frequency = 5;
state = 0;
+ plc_lock = false;
active_style = undefined;
inactive_style = undefined;
- // TODO decouple update of DOM from event (i.e use animate())
-
-
- // TODO State of the button should distinguish UI feedbak from current PLC value
-
- on_mouse_down(evt) {
- if (this.active_style && this.inactive_style) {
- this.active_elt.setAttribute("style", this.active_style);
- this.inactive_elt.setAttribute("style", "display:none");
+ dispatch(value) {
+ if(value){
+ this.button_release();
}
- this.apply_hmi_value(0, 1);
- // TODO inhibit all mouse/touch events except mouse up (in other word grab cursor)
}
- on_mouse_up(evt) {
+ on_mouse_down(evt) {
+ if (this.active_style && this.inactive_style) {
+ this.active_elt.setAttribute("style", this.active_style);
+ this.inactive_elt.setAttribute("style", "display:none");
+ }
+ this.apply_hmi_value(0, 1);
+ this.plc_lock = false;
+ }
+
+ on_mouse_up(evt) {
+ this.button_release();
+ }
+
+ button_release(){
+ if(!this.plc_lock){
+ this.plc_lock = true;
+ }
+ else{
+ if (this.active_style && this.inactive_style) {
+ this.active_elt.setAttribute("style", "display:none");
+ this.inactive_elt.setAttribute("style", this.inactive_style);
+ }
+ this.apply_hmi_value(0, 0);
+ }
+ }
+
+ init() {
+ this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
+ this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
+
if (this.active_style && this.inactive_style) {
this.active_elt.setAttribute("style", "display:none");
this.inactive_elt.setAttribute("style", this.inactive_style);
}
- this.apply_hmi_value(0, 0);
- // TODO release inhibited events
- }
- init() {
- // TODO : move to widget_defs so that we can have generated string literals directly
- this.active_style = this.active_elt ? this.active_elt.style.cssText : undefined;
- this.inactive_style = this.inactive_elt ? this.inactive_elt.style.cssText : undefined;
-
- if (this.active_style && this.inactive_style) {
- this.active_elt.setAttribute("style", "display:none");
- this.inactive_elt.setAttribute("style", this.inactive_style);
- }
-
- this.element.setAttribute("onmousedown", "hmi_widgets[\""+this.element_id+"\"].on_mouse_down(evt)");
- this.element.setAttribute("onmouseup", "hmi_widgets[\""+this.element_id+"\"].on_mouse_up(evt)");
- }
+ this.element.setAttribute("onmousedown", "hmi_widgets["+this.element_id+"].on_mouse_down(evt)");
+ this.element.setAttribute("onmouseup", "hmi_widgets["+this.element_id+"].on_mouse_up(evt)");
+ }
}
||
}
--- a/svghmi/widget_slider.ysl2 Tue Sep 15 13:57:06 2020 +0200
+++ b/svghmi/widget_slider.ysl2 Thu Sep 17 11:30:22 2020 +0200
@@ -5,27 +5,85 @@
class SliderWidget extends Widget{
frequency = 5;
range = undefined;
+ handle_orig = undefined;
+ scroll_size = 10;
+ min_size = 0.07;
fi = undefined;
- svg_dist = undefined;
+ curr_value = 0;
drag = false;
enTimer = false;
+ handle_click = undefined;
+ last_drag = false;
dispatch(value) {
+ //save current value inside widget
+ this.curr_value = value;
+
if(this.value_elt)
this.value_elt.textContent = String(value);
- this.update_DOM(value, this.handle_elt);
-
- }
-
- last_drag = false;
+ //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,start,totallength] = this.range;
- let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
- let tip = this.range_elt.getPointAtLength(length);
- elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
-
+ // check if handle is resizeable
+ if (this.scroll_size != undefined){ //size changes
+ //get parameters
+ let length = Math.max(min,Math.min(max,(Number(value)-min)*max/(max-min)));
+ let tip = this.range_elt.getPointAtLength(length);
+ let handle_min = totallength*this.min_size;
+
+ let step = 1;
+ //check if range is bigger than max displayed and recalculate step
+ if ((totallength/handle_min) < (max-min+1)){
+ step = (max-min+1)/(totallength/handle_min-1);
+ }
+
+ let kx,ky,offseY,offseX = undefined;
+ //scale on x or y axes
+ if (this.fi > 0.75){
+ //get scale factor
+ if(step > 1){
+ ky = handle_min/this.handle_orig.height;
+ }
+ else{
+ ky = (totallength-handle_min*(max-min))/this.handle_orig.height;
+ }
+ kx = 1;
+ //get 0 offset to stay inside range
+ offseY = start.y - (this.handle_orig.height + this.handle_orig.y) * ky;
+ offseX = 0;
+ //get distance from value
+ tip.y =this.range_elt.getPointAtLength(0).y - length/step *handle_min;
+ }
+ else{
+ //get scale factor
+ if(step > 1){
+ kx = handle_min/this.handle_orig.width;
+ }
+ else{
+ kx = (totallength-handle_min*(max-min))/this.handle_orig.width;
+ }
+ ky = 1;
+ //get 0 offset to stay inside range
+ offseX = start.x - (this.handle_orig.x * kx);
+ offseY = 0;
+ //get distance from value
+ tip.x =this.range_elt.getPointAtLength(0).x + length/step *handle_min;
+ }
+ elt.setAttribute('transform',"matrix("+(kx)+" 0 0 "+(ky)+" "+(tip.x-start.x+offseX)+" "+(tip.y-start.y+offseY)+")");
+ }
+ else{ //size stays the same
+ let length = Math.max(0,Math.min(totallength,(Number(value)-min)*totallength/(max-min)));
+ let tip = this.range_elt.getPointAtLength(length);
+ elt.setAttribute('transform',"translate("+(tip.x-start.x)+","+(tip.y-start.y)+")");
+ }
+
+ // show or hide ghost if exists
if(this.setpoint_elt != undefined){
if(this.last_drag!= this.drag){
if(this.drag){
@@ -39,22 +97,29 @@
}
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);
@@ -63,11 +128,12 @@
update_position(evt){
var html_dist = 0;
+ let [min,max,start,totallength] = this.range;
//calculate size of widget in html
var range_borders = this.range_elt.getBoundingClientRect();
+ var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
var range_length = Math.sqrt( range_borders.height*range_borders.height + range_borders.width*range_borders.width );
- var [minX,minY,maxX,maxY] = [range_borders.left,range_borders.bottom,range_borders.right,range_borders.top];
//get range and mouse coordinates
var mouseX = undefined;
@@ -81,59 +147,123 @@
mouseY = evt.pageY;
}
- //get handle distance from mouse position
- if (minX > mouseX && minY < mouseY){
- html_dist = 0;
- }
- else if (maxX < mouseX && maxY > mouseY){
- html_dist = range_length;
+ // calculate position
+ if (this.handle_click){ //if clicked on handle
+ let moveDist = 0, resizeAdd = 0;
+ let range_percent = 1;
+
+ //set paramters for resizeable handle
+ if (this.scroll_size != undefined){
+ // add one more object to stay inside range
+ resizeAdd = 1;
+
+ //chack if range is bigger than display option and
+ // calculate percent of range with out handle
+ if(((max/(max*this.min_size)) < (max-min+1))){
+ range_percent = 1-this.min_size;
+ }
+ else{
+ range_percent = 1-(max-max*this.min_size*(max-min))/max;
+ }
+ }
+
+ //calculate value difference on x or y axis
+ if(this.fi > 0.7){
+ moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((this.handle_click[1]-mouseY)/Math.sin(this.fi));
+ }
+ else{
+ moveDist = ((max-min+resizeAdd)/(range_length*range_percent))*((mouseX-this.handle_click[0])/Math.cos(this.fi));
+ }
+
+ this.curr_value = Math.ceil(this.handle_click[2] + moveDist);
+ }
+ else{ //if clicked on widget
+ //get handle distance from mouse position
+ if (minX > mouseX && minY < mouseY){
+ html_dist = 0;
+ }
+ else if (maxX < mouseX && maxY > mouseY){
+ html_dist = range_length;
+ }
+ else{
+ if(this.fi > 0.7){
+ html_dist = (minY - mouseY)/Math.sin(this.fi);
+ }
+ else{
+ html_dist = (mouseX - minX)/Math.cos(this.fi);
+ }
+ }
+ //calculate distance
+ this.curr_value=Math.ceil((html_dist/range_length)*(this.range[1]-this.range[0])+this.range[0]);
+ }
+
+ //check if in range
+ if (this.curr_value > max){
+ this.curr_value = max;
+ }
+ else if (this.curr_value < min){
+ this.curr_value = min;
+ }
+
+ this.apply_hmi_value(0, 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{
- // calculate distace
- if(this.fi > 0.7){
- html_dist = (minY - mouseY)/Math.sin(this.fi);
- }
- else{
- html_dist = (mouseX - minX)/Math.cos(this.fi);
- }
-
- //check if in range
- if (html_dist > range_length){
- html_dist = range_length;
- }
- else if (html_dist < 0){
- html_dist = 0;
- }
-
- }
-
- this.svg_dist=Math.ceil((html_dist/range_length)*this.range[1]);
-
- this.apply_hmi_value(0, this.svg_dist);
-
- // update ghost cursor
- if(this.setpoint_elt != undefined){
- this.request_animate();
- }
- }
-
- animate(){
- this.update_DOM(this.svg_dist, this.setpoint_elt);
+ 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);
- this.update_position(evt);
+
+ // check if handle was pressed
+ if (evt.currentTarget == this.handle_elt){
+ //get mouse position on the handle
+ 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;
+ }
+ //save coordinates and orig value
+ this.handle_click = [mouseX,mouseY,this.curr_value];
+ }
+ else{
+ // get new handle position and reset if handle was not pressed
+ this.handle_click = undefined;
+ this.update_position(evt);
+ }
+
+ //prevent next events
+ evt.stopPropagation();
}
init() {
+ //set min max value if not defined
let min = this.min_elt ?
Number(this.min_elt.textContent) :
this.args.length >= 1 ? this.args[0] : 0;
@@ -141,15 +271,20 @@
Number(this.max_elt.textContent) :
this.args.length >= 2 ? this.args[1] : 100;
+ // save initial parameters
+ this.range_elt.style.strokeMiterlimit="0";
this.range = [min, max, this.range_elt.getPointAtLength(0),this.range_elt.getTotalLength()];
let start = this.range_elt.getPointAtLength(0);
let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength());
this.fi = Math.atan2(start.y-end.y, end.x-start.x);
-
+ this.handle_orig = this.handle_elt.getBBox();
+
+ //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);
--- a/svghmi/widget_tooglebutton.ysl2 Tue Sep 15 13:57:06 2020 +0200
+++ b/svghmi/widget_tooglebutton.ysl2 Thu Sep 17 11:30:22 2020 +0200
@@ -10,19 +10,28 @@
inactive_style = undefined;
dispatch(value) {
- this.state = value;
- if (this.state) {
- this.active_elt.setAttribute("style", this.active_style);
- this.inactive_elt.setAttribute("style", "display:none");
- this.state = 0;
- } else {
- this.inactive_elt.setAttribute("style", this.inactive_style);
- this.active_elt.setAttribute("style", "display:none");
- this.state = 1;
+ if(this.state != value){
+ this.state = value;
+ if (this.state) {
+ this.active_elt.setAttribute("style", this.active_style);
+ this.inactive_elt.setAttribute("style", "display:none");
+ } else {
+ this.inactive_elt.setAttribute("style", this.inactive_style);
+ this.active_elt.setAttribute("style", "display:none");
+ }
}
}
on_click(evt) {
+ if (this.state) {
+ this.inactive_elt.setAttribute("style", this.inactive_style);
+ this.active_elt.setAttribute("style", "display:none");
+ this.state = 0;
+ } else {
+ this.active_elt.setAttribute("style", this.active_style);
+ this.inactive_elt.setAttribute("style", "display:none");
+ this.state = 1;
+ }
this.apply_hmi_value(0, this.state);
}
@@ -30,6 +39,8 @@
this.active_style = this.active_elt.style.cssText;
this.inactive_style = this.inactive_elt.style.cssText;
this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)");
+ this.inactive_elt.setAttribute("style", this.inactive_style);
+ this.active_elt.setAttribute("style", "display:none");
}
}
||
--- a/svghmi/widgets_common.ysl2 Tue Sep 15 13:57:06 2020 +0200
+++ b/svghmi/widgets_common.ysl2 Thu Sep 17 11:30:22 2020 +0200
@@ -29,8 +29,10 @@
choose {
when "not(@index)" {
choose {
- when "not(@type)"
- error > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
+ when "not(@type)" {
+ warning > Widget «$widget/@type» id="«$eltid»" : No match for path "«@value»" in HMI tree
+ > undefined`if "position()!=last()" > ,`
+ }
when "@type = 'PAGE_LOCAL'"
> "«@value»"`if "position()!=last()" > ,`
when "@type = 'HMI_LOCAL'"
@@ -152,6 +154,7 @@
if(!this.unsubscribable)
for(let i = 0; i < this.indexes.length; i++) {
let index = this.get_variable_index(i);
+ if(index == undefined) continue;
subscribers(index).add(this);
}
need_cache_apply.push(this);
@@ -161,6 +164,7 @@
if(!this.unsubscribable) for(let index in this.indexes){
/* dispatch current cache in newly opened page widgets */
let realindex = this.get_variable_index(index);
+ if(realindex == undefined) continue;
let cached_val = cache[realindex];
if(cached_val != undefined)
this._dispatch(cached_val, cached_val, index);
@@ -178,18 +182,23 @@
}
return index;
}
- change_hmi_value(index,opstr) {
- return change_hmi_value(this.get_variable_index(index), opstr);
+ change_hmi_value(index, opstr) {
+ let realindex = this.get_variable_index(index);
+ if(realindex == undefined) return undefined;
+ return change_hmi_value(realindex, opstr);
}
apply_hmi_value(index, new_val) {
- return apply_hmi_value(this.get_variable_index(index), new_val);
+ let realindex = this.get_variable_index(index);
+ if(realindex == undefined) return undefined;
+ return apply_hmi_value(realindex, 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(refindex == undefined) continue;
if(index == refindex) {
this._dispatch(value, oldval, i);
--- a/tests/svghmi_v2/plc.xml Tue Sep 15 13:57:06 2020 +0200
+++ b/tests/svghmi_v2/plc.xml Thu Sep 17 11:30:22 2020 +0200
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
<fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2019-08-06T14:23:42"/>
- <contentHeader name="Unnamed" modificationDateTime="2020-07-30T12:04:22">
+ <contentHeader name="Unnamed" modificationDateTime="2020-09-15T14:59:06">
<coordinateInfo>
<fbd>
<scaling x="5" y="5"/>
@@ -55,6 +55,21 @@
<derived name="HMI_INT"/>
</type>
</variable>
+ <variable name="Toggle">
+ <type>
+ <derived name="HMI_BOOL"/>
+ </type>
+ </variable>
+ <variable name="Toggle1">
+ <type>
+ <derived name="HMI_BOOL"/>
+ </type>
+ </variable>
+ <variable name="Toggle2">
+ <type>
+ <derived name="HMI_BOOL"/>
+ </type>
+ </variable>
<variable name="MultistateExt">
<type>
<INT/>
@@ -88,7 +103,7 @@
<expression>TargetPressure</expression>
</inVariable>
<inVariable localId="6" executionOrderId="0" height="25" width="90" negated="false">
- <position x="155" y="220"/>
+ <position x="130" y="60"/>
<connectionPointOut>
<relPosition x="90" y="10"/>
</connectionPointOut>
@@ -100,7 +115,9 @@
<relPosition x="0" y="10"/>
<connection refLocalId="6">
<position x="495" y="230"/>
- <position x="245" y="230"/>
+ <position x="367" y="230"/>
+ <position x="367" y="70"/>
+ <position x="220" y="70"/>
</connection>
</connectionPointIn>
<expression>TestLocal</expression>
@@ -190,34 +207,12 @@
</connectionPointOut>
<expression>TargetPressure</expression>
</inVariable>
- <inOutVariable localId="4" executionOrderId="0" height="30" width="60" negatedOut="false" negatedIn="false">
- <position x="510" y="80"/>
- <connectionPointIn>
- <relPosition x="0" y="15"/>
- <connection refLocalId="6" formalParameter="OUT">
- <position x="510" y="95"/>
- <position x="470" y="95"/>
- </connection>
- </connectionPointIn>
- <connectionPointOut>
- <relPosition x="60" y="15"/>
- </connectionPointOut>
- <expression>Sloth</expression>
- </inOutVariable>
<block localId="6" typeName="ADD" executionOrderId="0" height="60" width="65">
<position x="405" y="65"/>
<inputVariables>
<variable formalParameter="IN1">
<connectionPointIn>
<relPosition x="0" y="30"/>
- <connection refLocalId="4">
- <position x="405" y="95"/>
- <position x="385" y="95"/>
- <position x="385" y="50"/>
- <position x="580" y="50"/>
- <position x="580" y="95"/>
- <position x="570" y="95"/>
- </connection>
</connectionPointIn>
</variable>
<variable formalParameter="IN2">
@@ -559,6 +554,13 @@
</connectionPointOut>
<expression>0</expression>
</inVariable>
+ <inVariable localId="4" executionOrderId="0" height="30" width="60" negated="false">
+ <position x="510" y="80"/>
+ <connectionPointOut>
+ <relPosition x="60" y="15"/>
+ </connectionPointOut>
+ <expression>Sloth</expression>
+ </inVariable>
</FBD>
</body>
</pou>
--- a/tests/svghmi_v2/svghmi_0@svghmi/svghmi.svg Tue Sep 15 13:57:06 2020 +0200
+++ b/tests/svghmi_v2/svghmi_0@svghmi/svghmi.svg Thu Sep 17 11:30:22 2020 +0200
@@ -16,7 +16,7 @@
version="1.1"
id="hmi0"
sodipodi:docname="svghmi.svg"
- inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ inkscape:version="0.92.5 (0.92.5+68)"
inkscape:label="Layer">
<metadata
id="metadata4542">
@@ -53,17 +53,6 @@
inkscape:vp_z="1272 : 385 : 1"
inkscape:persp3d-origin="536 : 237 : 1"
id="perspective445" />
- <inkscape:tag
- id="Set 1"
- inkscape:label="HMI:AccessList@Admin"
- inkscape:expanded="true">
- <inkscape:tagref
- xlink:href="#text995"
- id="tagref192" />
- <inkscape:tagref
- xlink:href="#g991"
- id="tagref194" />
- </inkscape:tag>
<linearGradient
inkscape:collect="always"
id="linearGradient962">
@@ -91,26 +80,6 @@
id="path924"
inkscape:connector-curvature="0" />
</marker>
- <inkscape:tag
- id="Set 3"
- inkscape:expanded="true"
- inkscape:label="HMI:Translate">
- <inkscape:tagref
- xlink:href="#text831"
- id="tagref1085" />
- <inkscape:tagref
- xlink:href="#text827"
- id="tagref1087" />
- <inkscape:tagref
- xlink:href="#text4497"
- id="tagref1089" />
- <inkscape:tagref
- xlink:href="#home_jmp"
- id="tagref1091" />
- <inkscape:tagref
- xlink:href="#setting_jmp"
- id="tagref1093" />
- </inkscape:tag>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient962"
@@ -137,16 +106,16 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:document-units="px"
- inkscape:current-layer="g110-0-9"
+ inkscape:current-layer="hmi0"
showgrid="false"
units="px"
inkscape:zoom="1.4142136"
- inkscape:cx="437.24009"
- inkscape:cy="177.36896"
- inkscape:window-width="1800"
- inkscape:window-height="836"
- inkscape:window-x="0"
- inkscape:window-y="27"
+ inkscape:cx="462.89448"
+ inkscape:cy="318.79031"
+ inkscape:window-width="2503"
+ inkscape:window-height="1416"
+ inkscape:window-x="57"
+ inkscape:window-y="24"
inkscape:window-maximized="1"
showguides="true"
inkscape:guide-bbox="true" />
@@ -200,7 +169,7 @@
y="-64.195457"
x="113.27539"
sodipodi:role="line"
- id="tspan1409">100</tspan></text>
+ id="tspan1409">10</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:10.58333302px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -1221,7 +1190,7 @@
y="5.501111"
x="159.67337"
sodipodi:role="line"
- id="tspan1409-1">100</tspan></text>
+ id="tspan1409-1">1000</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:7.78479624px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.19461991px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -1522,4 +1491,66 @@
y="258.16129"
style="fill:#ff00ca;fill-opacity:1;stroke:none;stroke-width:2.25346255px;stroke-opacity:1">000</tspan></text>
</g>
+ <g
+ id="g4791"
+ inkscape:label="HMI:ToggleButton@/TOGGLE2">
+ <rect
+ inkscape:label="inactive"
+ y="46.127251"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772"
+ style="opacity:1;fill:#ff0015;fill-opacity:1;stroke:none" />
+ <rect
+ inkscape:label="active"
+ y="46.127251"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772-3"
+ style="opacity:1;fill:#00ff03;fill-opacity:1;stroke:none" />
+ </g>
+ <g
+ transform="translate(-67.175138,-1.0606552)"
+ id="g4791-6"
+ inkscape:label="HMI:ToggleButton@/TOGGLE1">
+ <rect
+ inkscape:label="inactive"
+ y="47.187904"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772-5"
+ style="opacity:1;fill:#ff0015;fill-opacity:1;stroke:none" />
+ <rect
+ inkscape:label="active"
+ y="47.187904"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772-3-7"
+ style="opacity:1;fill:#00ff03;fill-opacity:1;stroke:none" />
+ </g>
+ <g
+ transform="translate(63.639613)"
+ id="g4791-3"
+ inkscape:label="HMI:ToggleButton@/TOGGLE">
+ <rect
+ inkscape:label="active"
+ y="46.127251"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772-3-5"
+ style="opacity:1;fill:#00ff03;fill-opacity:1;stroke:none" />
+ <rect
+ inkscape:label="inactive"
+ y="46.127251"
+ x="906.51086"
+ height="44.547726"
+ width="45.254833"
+ id="rect4772-6"
+ style="opacity:1;fill:#ff0015;fill-opacity:1;stroke:none" />
+ </g>
</svg>