# HG changeset patch # User Edouard Tisserant # Date 1600335022 -7200 # Node ID 6ea4b7e1a9ed5c25dfd070227118507b42cf5852 # Parent 6dd617cc9c050afc57ea631d23d071e965609a2d# Parent 696301e869d56c1acb009346c93a6b88deb28f4f Merge + fix side effects of making warning instead of errors in case of missing HMI variable diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed svghmi/gen_index_xhtml.xslt --- 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>} diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed svghmi/widget_button.ysl2 --- 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)"); + } } || } diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed svghmi/widget_slider.ysl2 --- 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); diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed svghmi/widget_tooglebutton.ysl2 --- 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"); } } || diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed svghmi/widgets_common.ysl2 --- 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); diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed tests/svghmi_v2/plc.xml --- 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> diff -r 6dd617cc9c05 -r 6ea4b7e1a9ed tests/svghmi_v2/svghmi_0@svghmi/svghmi.svg --- 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>