# HG changeset patch # User Edouard Tisserant # Date 1597238642 -7200 # Node ID 0a9f6f29b7ddaaef4a0bee5601ac8c601b46f262 # Parent 407a0205405a64c144550be67da941498cac1ebc# Parent 49799de67540ba934ab0c4cb7ef9421fe0db05ab Merge diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/gen_index_xhtml.xslt Wed Aug 12 15:24:02 2020 +0200 @@ -850,7 +850,7 @@ <xsl:variable name="args"> <xsl:for-each select="$widget/arg"> <xsl:text>"</xsl:text> - <xsl:value-of select="@value"/> + <xsl:value-of select="func:escape_quotes(@value)"/> <xsl:text>"</xsl:text> <xsl:if test="position()!=last()"> <xsl:text>,</xsl:text> @@ -959,6 +959,34 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text>const local_defaults = { +</xsl:text> + <xsl:for-each select="$parsed_widgets/widget[@type = 'VarInit']"> + <xsl:if test="count(path) != 1"> + <xsl:message terminate="yes"> + <xsl:text>VarInit </xsl:text> + <xsl:value-of select="@id"/> + <xsl:text> must have only one variable given.</xsl:text> + </xsl:message> + </xsl:if> + <xsl:if test="path/@type != 'PAGE_LOCAL' and path/@type != 'HMI_LOCAL'"> + <xsl:message terminate="yes"> + <xsl:text>VarInit </xsl:text> + <xsl:value-of select="@id"/> + <xsl:text> only applies to HMI variable.</xsl:text> + </xsl:message> + </xsl:if> + <xsl:value-of select="arg[0]"/> + <xsl:text>:</xsl:text> + <xsl:value-of select="path/@value"/> + <xsl:if test="position()!=last()"> + <xsl:text>,</xsl:text> + </xsl:if> + <xsl:text> +</xsl:text> + </xsl:for-each> + <xsl:text>}; +</xsl:text> <xsl:text>var cache = hmitree_types.map(_ignored => undefined); </xsl:text> <xsl:text> @@ -975,20 +1003,12 @@ </xsl:text> <xsl:text> hmi_locals[pagename] = {[varname]:new_index} </xsl:text> - <xsl:text> console.log("pagelocalindex insert",varname, pagename, new_index); -</xsl:text> <xsl:text> } else { </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> console.log("pagevars",pagevars); -</xsl:text> <xsl:text> let result = pagevars[varname]; </xsl:text> <xsl:text> if(result != undefined) { </xsl:text> - <xsl:text> console.log("pagelocalindex reuse",varname, pagename, result); -</xsl:text> <xsl:text> return result; </xsl:text> <xsl:text> } @@ -999,12 +1019,8 @@ </xsl:text> <xsl:text> pagevars[varname] = new_index; </xsl:text> - <xsl:text> console.log("pagelocalindex addwidget",varname, pagename, new_index); -</xsl:text> <xsl:text> } </xsl:text> - <xsl:text> cache[new_index] = ""; -</xsl:text> <xsl:text> return new_index; </xsl:text> <xsl:text>} @@ -1030,6 +1046,10 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text>var pending_widget_animates = []; +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text>class Widget { </xsl:text> <xsl:text> offset = 0; @@ -1038,6 +1058,10 @@ </xsl:text> <xsl:text> unsubscribable = false; </xsl:text> + <xsl:text> pending_animate = false; +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text> constructor(elt_id,args,indexes,members){ </xsl:text> <xsl:text> this.element_id = elt_id; @@ -1108,11 +1132,11 @@ </xsl:text> <xsl:text> apply_cache() { </xsl:text> - <xsl:text> if(!this.unsubscribable) for(let i = 0; i < this.indexes.length; i++) { + <xsl:text> if(!this.unsubscribable) for(let index of this.indexes){ </xsl:text> <xsl:text> /* dispatch current cache in newly opened page widgets */ </xsl:text> - <xsl:text> let realindex = this.get_variable_index(i); + <xsl:text> let realindex = this.get_variable_index(index); </xsl:text> <xsl:text> let cached_val = cache[realindex]; </xsl:text> @@ -1166,7 +1190,7 @@ </xsl:text> <xsl:text> new_hmi_value(index, value, oldval) { </xsl:text> - <xsl:text> /* try {*/ + <xsl:text> try { </xsl:text> <xsl:text> // TODO avoid searching, store index at sub() </xsl:text> @@ -1206,11 +1230,39 @@ </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> /* } catch(err) { + <xsl:text> } catch(err) { </xsl:text> <xsl:text> console.log(err); </xsl:text> - <xsl:text> }*/ + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> _animate(){ +</xsl:text> + <xsl:text> this.animate(); +</xsl:text> + <xsl:text> this.pending_animate = false; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> request_animate(){ +</xsl:text> + <xsl:text> if(!this.pending_animate){ +</xsl:text> + <xsl:text> pending_widget_animates.push(this); +</xsl:text> + <xsl:text> this.pending_animate = true; +</xsl:text> + <xsl:text> requestHMIAnimation(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> </xsl:text> <xsl:text> } </xsl:text> @@ -1246,8 +1298,8 @@ <xsl:text>} </xsl:text> </xsl:template> - <xsl:variable name="excluded_types" select="str:split('Page Lang')"/> - <xsl:variable name="excluded_ids" select="$parsed_widgets/widget[not(@type = $excluded_types)]/@id"/> + <xsl:variable name="excluded_types" select="str:split('Page Lang VarInit')"/> + <xsl:variable name="included_ids" select="$parsed_widgets/widget[not(@type = $excluded_types)]/@id"/> <declarations:hmi-elements/> <xsl:template match="declarations:hmi-elements"> <xsl:text> @@ -1260,7 +1312,7 @@ </xsl:text> <xsl:text>var hmi_widgets = { </xsl:text> - <xsl:apply-templates mode="hmi_widgets" select="$hmi_elements[@id = $excluded_ids]"/> + <xsl:apply-templates mode="hmi_widgets" select="$hmi_elements[@id = $included_ids]"/> <xsl:text>} </xsl:text> <xsl:text> @@ -1343,11 +1395,9 @@ </xsl:template> <func:function name="func:escape_quotes"> <xsl:param name="txt"/> - <xsl:variable name="frst" select="substring-before($txt,'"')"/> - <xsl:variable name="frstln" select="string-length($frst)"/> <xsl:choose> - <xsl:when test="$frstln > 0 and string-length($txt) > $frstln"> - <func:result select="concat($frst,'\"',func:escape_quotes(substring-after($txt,'"')))"/> + <xsl:when test="contains($txt,'"')"> + <func:result select="concat(substring-before($txt,'"'),'\"',func:escape_quotes(substring-after($txt,'"')))"/> </xsl:when> <xsl:otherwise> <func:result select="$txt"/> @@ -1380,6 +1430,78 @@ <xsl:text>} </xsl:text> </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='Button']"> + <xsl:text>class ButtonWidget extends Widget{ +</xsl:text> + <xsl:text> frequency = 5; +</xsl:text> + <xsl:text> state = 0; +</xsl:text> + <xsl:text> active_style = undefined; +</xsl:text> + <xsl:text> inactive_style = undefined; +</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> } +</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> } +</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", "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> + </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='Button']"> <xsl:param name="hmi_element"/> <xsl:call-template name="defs_by_labels"> @@ -1389,63 +1511,7 @@ </xsl:with-param> <xsl:with-param name="mandatory" select="'no'"/> </xsl:call-template> - <xsl:text>frequency: 5, -</xsl:text> - <xsl:text>on_mouse_down: function(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>}, -</xsl:text> - <xsl:text>on_mouse_up: function(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>}, -</xsl:text> - <xsl:text>active_style: undefined, -</xsl:text> - <xsl:text>inactive_style: undefined, -</xsl:text> - <xsl:text>init: function() { -</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> 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.element.setAttribute("onmousedown", "hmi_widgets['</xsl:text> - <xsl:value-of select="$hmi_element/@id"/> - <xsl:text>'].on_mouse_down(evt)"); -</xsl:text> - <xsl:text> this.element.setAttribute("onmouseup", "hmi_widgets['</xsl:text> - <xsl:value-of select="$hmi_element/@id"/> - <xsl:text>'].on_mouse_up(evt)"); -</xsl:text> - <xsl:text>}, + <xsl:text> </xsl:text> </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='CircularBar']"> @@ -1544,6 +1610,320 @@ <xsl:text>}, </xsl:text> </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='CircularSlider']"> + <xsl:text>class CircularSliderWidget extends Widget{ +</xsl:text> + <xsl:text> frequency = 5; +</xsl:text> + <xsl:text> range = undefined; +</xsl:text> + <xsl:text> circle = undefined; +</xsl:text> + <xsl:text> handle_pos = undefined; +</xsl:text> + <xsl:text> drag = false; +</xsl:text> + <xsl:text> enTimer = false; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> dispatch(value) { +</xsl:text> + <xsl:text> if(!this.drag){ +</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.handle_position(value); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> handle_position(value){ +</xsl:text> + <xsl:text> let [min,max,totalDistance] = this.range; +</xsl:text> + <xsl:text> let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance))); +</xsl:text> + <xsl:text> let tip = this.range_elt.getPointAtLength(length); +</xsl:text> + <xsl:text> this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")"); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_release(evt) { +</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> +</xsl:text> + <xsl:text> update_position(evt){ +</xsl:text> + <xsl:text> if(this.drag && this.enTimer){ +</xsl:text> + <xsl:text> var svg_dist = 0; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //calculate center of widget in html +</xsl:text> + <xsl:text> // --TODO maybe it would be better to bind this part to window change size event ??? +</xsl:text> + <xsl:text> let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; +</xsl:text> + <xsl:text> let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle; +</xsl:text> + <xsl:text> let htmlCirc = this.range_elt.getBoundingClientRect(); +</xsl:text> + <xsl:text> let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left; +</xsl:text> + <xsl:text> let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //get mouse coordinates +</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> mouseX = evt.pageX; +</xsl:text> + <xsl:text> mouseY = evt.pageY; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //calculate angle +</xsl:text> + <xsl:text> let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // transform from 0 to 2PI +</xsl:text> + <xsl:text> if (fi > 0){ +</xsl:text> + <xsl:text> fi = 2*Math.PI-fi; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> fi = -fi; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //offset it to 0 +</xsl:text> + <xsl:text> fi = fi - fiStart; +</xsl:text> + <xsl:text> if (fi < 0){ +</xsl:text> + <xsl:text> fi = fi + 2*Math.PI; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //get handle distance from mouse position +</xsl:text> + <xsl:text> if(fi<fiEnd){ +</xsl:text> + <xsl:text> svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else if(fiEnd<fi && fi<fiEnd+minMax){ +</xsl:text> + <xsl:text> svg_dist = this.range[1]; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> svg_dist = this.range[0]; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //redraw handle --TODO is it fast enough if I just call change_hmi_value??? +</xsl:text> + <xsl:text> this.handle_position(svg_dist); +</xsl:text> + <xsl:text> if(this.value_elt) +</xsl:text> + <xsl:text> this.value_elt.textContent = String(Math.ceil(svg_dist)); +</xsl:text> + <xsl:text> this.apply_hmi_value(0, Math.ceil(svg_dist)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //reset timer +</xsl:text> + <xsl:text> this.enTimer = false; +</xsl:text> + <xsl:text> setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</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> this.update_position(evt); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> init() { +</xsl:text> + <xsl:text> //get min max +</xsl:text> + <xsl:text> let min = this.min_elt ? +</xsl:text> + <xsl:text> Number(this.min_elt.textContent) : +</xsl:text> + <xsl:text> this.args.length >= 1 ? this.args[0] : 0; +</xsl:text> + <xsl:text> let max = this.max_elt ? +</xsl:text> + <xsl:text> Number(this.max_elt.textContent) : +</xsl:text> + <xsl:text> this.args.length >= 2 ? this.args[1] : 100; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //fiStart ==> offset +</xsl:text> + <xsl:text> let fiStart = Number(this.range_elt.getAttribute('sodipodi:start')); +</xsl:text> + <xsl:text> let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end')); +</xsl:text> + <xsl:text> fiEnd = fiEnd - fiStart; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //fiEnd ==> size of angle +</xsl:text> + <xsl:text> if (fiEnd < 0){ +</xsl:text> + <xsl:text> fiEnd = 2*Math.PI + fiEnd; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //min max barrier angle +</xsl:text> + <xsl:text> let minMax = (2*Math.PI - fiEnd)/2; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //get parameters from svg +</xsl:text> + <xsl:text> let cX = Number(this.range_elt.getAttribute('sodipodi:cx')); +</xsl:text> + <xsl:text> let cY = Number(this.range_elt.getAttribute('sodipodi:cy')); +</xsl:text> + <xsl:text> this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object +</xsl:text> + <xsl:text> this.range = [min, max,this.range_elt.getTotalLength()]; +</xsl:text> + <xsl:text> let cPos = this.range_elt.getBBox(); +</xsl:text> + <xsl:text> this.handle_pos = this.range_elt.getPointAtLength(0); +</xsl:text> + <xsl:text> this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height]; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //init events +</xsl:text> + <xsl:text> this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this)); +</xsl:text> + <xsl:text> this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); +</xsl:text> + <xsl:text> this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this)) +</xsl:text> + <xsl:text> window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> + <xsl:template mode="widget_defs" match="widget[@type='CircularSlider']"> + <xsl:param name="hmi_element"/> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>handle range</xsl:text> + </xsl:with-param> + </xsl:call-template> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>value min max</xsl:text> + </xsl:with-param> + <xsl:with-param name="mandatory" select="'no'"/> + </xsl:call-template> + <xsl:text> +</xsl:text> + </xsl:template> <xsl:template mode="widget_class" match="widget[@type='Display']"> <xsl:text>class DisplayWidget extends Widget{ </xsl:text> @@ -2833,6 +3213,13 @@ </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='Input']"> <xsl:param name="hmi_element"/> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>key_pos</xsl:text> + </xsl:with-param> + <xsl:with-param name="mandatory" select="'no'"/> + </xsl:call-template> <xsl:variable name="value_elt"> <xsl:call-template name="defs_by_labels"> <xsl:with-param name="hmi_element" select="$hmi_element"/> @@ -2891,11 +3278,13 @@ </xsl:text> <xsl:text> on_edit_click: function(opstr) { </xsl:text> + <xsl:text> var size = (typeof this.key_pos_elt !== 'undefined') ? this.key_pos_elt.getBBox() : undefined +</xsl:text> <xsl:text> edit_value("</xsl:text> <xsl:value-of select="path/@value"/> <xsl:text>", "</xsl:text> <xsl:value-of select="path/@type"/> - <xsl:text>", this, this.last_val); + <xsl:text>", this, this.last_val,size); </xsl:text> <xsl:text> }, </xsl:text> @@ -3310,6 +3699,298 @@ <xsl:text> </xsl:text> </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='Keypad']"> + <xsl:text>class KeypadWidget extends Widget{ +</xsl:text> + <xsl:text> moving = undefined; +</xsl:text> + <xsl:text> enTimer = undefined; +</xsl:text> + <xsl:text> offset = undefined; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_position_click(evt) { +</xsl:text> + <xsl:text> this.moving = true; +</xsl:text> + <xsl:text> this.enTimer = true; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // get click position offset from widget x,y and save it to variable +</xsl:text> + <xsl:text> var keypad_borders = this.position_elt.getBoundingClientRect(); +</xsl:text> + <xsl:text> var clickX = undefined; +</xsl:text> + <xsl:text> var clickY = undefined; +</xsl:text> + <xsl:text> if (evt.type == "touchstart"){ +</xsl:text> + <xsl:text> clickX = Math.ceil(evt.touches[0].clientX); +</xsl:text> + <xsl:text> clickY = Math.ceil(evt.touches[0].clientY); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> clickX = evt.pageX; +</xsl:text> + <xsl:text> clickY = evt.pageY; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> this.offset=[clickX-keypad_borders.left,clickY-keypad_borders.top] +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> off_position_click(evt) { +</xsl:text> + <xsl:text> if(this.moving) +</xsl:text> + <xsl:text> this.moving = false; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_move(evt) { +</xsl:text> + <xsl:text> if(this.moving && this.enTimer){ +</xsl:text> + <xsl:text> //get keyboard pos in html +</xsl:text> + <xsl:text> let [eltid, tmpgrp] = current_modal; +</xsl:text> + <xsl:text> let [xcoord,ycoord] = this.coordinates; +</xsl:text> + <xsl:text> let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //get mouse coordinates +</xsl:text> + <xsl:text> var clickX = undefined; +</xsl:text> + <xsl:text> var clickY = undefined; +</xsl:text> + <xsl:text> if (evt.type == "touchmove"){ +</xsl:text> + <xsl:text> clickX = Math.ceil(evt.touches[0].clientX); +</xsl:text> + <xsl:text> clickY = Math.ceil(evt.touches[0].clientY); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> clickX = evt.pageX; +</xsl:text> + <xsl:text> clickY = evt.pageY; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //translate keyboard position +</xsl:text> + <xsl:text> let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth; +</xsl:text> + <xsl:text> let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight; +</xsl:text> + <xsl:text> tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")"); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //reset timer +</xsl:text> + <xsl:text> this.enTimer = false; +</xsl:text> + <xsl:text> setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_key_click(symbols) { +</xsl:text> + <xsl:text> var syms = symbols.split(" "); +</xsl:text> + <xsl:text> this.shift |= this.caps; +</xsl:text> + <xsl:text> this.editstr += syms[this.shift?syms.length-1:0]; +</xsl:text> + <xsl:text> this.shift = false; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_Esc_click() { +</xsl:text> + <xsl:text> end_modal.call(this); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_Enter_click() { +</xsl:text> + <xsl:text> end_modal.call(this); +</xsl:text> + <xsl:text> let callback_obj = this.result_callback_obj; +</xsl:text> + <xsl:text> callback_obj.edit_callback(this.editstr); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_BackSpace_click() { +</xsl:text> + <xsl:text> this.editstr = this.editstr.slice(0,this.editstr.length-1); +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_Sign_click() { +</xsl:text> + <xsl:text> if(this.editstr[0] == "-") +</xsl:text> + <xsl:text> this.editstr = this.editstr.slice(1,this.editstr.length); +</xsl:text> + <xsl:text> else +</xsl:text> + <xsl:text> this.editstr = "-" + this.editstr; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_NumDot_click() { +</xsl:text> + <xsl:text> if(this.editstr.indexOf(".") == "-1"){ +</xsl:text> + <xsl:text> this.editstr += "."; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_Space_click() { +</xsl:text> + <xsl:text> this.editstr += " "; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> caps = false; +</xsl:text> + <xsl:text> _caps = undefined; +</xsl:text> + <xsl:text> on_CapsLock_click() { +</xsl:text> + <xsl:text> this.caps = !this.caps; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> shift = false; +</xsl:text> + <xsl:text> _shift = undefined; +</xsl:text> + <xsl:text> on_Shift_click() { +</xsl:text> + <xsl:text> this.shift = !this.shift; +</xsl:text> + <xsl:text> this.caps = false; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> editstr = ""; +</xsl:text> + <xsl:text> _editstr = undefined; +</xsl:text> + <xsl:text> result_callback_obj = undefined; +</xsl:text> + <xsl:text> start_edit(info, valuetype, callback_obj, initial,size) { +</xsl:text> + <xsl:text> show_modal.call(this,size); +</xsl:text> + <xsl:text> this.editstr = initial; +</xsl:text> + <xsl:text> this.result_callback_obj = callback_obj; +</xsl:text> + <xsl:text> this.Info_elt.textContent = info; +</xsl:text> + <xsl:text> this.shift = false; +</xsl:text> + <xsl:text> this.caps = false; +</xsl:text> + <xsl:text> this.update(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> update() { +</xsl:text> + <xsl:text> if(this.editstr != this._editstr){ +</xsl:text> + <xsl:text> this._editstr = this.editstr; +</xsl:text> + <xsl:text> this.Value_elt.textContent = this.editstr; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> if(this.shift != this._shift){ +</xsl:text> + <xsl:text> this._shift = this.shift; +</xsl:text> + <xsl:text> (this.shift?widget_active_activable:widget_inactive_activable)(this.Shift_sub); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> if(this.caps != this._caps){ +</xsl:text> + <xsl:text> this._caps = this.caps; +</xsl:text> + <xsl:text> (this.caps?widget_active_activable:widget_inactive_activable)(this.CapsLock_sub); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='Keypad']"> <xsl:param name="hmi_element"/> <xsl:call-template name="defs_by_labels"> @@ -3321,7 +4002,7 @@ <xsl:call-template name="defs_by_labels"> <xsl:with-param name="hmi_element" select="$hmi_element"/> <xsl:with-param name="labels"> - <xsl:text>Sign Space NumDot</xsl:text> + <xsl:text>Sign Space NumDot position</xsl:text> </xsl:with-param> <xsl:with-param name="mandatory" select="'no'"/> </xsl:call-template> @@ -3359,105 +4040,27 @@ <xsl:text>_click()"); </xsl:text> </xsl:for-each> + <xsl:text> if(this.position_elt){ +</xsl:text> + <xsl:text> this.position_elt.setAttribute("onmousedown", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)"); +</xsl:text> + <xsl:text> this.position_elt.setAttribute("ontouchstart", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)"); +</xsl:text> + <xsl:text> window.addEventListener("mouseup", hmi_widgets[this.element_id].off_position_click.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("touchend", hmi_widgets[this.element_id].off_position_click.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("touchcancel", hmi_widgets[this.element_id].off_position_click.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("mousemove", hmi_widgets[this.element_id].on_move.bind(this)); +</xsl:text> + <xsl:text> window.addEventListener("touchmove", hmi_widgets[this.element_id].on_move.bind(this)); +</xsl:text> + <xsl:text> } +</xsl:text> <xsl:text> }, </xsl:text> - <xsl:text> on_key_click: function(symbols) { -</xsl:text> - <xsl:text> var syms = symbols.split(" "); -</xsl:text> - <xsl:text> this.shift |= this.caps; -</xsl:text> - <xsl:text> this.editstr += syms[this.shift?syms.length-1:0]; -</xsl:text> - <xsl:text> this.shift = false; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_Esc_click: function() { -</xsl:text> - <xsl:text> end_modal.call(this); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_Enter_click: function() { -</xsl:text> - <xsl:text> end_modal.call(this); -</xsl:text> - <xsl:text> callback_obj = this.result_callback_obj; -</xsl:text> - <xsl:text> callback_obj.edit_callback(this.editstr); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_BackSpace_click: function() { -</xsl:text> - <xsl:text> this.editstr = this.editstr.slice(0,this.editstr.length-1); -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_Sign_click: function() { -</xsl:text> - <xsl:text> if(this.editstr[0] == "-") -</xsl:text> - <xsl:text> this.editstr = this.editstr.slice(1,this.editstr.length); -</xsl:text> - <xsl:text> else -</xsl:text> - <xsl:text> this.editstr = "-" + this.editstr; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_NumDot_click: function() { -</xsl:text> - <xsl:text> if(this.editstr.indexOf(".") == "-1"){ -</xsl:text> - <xsl:text> this.editstr += "."; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> on_Space_click: function() { -</xsl:text> - <xsl:text> this.editstr += " "; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> caps: false, -</xsl:text> - <xsl:text> _caps: undefined, -</xsl:text> - <xsl:text> on_CapsLock_click: function() { -</xsl:text> - <xsl:text> this.caps = !this.caps; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> shift: false, -</xsl:text> - <xsl:text> _shift: undefined, -</xsl:text> - <xsl:text> on_Shift_click: function() { -</xsl:text> - <xsl:text> this.shift = !this.shift; -</xsl:text> - <xsl:text> this.caps = false; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, + <xsl:text> </xsl:text> <xsl:variable name="g" select="$geometry[@Id = $hmi_element/@id]"/> <xsl:text> coordinates: [</xsl:text> @@ -3466,58 +4069,6 @@ <xsl:value-of select="$g/@y"/> <xsl:text>], </xsl:text> - <xsl:text> editstr: "", -</xsl:text> - <xsl:text> _editstr: undefined, -</xsl:text> - <xsl:text> result_callback_obj: undefined, -</xsl:text> - <xsl:text> start_edit: function(info, valuetype, callback_obj, initial) { -</xsl:text> - <xsl:text> show_modal.call(this); -</xsl:text> - <xsl:text> this.editstr = initial; -</xsl:text> - <xsl:text> this.result_callback_obj = callback_obj; -</xsl:text> - <xsl:text> this.Info_elt.textContent = info; -</xsl:text> - <xsl:text> this.shift = false; -</xsl:text> - <xsl:text> this.caps = false; -</xsl:text> - <xsl:text> this.update(); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> update: function() { -</xsl:text> - <xsl:text> if(this.editstr != this._editstr){ -</xsl:text> - <xsl:text> this._editstr = this.editstr; -</xsl:text> - <xsl:text> this.Value_elt.textContent = this.editstr; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> if(this.shift != this._shift){ -</xsl:text> - <xsl:text> this._shift = this.shift; -</xsl:text> - <xsl:text> (this.shift?widget_active_activable:widget_inactive_activable)(this.Shift_sub); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> if(this.caps != this._caps){ -</xsl:text> - <xsl:text> this._caps = this.caps; -</xsl:text> - <xsl:text> (this.caps?widget_active_activable:widget_inactive_activable)(this.CapsLock_sub); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> }, -</xsl:text> </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='List']"> <xsl:param name="hmi_element"/> @@ -3592,16 +4143,20 @@ <xsl:text> }, </xsl:text> </xsl:template> - <xsl:template mode="widget_class" match="widget[@type='Switch']"> - <xsl:text>class SwitchWidget extends Widget{ + <xsl:template mode="widget_class" match="widget[@type='MultiState']"> + <xsl:text>class MultiStateWidget extends Widget{ </xsl:text> <xsl:text> frequency = 5; </xsl:text> + <xsl:text> state = 0; +</xsl:text> <xsl:text> dispatch(value) { </xsl:text> + <xsl:text> this.state = value; +</xsl:text> <xsl:text> for(let choice of this.choices){ </xsl:text> - <xsl:text> if(value != choice.value){ + <xsl:text> if(this.state != choice.value){ </xsl:text> <xsl:text> choice.elt.setAttribute("style", "display:none"); </xsl:text> @@ -3615,10 +4170,62 @@ </xsl:text> <xsl:text> } </xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_click(evt) { +</xsl:text> + <xsl:text> //get current selected value +</xsl:text> + <xsl:text> let next_ind; +</xsl:text> + <xsl:text> for(next_ind=0; next_ind<this.choices.length; next_ind++){ +</xsl:text> + <xsl:text> if(this.state == this.choices[next_ind].value){ +</xsl:text> + <xsl:text> next_ind = next_ind + 1; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //get next selected value +</xsl:text> + <xsl:text> if(this.choices.length > next_ind){ +</xsl:text> + <xsl:text> this.state = this.choices[next_ind].value; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> this.state = this.choices[0].value; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> //post value to plc +</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.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); +</xsl:text> + <xsl:text> } +</xsl:text> <xsl:text>} </xsl:text> </xsl:template> - <xsl:template mode="widget_defs" match="widget[@type='Switch']"> + <xsl:template mode="widget_defs" match="widget[@type='MultiState']"> <xsl:param name="hmi_element"/> <xsl:text> choices: [ </xsl:text> @@ -3649,6 +4256,457 @@ <xsl:text> ], </xsl:text> </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='Slider']"> + <xsl:text>class SliderWidget extends Widget{ +</xsl:text> + <xsl:text> frequency = 5; +</xsl:text> + <xsl:text> range = undefined; +</xsl:text> + <xsl:text> fi = undefined; +</xsl:text> + <xsl:text> drag = false; +</xsl:text> + <xsl:text> enTimer = false; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> dispatch(value) { +</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> + <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> + <xsl:text> if(this.setpoint_elt != undefined){ +</xsl:text> + <xsl:text> if(this.last_drag!= this.drag){ +</xsl:text> + <xsl:text> if(this.drag){ +</xsl:text> + <xsl:text> this.setpoint_elt.setAttribute("style", this.setpoint_style); +</xsl:text> + <xsl:text> }else{ +</xsl:text> + <xsl:text> this.setpoint_elt.setAttribute("style", "display:none"); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> this.last_drag = this.drag; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_release(evt) { +</xsl:text> + <xsl:text> window.removeEventListener("touchmove", this.on_bound_drag, true); +</xsl:text> + <xsl:text> window.removeEventListener("mousemove", this.on_bound_drag, true); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> window.removeEventListener("mouseup", this.bound_on_release, true) +</xsl:text> + <xsl:text> window.removeEventListener("touchend", this.bound_on_release, true); +</xsl:text> + <xsl:text> window.removeEventListener("touchcancel", this.bound_on_release, true); +</xsl:text> + <xsl:text> if(this.drag){ +</xsl:text> + <xsl:text> this.drag = false; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> this.update_position(evt); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> on_drag(evt){ +</xsl:text> + <xsl:text> if(this.enTimer && this.drag){ +</xsl:text> + <xsl:text> this.update_position(evt); +</xsl:text> + <xsl:text> //reset timer +</xsl:text> + <xsl:text> this.enTimer = false; +</xsl:text> + <xsl:text> setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> update_position(evt){ +</xsl:text> + <xsl:text> var html_dist = 0; +</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 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 +</xsl:text> + <xsl:text> var mouseX = undefined; +</xsl:text> + <xsl:text> var 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> mouseX = evt.pageX; +</xsl:text> + <xsl:text> mouseY = evt.pageY; +</xsl:text> + <xsl:text> } +</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> + <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> // 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> + <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> //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> + <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> + <xsl:text> } +</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> let min = this.min_elt ? +</xsl:text> + <xsl:text> Number(this.min_elt.textContent) : +</xsl:text> + <xsl:text> this.args.length >= 1 ? this.args[0] : 0; +</xsl:text> + <xsl:text> let max = this.max_elt ? +</xsl:text> + <xsl:text> Number(this.max_elt.textContent) : +</xsl:text> + <xsl:text> this.args.length >= 2 ? this.args[1] : 100; +</xsl:text> + <xsl:text> +</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); +</xsl:text> + <xsl:text> let end = this.range_elt.getPointAtLength(this.range_elt.getTotalLength()); +</xsl:text> + <xsl:text> this.fi = Math.atan2(start.y-end.y, end.x-start.x); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> this.bound_on_select = this.on_select.bind(this); +</xsl:text> + <xsl:text> this.bound_on_release = this.on_release.bind(this); +</xsl:text> + <xsl:text> this.on_bound_drag = this.on_drag.bind(this); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> this.element.addEventListener("mousedown", this.bound_on_select); +</xsl:text> + <xsl:text> this.element.addEventListener("touchstart", this.bound_on_select); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(this.setpoint_elt != undefined){ +</xsl:text> + <xsl:text> this.setpoint_style = this.setpoint_elt.getAttribute("style"); +</xsl:text> + <xsl:text> this.setpoint_elt.setAttribute("style", "display:none"); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> + <xsl:template mode="widget_defs" match="widget[@type='Slider']"> + <xsl:param name="hmi_element"/> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>handle range</xsl:text> + </xsl:with-param> + </xsl:call-template> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>value min max setpoint</xsl:text> + </xsl:with-param> + <xsl:with-param name="mandatory" select="'no'"/> + </xsl:call-template> + <xsl:text> +</xsl:text> + </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='Switch']"> + <xsl:text>class SwitchWidget extends Widget{ +</xsl:text> + <xsl:text> frequency = 5; +</xsl:text> + <xsl:text> dispatch(value) { +</xsl:text> + <xsl:text> for(let choice of this.choices){ +</xsl:text> + <xsl:text> if(value != choice.value){ +</xsl:text> + <xsl:text> choice.elt.setAttribute("style", "display:none"); +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> choice.elt.setAttribute("style", choice.style); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> + <xsl:template mode="widget_defs" match="widget[@type='Switch']"> + <xsl:param name="hmi_element"/> + <xsl:text> choices: [ +</xsl:text> + <xsl:variable name="regex" select="'^("[^"].*"|\-?[0-9]+|false|true)(#.*)?$'"/> + <xsl:for-each select="$result_svg_ns//*[@id = $hmi_element/@id]//*[regexp:test(@inkscape:label,$regex)]"> + <xsl:variable name="literal" select="regexp:match(@inkscape:label,$regex)[2]"/> + <xsl:text> { +</xsl:text> + <xsl:text> elt:id("</xsl:text> + <xsl:value-of select="@id"/> + <xsl:text>"), +</xsl:text> + <xsl:text> style:"</xsl:text> + <xsl:value-of select="@style"/> + <xsl:text>", +</xsl:text> + <xsl:text> value:</xsl:text> + <xsl:value-of select="$literal"/> + <xsl:text> +</xsl:text> + <xsl:text> }</xsl:text> + <xsl:if test="position()!=last()"> + <xsl:text>,</xsl:text> + </xsl:if> + <xsl:text> +</xsl:text> + </xsl:for-each> + <xsl:text> ], +</xsl:text> + </xsl:template> + <xsl:template mode="widget_class" match="widget[@type='ToggleButton']"> + <xsl:text>class ToggleButtonWidget extends Widget{ +</xsl:text> + <xsl:text> frequency = 5; +</xsl:text> + <xsl:text> state = 0; +</xsl:text> + <xsl:text> active_style = undefined; +</xsl:text> + <xsl:text> inactive_style = undefined; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> dispatch(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> 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> } +</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; +</xsl:text> + <xsl:text> this.inactive_style = this.inactive_elt.style.cssText; +</xsl:text> + <xsl:text> this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> <xsl:template mode="widget_defs" match="widget[@type='ToggleButton']"> <xsl:param name="hmi_element"/> <xsl:call-template name="defs_by_labels"> @@ -3657,55 +4715,7 @@ <xsl:text>active inactive</xsl:text> </xsl:with-param> </xsl:call-template> - <xsl:text> frequency: 5, -</xsl:text> - <xsl:text> state: 0, -</xsl:text> - <xsl:text> dispatch: function(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> 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> }, -</xsl:text> - <xsl:text> on_click: function(evt) { -</xsl:text> - <xsl:text> this.apply_hmi_value(0, this.state); -</xsl:text> - <xsl:text> }, -</xsl:text> - <xsl:text> active_style: undefined, -</xsl:text> - <xsl:text> inactive_style: undefined, -</xsl:text> - <xsl:text> init: function() { -</xsl:text> - <xsl:text> this.active_style = this.active_elt.style.cssText; -</xsl:text> - <xsl:text> this.inactive_style = this.inactive_elt.style.cssText; -</xsl:text> - <xsl:text> this.element.setAttribute("onclick", "hmi_widgets['</xsl:text> - <xsl:value-of select="$hmi_element/@id"/> - <xsl:text>'].on_click(evt)"); -</xsl:text> - <xsl:text> }, + <xsl:text> </xsl:text> </xsl:template> <xsl:template match="/"> @@ -3866,8 +4876,6 @@ </xsl:text> <xsl:text> // -> pass Number(index) instead </xsl:text> - <xsl:text> console.log("apply updated local variable ",index, updates[index]); -</xsl:text> <xsl:text> dispatch_value(Number(index), updates[index]); </xsl:text> <xsl:text> delete updates[index]; @@ -3908,6 +4916,14 @@ </xsl:text> <xsl:text> apply_updates(); </xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> pending_widget_animates.forEach(widget => widget._animate()); +</xsl:text> + <xsl:text> pending_widget_animates = []; +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text> requestAnimationFrameID = null; </xsl:text> <xsl:text>} @@ -4576,7 +5592,7 @@ </xsl:text> <xsl:text>var edit_callback; </xsl:text> - <xsl:text>function edit_value(path, valuetype, callback, initial) { + <xsl:text>function edit_value(path, valuetype, callback, initial, size) { </xsl:text> <xsl:text> </xsl:text> @@ -4588,7 +5604,7 @@ </xsl:text> <xsl:text> let widget = hmi_widgets[keypadid]; </xsl:text> - <xsl:text> widget.start_edit(path, valuetype, callback, initial); + <xsl:text> widget.start_edit(path, valuetype, callback, initial, size); </xsl:text> <xsl:text>}; </xsl:text> @@ -4598,7 +5614,7 @@ </xsl:text> <xsl:text> </xsl:text> - <xsl:text>function show_modal() { + <xsl:text>function show_modal(size) { </xsl:text> <xsl:text> let [element, parent] = detachable_elements[this.element.id]; </xsl:text> @@ -4608,13 +5624,23 @@ </xsl:text> <xsl:text> tmpgrpattr = document.createAttribute("transform"); </xsl:text> - <xsl:text> -</xsl:text> <xsl:text> let [xcoord,ycoord] = this.coordinates; </xsl:text> <xsl:text> let [xdest,ydest] = page_desc[current_visible_page].bbox; </xsl:text> - <xsl:text> tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; + <xsl:text> if (typeof size === 'undefined'){ +</xsl:text> + <xsl:text> tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> else{ +</xsl:text> + <xsl:text> tmpgrpattr.value = "translate("+String(xdest-xcoord+size.x)+","+String(ydest-ycoord+size.y)+")"; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> </xsl:text> <xsl:text> tmpgrp.setAttributeNode(tmpgrpattr); </xsl:text> diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/svghmi.js --- a/svghmi/svghmi.js Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/svghmi.js Wed Aug 12 15:24:02 2020 +0200 @@ -75,6 +75,10 @@ if(jumps_need_update) update_jumps(); apply_updates(); + + pending_widget_animates.forEach(widget => widget._animate()); + pending_widget_animates = []; + requestAnimationFrameID = null; } @@ -409,26 +413,31 @@ var xmlns = "http://www.w3.org/2000/svg"; var edit_callback; -function edit_value(path, valuetype, callback, initial) { +function edit_value(path, valuetype, callback, initial, size) { let [keypadid, xcoord, ycoord] = keypads[valuetype]; console.log('XXX TODO : Edit value', path, valuetype, callback, initial, keypadid); edit_callback = callback; let widget = hmi_widgets[keypadid]; - widget.start_edit(path, valuetype, callback, initial); + widget.start_edit(path, valuetype, callback, initial, size); }; var current_modal; /* TODO stack ?*/ -function show_modal() { +function show_modal(size) { let [element, parent] = detachable_elements[this.element.id]; tmpgrp = document.createElementNS(xmlns,"g"); tmpgrpattr = document.createAttribute("transform"); - let [xcoord,ycoord] = this.coordinates; let [xdest,ydest] = page_desc[current_visible_page].bbox; - tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; + if (typeof size === 'undefined'){ + tmpgrpattr.value = "translate("+String(xdest-xcoord)+","+String(ydest-ycoord)+")"; + } + else{ + tmpgrpattr.value = "translate("+String(xdest-xcoord+size.x)+","+String(ydest-ycoord+size.y)+")"; + } + tmpgrp.setAttributeNode(tmpgrpattr); tmpgrp.appendChild(element); diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_button.ysl2 --- a/svghmi/widget_button.ysl2 Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/widget_button.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -1,33 +1,48 @@ // widget_button.ysl2 +template "widget[@type='Button']", mode="widget_class"{ + || + class ButtonWidget extends Widget{ + frequency = 5; + state = 0; + active_style = undefined; + inactive_style = undefined; + + 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); + } + + on_mouse_up(evt) { + 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.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)"); + } + } + || +} + + template "widget[@type='Button']", mode="widget_defs" { param "hmi_element"; optional_labels("active inactive"); - | frequency: 5, - | on_mouse_down: function(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); - | }, - | on_mouse_up: function(evt) { - | 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); - | }, - | active_style: undefined, - | inactive_style: undefined, - | init: function() { - | 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['«$hmi_element/@id»'].on_mouse_down(evt)"); - | this.element.setAttribute("onmouseup", "hmi_widgets['«$hmi_element/@id»'].on_mouse_up(evt)"); - | }, + |, } diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_circularslider.ysl2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widget_circularslider.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,159 @@ +// widget_circuralslider.ysl2 + +template "widget[@type='CircularSlider']", mode="widget_class" + || + class CircularSliderWidget extends Widget{ + frequency = 5; + range = undefined; + circle = undefined; + handle_pos = undefined; + drag = false; + enTimer = false; + + dispatch(value) { + if(!this.drag){ + if(this.value_elt) + this.value_elt.textContent = String(value); + + this.handle_position(value); + } + } + + handle_position(value){ + let [min,max,totalDistance] = this.range; + let length = Math.max(0,Math.min((totalDistance),(Number(value)-min)/(max-min)*(totalDistance))); + let tip = this.range_elt.getPointAtLength(length); + this.handle_elt.setAttribute('transform',"translate("+(tip.x-this.handle_pos.x)+","+(tip.y-this.handle_pos.y)+")"); + } + + on_release(evt) { + if(this.drag){ + this.drag = false; + } + } + + update_position(evt){ + if(this.drag && this.enTimer){ + var svg_dist = 0; + + //calculate center of widget in html + // --TODO maybe it would be better to bind this part to window change size event ??? + let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; + let [cX, cY,fiStart,fiEnd,minMax,x1,y1,width,height] = this.circle; + let htmlCirc = this.range_elt.getBoundingClientRect(); + let cxHtml = ((htmlCirc.right-htmlCirc.left)/(width)*(cX-x1))+htmlCirc.left; + let cyHtml = ((htmlCirc.bottom-htmlCirc.top)/(height)*(cY-y1))+htmlCirc.top; + + + //get mouse coordinates + let mouseX = undefined; + let mouseY = undefined; + if (evt.type.startsWith("touch")){ + mouseX = Math.ceil(evt.touches[0].clientX); + mouseY = Math.ceil(evt.touches[0].clientY); + } + else{ + mouseX = evt.pageX; + mouseY = evt.pageY; + } + + //calculate angle + let fi = Math.atan2(cyHtml-mouseY, mouseX-cxHtml); + + // transform from 0 to 2PI + if (fi > 0){ + fi = 2*Math.PI-fi; + } + else{ + fi = -fi; + } + + //offset it to 0 + fi = fi - fiStart; + if (fi < 0){ + fi = fi + 2*Math.PI; + } + + //get handle distance from mouse position + if(fi<fiEnd){ + svg_dist=(fi)/(fiEnd)*(this.range[1]-this.range[0]); + } + else if(fiEnd<fi && fi<fiEnd+minMax){ + svg_dist = this.range[1]; + } + else{ + svg_dist = this.range[0]; + } + + //redraw handle --TODO is it fast enough if I just call change_hmi_value??? + this.handle_position(svg_dist); + if(this.value_elt) + this.value_elt.textContent = String(Math.ceil(svg_dist)); + this.apply_hmi_value(0, Math.ceil(svg_dist)); + + //reset timer + this.enTimer = false; + setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); + } + + } + + on_select(evt){ + this.drag = true; + this.enTimer = true; + this.update_position(evt); + } + + init() { + //get min max + let min = this.min_elt ? + Number(this.min_elt.textContent) : + this.args.length >= 1 ? this.args[0] : 0; + let max = this.max_elt ? + Number(this.max_elt.textContent) : + this.args.length >= 2 ? this.args[1] : 100; + + //fiStart ==> offset + let fiStart = Number(this.range_elt.getAttribute('sodipodi:start')); + let fiEnd = Number(this.range_elt.getAttribute('sodipodi:end')); + fiEnd = fiEnd - fiStart; + + //fiEnd ==> size of angle + if (fiEnd < 0){ + fiEnd = 2*Math.PI + fiEnd; + } + + //min max barrier angle + let minMax = (2*Math.PI - fiEnd)/2; + + //get parameters from svg + let cX = Number(this.range_elt.getAttribute('sodipodi:cx')); + let cY = Number(this.range_elt.getAttribute('sodipodi:cy')); + this.range_elt.style.strokeMiterlimit="0"; //eliminates some weird border around html object + this.range = [min, max,this.range_elt.getTotalLength()]; + let cPos = this.range_elt.getBBox(); + this.handle_pos = this.range_elt.getPointAtLength(0); + this.circle = [cX, cY,fiStart,fiEnd,minMax,cPos.x,cPos.y,cPos.width,cPos.height]; + + //init events + this.handle_elt.addEventListener("touchstart", hmi_widgets[this.element_id].on_select.bind(this)); + this.handle_elt.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); + this.element.addEventListener("mousedown", hmi_widgets[this.element_id].on_select.bind(this)); + + window.addEventListener("touchmove", hmi_widgets[this.element_id].update_position.bind(this)); + window.addEventListener("mousemove", hmi_widgets[this.element_id].update_position.bind(this)); + + window.addEventListener("mouseup", hmi_widgets[this.element_id].on_release.bind(this)) + window.addEventListener("touchend", hmi_widgets[this.element_id].on_release.bind(this)); + window.addEventListener("touchcancel", hmi_widgets[this.element_id].on_release.bind(this)); + + } + } + || + +template "widget[@type='CircularSlider']", mode="widget_defs" { + param "hmi_element"; + labels("handle range"); + optional_labels("value min max"); + |, +} diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_input.ysl2 --- a/svghmi/widget_input.ysl2 Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/widget_input.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -2,6 +2,7 @@ template "widget[@type='Input']", mode="widget_defs" { param "hmi_element"; + optional_labels("key_pos"); const "value_elt" { optional_labels("value"); } @@ -33,7 +34,8 @@ // } | }, | on_edit_click: function(opstr) { - | edit_value("«path/@value»", "«path/@type»", this, this.last_val); + | var size = (typeof this.key_pos_elt !== 'undefined') ? this.key_pos_elt.getBBox() : undefined + | edit_value("«path/@value»", "«path/@type»", this, this.last_val,size); | }, | edit_callback: function(new_val) { diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_keypad.ysl2 --- a/svghmi/widget_keypad.ysl2 Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/widget_keypad.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -13,10 +13,159 @@ | } } +template "widget[@type='Keypad']", mode="widget_class" + || + class KeypadWidget extends Widget{ + moving = undefined; + enTimer = undefined; + offset = undefined; + + on_position_click(evt) { + this.moving = true; + this.enTimer = true; + + // get click position offset from widget x,y and save it to variable + var keypad_borders = this.position_elt.getBoundingClientRect(); + var clickX = undefined; + var clickY = undefined; + if (evt.type == "touchstart"){ + clickX = Math.ceil(evt.touches[0].clientX); + clickY = Math.ceil(evt.touches[0].clientY); + } + else{ + clickX = evt.pageX; + clickY = evt.pageY; + } + this.offset=[clickX-keypad_borders.left,clickY-keypad_borders.top] + } + + off_position_click(evt) { + if(this.moving) + this.moving = false; + } + + on_move(evt) { + if(this.moving && this.enTimer){ + //get keyboard pos in html + let [eltid, tmpgrp] = current_modal; + let [xcoord,ycoord] = this.coordinates; + let [xdest,ydest,svgWidth,svgHeight] = page_desc[current_visible_page].bbox; + + //get mouse coordinates + var clickX = undefined; + var clickY = undefined; + if (evt.type == "touchmove"){ + clickX = Math.ceil(evt.touches[0].clientX); + clickY = Math.ceil(evt.touches[0].clientY); + } + else{ + clickX = evt.pageX; + clickY = evt.pageY; + } + + //translate keyboard position + let mouseX = ((clickX-this.offset[0])/window.innerWidth)*svgWidth; + let mouseY = ((clickY-this.offset[1])/window.innerHeight)*svgHeight; + tmpgrp.setAttribute("transform","translate("+String(xdest-xcoord+mouseX)+","+String(ydest-ycoord+mouseY)+")"); + + //reset timer + this.enTimer = false; + setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); + } + + } + + on_key_click(symbols) { + var syms = symbols.split(" "); + this.shift |= this.caps; + this.editstr += syms[this.shift?syms.length-1:0]; + this.shift = false; + this.update(); + } + + on_Esc_click() { + end_modal.call(this); + } + + on_Enter_click() { + end_modal.call(this); + let callback_obj = this.result_callback_obj; + callback_obj.edit_callback(this.editstr); + } + + on_BackSpace_click() { + this.editstr = this.editstr.slice(0,this.editstr.length-1); + this.update(); + } + + on_Sign_click() { + if(this.editstr[0] == "-") + this.editstr = this.editstr.slice(1,this.editstr.length); + else + this.editstr = "-" + this.editstr; + this.update(); + } + + on_NumDot_click() { + if(this.editstr.indexOf(".") == "-1"){ + this.editstr += "."; + this.update(); + } + } + + on_Space_click() { + this.editstr += " "; + this.update(); + } + + caps = false; + _caps = undefined; + on_CapsLock_click() { + this.caps = !this.caps; + this.update(); + } + + shift = false; + _shift = undefined; + on_Shift_click() { + this.shift = !this.shift; + this.caps = false; + this.update(); + } + editstr = ""; + _editstr = undefined; + result_callback_obj = undefined; + start_edit(info, valuetype, callback_obj, initial,size) { + show_modal.call(this,size); + this.editstr = initial; + this.result_callback_obj = callback_obj; + this.Info_elt.textContent = info; + this.shift = false; + this.caps = false; + this.update(); + } + + update() { + if(this.editstr != this._editstr){ + this._editstr = this.editstr; + this.Value_elt.textContent = this.editstr; + } + if(this.shift != this._shift){ + this._shift = this.shift; + (this.shift?widget_active_activable:widget_inactive_activable)(this.Shift_sub); + } + if(this.caps != this._caps){ + this._caps = this.caps; + (this.caps?widget_active_activable:widget_inactive_activable)(this.CapsLock_sub); + } + } + } + || + template "widget[@type='Keypad']", mode="widget_defs" { param "hmi_element"; labels("Esc Enter BackSpace Keys Info Value"); - optional_labels("Sign Space NumDot"); + optional_labels("Sign Space NumDot position"); activable_labels("CapsLock Shift"); | init: function() { foreach "$hmi_element/*[@inkscape:label = 'Keys']/*" { @@ -26,82 +175,19 @@ | if(this.«.»_elt) | this.«.»_elt.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_«.»_click()"); } + | if(this.position_elt){ + | this.position_elt.setAttribute("onmousedown", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)"); + | this.position_elt.setAttribute("ontouchstart", "hmi_widgets['"+this.element_id+"'].on_position_click(evt)"); + + | window.addEventListener("mouseup", hmi_widgets[this.element_id].off_position_click.bind(this)); + | window.addEventListener("touchend", hmi_widgets[this.element_id].off_position_click.bind(this)); + | window.addEventListener("touchcancel", hmi_widgets[this.element_id].off_position_click.bind(this)); + + | window.addEventListener("mousemove", hmi_widgets[this.element_id].on_move.bind(this)); + | window.addEventListener("touchmove", hmi_widgets[this.element_id].on_move.bind(this)); + | } | }, - | on_key_click: function(symbols) { - | var syms = symbols.split(" "); - | this.shift |= this.caps; - | this.editstr += syms[this.shift?syms.length-1:0]; - | this.shift = false; - | this.update(); - | }, - | on_Esc_click: function() { - | end_modal.call(this); - | }, - | on_Enter_click: function() { - | end_modal.call(this); - | callback_obj = this.result_callback_obj; - | callback_obj.edit_callback(this.editstr); - | }, - | on_BackSpace_click: function() { - | this.editstr = this.editstr.slice(0,this.editstr.length-1); - | this.update(); - | }, - | on_Sign_click: function() { - | if(this.editstr[0] == "-") - | this.editstr = this.editstr.slice(1,this.editstr.length); - | else - | this.editstr = "-" + this.editstr; - | this.update(); - | }, - | on_NumDot_click: function() { - | if(this.editstr.indexOf(".") == "-1"){ - | this.editstr += "."; - | this.update(); - | } - | }, - | on_Space_click: function() { - | this.editstr += " "; - | this.update(); - | }, - | caps: false, - | _caps: undefined, - | on_CapsLock_click: function() { - | this.caps = !this.caps; - | this.update(); - | }, - | shift: false, - | _shift: undefined, - | on_Shift_click: function() { - | this.shift = !this.shift; - | this.caps = false; - | this.update(); - | }, + | const "g", "$geometry[@Id = $hmi_element/@id]"; | coordinates: [«$g/@x», «$g/@y»], - | editstr: "", - | _editstr: undefined, - | result_callback_obj: undefined, - | start_edit: function(info, valuetype, callback_obj, initial) { - | show_modal.call(this); - | this.editstr = initial; - | this.result_callback_obj = callback_obj; - | this.Info_elt.textContent = info; - | this.shift = false; - | this.caps = false; - | this.update(); - | }, - | update: function() { - | if(this.editstr != this._editstr){ - | this._editstr = this.editstr; - | this.Value_elt.textContent = this.editstr; - | } - | if(this.shift != this._shift){ - | this._shift = this.shift; - | (this.shift?widget_active_activable:widget_inactive_activable)(this.Shift_sub); - | } - | if(this.caps != this._caps){ - | this._caps = this.caps; - | (this.caps?widget_active_activable:widget_inactive_activable)(this.CapsLock_sub); - | } - | }, } diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_multistate.ysl2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widget_multistate.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,60 @@ +// widget_multistate.ysl2 + +template "widget[@type='MultiState']", mode="widget_class" + || + class MultiStateWidget extends Widget{ + frequency = 5; + state = 0; + dispatch(value) { + this.state = value; + for(let choice of this.choices){ + if(this.state != choice.value){ + choice.elt.setAttribute("style", "display:none"); + } else { + choice.elt.setAttribute("style", choice.style); + } + } + } + + on_click(evt) { + //get current selected value + let next_ind; + for(next_ind=0; next_ind<this.choices.length; next_ind++){ + if(this.state == this.choices[next_ind].value){ + next_ind = next_ind + 1; + break; + } + } + + //get next selected value + if(this.choices.length > next_ind){ + this.state = this.choices[next_ind].value; + } + else{ + this.state = this.choices[0].value; + } + + //post value to plc + this.apply_hmi_value(0, this.state); + } + + init() { + this.element.setAttribute("onclick", "hmi_widgets['"+this.element_id+"'].on_click(evt)"); + } + } + || + +template "widget[@type='MultiState']", mode="widget_defs" { + param "hmi_element"; + | choices: [ + const "regex",!"'^(\"[^\"].*\"|\-?[0-9]+|false|true)(#.*)?$'"!; + foreach "$result_svg_ns//*[@id = $hmi_element/@id]//*[regexp:test(@inkscape:label,$regex)]" { + const "literal", "regexp:match(@inkscape:label,$regex)[2]"; + | { + | elt:id("«@id»"), + | style:"«@style»", + | value:«$literal» + | }`if "position()!=last()" > ,` + } + | ], +} diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_slider.ysl2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widget_slider.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,169 @@ +// widget_slider.ysl2 + +template "widget[@type='Slider']", mode="widget_class" + || + class SliderWidget extends Widget{ + frequency = 5; + range = undefined; + fi = undefined; + drag = false; + enTimer = false; + + dispatch(value) { + if(this.value_elt) + this.value_elt.textContent = String(value); + + this.update_DOM(value, this.handle_elt); + + } + + last_drag = false; + + 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)+")"); + + if(this.setpoint_elt != undefined){ + if(this.last_drag!= this.drag){ + if(this.drag){ + this.setpoint_elt.setAttribute("style", this.setpoint_style); + }else{ + this.setpoint_elt.setAttribute("style", "display:none"); + } + this.last_drag = this.drag; + } + } + } + + on_release(evt) { + 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); + if(this.drag){ + this.drag = false; + } + this.update_position(evt); + } + + + on_drag(evt){ + if(this.enTimer && this.drag){ + this.update_position(evt); + //reset timer + this.enTimer = false; + setTimeout("{hmi_widgets['"+this.element_id+"'].enTimer = true;}", 100); + } + } + + update_position(evt){ + var html_dist = 0; + + //calculate size of widget in html + var range_borders = this.range_elt.getBoundingClientRect(); + 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; + var 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; + } + + //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{ + // 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); + } + + on_select(evt){ + this.drag = true; + this.enTimer = true; + 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); + } + + init() { + let min = this.min_elt ? + Number(this.min_elt.textContent) : + this.args.length >= 1 ? this.args[0] : 0; + let max = this.max_elt ? + Number(this.max_elt.textContent) : + this.args.length >= 2 ? this.args[1] : 100; + + 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.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.element.addEventListener("mousedown", this.bound_on_select); + this.element.addEventListener("touchstart", this.bound_on_select); + + if(this.setpoint_elt != undefined){ + this.setpoint_style = this.setpoint_elt.getAttribute("style"); + this.setpoint_elt.setAttribute("style", "display:none"); + } + + } + } + || + +template "widget[@type='Slider']", mode="widget_defs" { + param "hmi_element"; + labels("handle range"); + optional_labels("value min max setpoint"); + |, +} diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widget_tooglebutton.ysl2 --- a/svghmi/widget_tooglebutton.ysl2 Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/widget_tooglebutton.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -1,30 +1,42 @@ // widget_tooglebutton.ysl2 + +template "widget[@type='ToggleButton']", mode="widget_class"{ + || + class ToggleButtonWidget extends Widget{ + frequency = 5; + state = 0; + active_style = undefined; + 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; + } + } + + on_click(evt) { + this.apply_hmi_value(0, this.state); + } + + init() { + 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)"); + } + } + || +} + template "widget[@type='ToggleButton']", mode="widget_defs" { param "hmi_element"; labels("active inactive"); - | frequency: 5, - | state: 0, - | dispatch: function(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; - | } - | }, - | on_click: function(evt) { - | this.apply_hmi_value(0, this.state); - | }, - | active_style: undefined, - | inactive_style: undefined, - | init: function() { - | this.active_style = this.active_elt.style.cssText; - | this.inactive_style = this.inactive_elt.style.cssText; - | this.element.setAttribute("onclick", "hmi_widgets['«$hmi_element/@id»'].on_click(evt)"); - | }, + |, } diff -r 407a0205405a -r 0a9f6f29b7dd svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Wed Aug 12 13:36:18 2020 +0200 +++ b/svghmi/widgets_common.ysl2 Wed Aug 12 15:24:02 2020 +0200 @@ -24,7 +24,7 @@ template "svg:*", mode="hmi_widgets" { const "widget", "func:widget(@id)"; const "eltid","@id"; - const "args" foreach "$widget/arg" > "«@value»"`if "position()!=last()" > ,` + const "args" foreach "$widget/arg" > "«func:escape_quotes(@value)»"`if "position()!=last()" > ,` const "indexes" foreach "$widget/path" { choose { when "not(@index)" { @@ -92,7 +92,6 @@ new_index = next_available_index++; pagevars[varname] = new_index; } - cache[new_index] = ""; return new_index; } @@ -104,10 +103,14 @@ emit "preamble:widget-base-class" { || + var pending_widget_animates = []; + class Widget { offset = 0; frequency = 10; /* FIXME arbitrary default max freq. Obtain from config ? */ unsubscribable = false; + pending_animate = false; + constructor(elt_id,args,indexes,members){ this.element_id = elt_id; this.element = id(elt_id); @@ -143,9 +146,9 @@ } apply_cache() { - if(!this.unsubscribable) for(let i = 0; i < this.indexes.length; i++) { + if(!this.unsubscribable) for(let index of this.indexes){ /* dispatch current cache in newly opened page widgets */ - let realindex = this.get_variable_index(i); + let realindex = this.get_variable_index(index); let cached_val = cache[realindex]; if(cached_val != undefined) this.new_hmi_value(realindex, cached_val, cached_val); @@ -196,6 +199,20 @@ console.log(err); } } + + _animate(){ + this.animate(); + this.pending_animate = false; + } + + request_animate(){ + if(!this.pending_animate){ + pending_widget_animates.push(this); + this.pending_animate = true; + requestHMIAnimation(); + } + + } } || } @@ -212,12 +229,12 @@ } || -const "excluded_types", "str:split('Page Lang')"; -const "excluded_ids","$parsed_widgets/widget[not(@type = $excluded_types)]/@id"; +const "excluded_types", "str:split('Page Lang VarInit')"; +const "included_ids","$parsed_widgets/widget[not(@type = $excluded_types)]/@id"; emit "declarations:hmi-elements" { | var hmi_widgets = { - apply "$hmi_elements[@id = $excluded_ids]", mode="hmi_widgets"; + apply "$hmi_elements[@id = $included_ids]", mode="hmi_widgets"; | } } @@ -266,11 +283,10 @@ def "func:escape_quotes" { param "txt"; // have to use a python string to enter escaped quote - const "frst", !"substring-before($txt,'\"')"!; - const "frstln", "string-length($frst)"; + // const "frstln", "string-length($frst)"; choose { - when "$frstln > 0 and string-length($txt) > $frstln" { - result !"concat($frst,'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!; + when !"contains($txt,'\"')"! { + result !"concat(substring-before($txt,'\"'),'\\\"',func:escape_quotes(substring-after($txt,'\"')))"!; } otherwise { result "$txt"; diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/beremiz.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,5 @@ +<?xml version='1.0' encoding='utf-8'?> +<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="PYRO://127.0.0.1:61284"> + <TargetType/> + <Libraries Enable_SVGHMI_Library="true"/> +</BeremizRoot> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/plc.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,578 @@ +<?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"> + <coordinateInfo> + <fbd> + <scaling x="5" y="5"/> + </fbd> + <ld> + <scaling x="0" y="0"/> + </ld> + <sfc> + <scaling x="0" y="0"/> + </sfc> + </coordinateInfo> + </contentHeader> + <types> + <dataTypes/> + <pous> + <pou name="MainStuff" pouType="program"> + <interface> + <localVars> + <variable name="TargetPressure"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + <variable name="selection"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + <variable name="Pump0"> + <type> + <derived name="PumpControl"/> + </type> + </variable> + <variable name="TestButton"> + <type> + <derived name="HMI_BOOL"/> + </type> + </variable> + <variable name="TestLocal"> + <type> + <BOOL/> + </type> + </variable> + <variable name="Multistate"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + <variable name="Radiostate"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + <variable name="MultistateExt"> + <type> + <INT/> + </type> + </variable> + </localVars> + </interface> + <body> + <FBD> + <block localId="4" typeName="PumpControl" instanceName="Pump0" executionOrderId="0" height="40" width="127"> + <position x="595" y="50"/> + <inputVariables> + <variable formalParameter="TargetPressure"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="5"> + <position x="595" y="80"/> + <position x="570" y="80"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables/> + </block> + <inVariable localId="5" executionOrderId="0" height="30" width="125" negated="false"> + <position x="445" y="65"/> + <connectionPointOut> + <relPosition x="125" y="15"/> + </connectionPointOut> + <expression>TargetPressure</expression> + </inVariable> + <inVariable localId="6" executionOrderId="0" height="25" width="90" negated="false"> + <position x="155" y="220"/> + <connectionPointOut> + <relPosition x="90" y="10"/> + </connectionPointOut> + <expression>TestButton</expression> + </inVariable> + <outVariable localId="7" executionOrderId="0" height="25" width="85" negated="false"> + <position x="495" y="220"/> + <connectionPointIn> + <relPosition x="0" y="10"/> + <connection refLocalId="6"> + <position x="495" y="230"/> + <position x="245" y="230"/> + </connection> + </connectionPointIn> + <expression>TestLocal</expression> + </outVariable> + <inVariable localId="1" executionOrderId="0" height="25" width="115" negated="false"> + <position x="175" y="355"/> + <connectionPointOut> + <relPosition x="115" y="10"/> + </connectionPointOut> + <expression>Multistate</expression> + </inVariable> + <outVariable localId="8" executionOrderId="0" height="25" width="115" negated="false"> + <position x="495" y="355"/> + <connectionPointIn> + <relPosition x="0" y="10"/> + <connection refLocalId="1"> + <position x="495" y="365"/> + <position x="290" y="365"/> + </connection> + </connectionPointIn> + <expression>MultistateExt</expression> + </outVariable> + </FBD> + </body> + </pou> + <pou name="PumpControl" pouType="functionBlock"> + <interface> + <localVars> + <variable name="Pump"> + <type> + <derived name="HMI_NODE"/> + </type> + </variable> + <variable name="Pressure"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + </localVars> + <inputVars> + <variable name="TargetPressure"> + <type> + <INT/> + </type> + </variable> + </inputVars> + <localVars> + <variable name="Sloth"> + <type> + <derived name="HMI_INT"/> + </type> + </variable> + <variable name="boolout"> + <type> + <derived name="HMI_BOOL"/> + </type> + </variable> + <variable name="boolin"> + <type> + <derived name="HMI_BOOL"/> + </type> + <initialValue> + <simpleValue value="True"/> + </initialValue> + </variable> + <variable name="strout"> + <type> + <derived name="HMI_STRING"/> + </type> + </variable> + <variable name="strin"> + <type> + <derived name="HMI_STRING"/> + </type> + <initialValue> + <simpleValue value="blup"/> + </initialValue> + </variable> + </localVars> + </interface> + <body> + <FBD> + <inVariable localId="5" executionOrderId="0" height="30" width="125" negated="false"> + <position x="150" y="100"/> + <connectionPointOut> + <relPosition x="125" y="15"/> + </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"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="7" formalParameter="OUT"> + <position x="405" y="115"/> + <position x="360" y="115"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="1" executionOrderId="0" height="30" width="75" negated="false"> + <position x="150" y="135"/> + <connectionPointOut> + <relPosition x="75" y="15"/> + </connectionPointOut> + <expression>Pressure</expression> + </inVariable> + <block localId="7" typeName="SUB" executionOrderId="0" height="60" width="65"> + <position x="295" y="85"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="5"> + <position x="295" y="115"/> + <position x="275" y="115"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="1"> + <position x="295" y="135"/> + <position x="285" y="135"/> + <position x="285" y="150"/> + <position x="225" y="150"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="2" executionOrderId="0" height="30" width="60" negated="false"> + <position x="240" y="190"/> + <connectionPointOut> + <relPosition x="60" y="15"/> + </connectionPointOut> + <expression>Sloth</expression> + </inVariable> + <outVariable localId="3" executionOrderId="0" height="30" width="75" negated="false"> + <position x="435" y="205"/> + <connectionPointIn> + <relPosition x="0" y="15"/> + <connection refLocalId="8" formalParameter="OUT"> + <position x="435" y="220"/> + <position x="410" y="220"/> + </connection> + </connectionPointIn> + <expression>Pressure</expression> + </outVariable> + <block localId="8" typeName="DIV" executionOrderId="0" height="60" width="65"> + <position x="345" y="190"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="2"> + <position x="345" y="220"/> + <position x="335" y="220"/> + <position x="335" y="205"/> + <position x="300" y="205"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="9"> + <position x="345" y="240"/> + <position x="300" y="240"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="9" executionOrderId="0" height="30" width="60" negated="false"> + <position x="240" y="225"/> + <connectionPointOut> + <relPosition x="60" y="15"/> + </connectionPointOut> + <expression>100</expression> + </inVariable> + <block localId="10" typeName="CONCAT" executionOrderId="0" height="60" width="65"> + <position x="360" y="345"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="13" formalParameter="OUT"> + <position x="360" y="375"/> + <position x="330" y="375"/> + <position x="330" y="332"/> + <position x="440" y="332"/> + <position x="440" y="300"/> + <position x="430" y="300"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="14"> + <position x="360" y="395"/> + <position x="322" y="395"/> + <position x="322" y="400"/> + <position x="285" y="400"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <outVariable localId="11" executionOrderId="0" height="30" width="58" negated="false"> + <position x="495" y="355"/> + <connectionPointIn> + <relPosition x="0" y="15"/> + <connection refLocalId="10" formalParameter="OUT"> + <position x="495" y="370"/> + <position x="450" y="370"/> + <position x="450" y="375"/> + <position x="425" y="375"/> + </connection> + </connectionPointIn> + <expression>strout</expression> + </outVariable> + <inVariable localId="12" executionOrderId="0" height="30" width="125" negated="false"> + <position x="145" y="285"/> + <connectionPointOut> + <relPosition x="125" y="15"/> + </connectionPointOut> + <expression>TargetPressure</expression> + </inVariable> + <block localId="13" typeName="INT_TO_STRING" executionOrderId="0" height="40" width="115"> + <position x="315" y="270"/> + <inputVariables> + <variable formalParameter="IN"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="12"> + <position x="315" y="300"/> + <position x="270" y="300"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="115" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="14" executionOrderId="0" height="30" width="50" negated="false"> + <position x="235" y="385"/> + <connectionPointOut> + <relPosition x="50" y="15"/> + </connectionPointOut> + <expression>strin</expression> + </inVariable> + <inVariable localId="15" executionOrderId="0" height="30" width="60" negated="false"> + <position x="690" y="210"/> + <connectionPointOut> + <relPosition x="60" y="15"/> + </connectionPointOut> + <expression>boolin</expression> + </inVariable> + <outVariable localId="16" executionOrderId="0" height="30" width="70" negated="false"> + <position x="915" y="240"/> + <connectionPointIn> + <relPosition x="0" y="15"/> + <connection refLocalId="17" formalParameter="OUT"> + <position x="915" y="255"/> + <position x="880" y="255"/> + </connection> + </connectionPointIn> + <expression>boolout</expression> + </outVariable> + <block localId="17" typeName="AND" executionOrderId="0" height="60" width="65"> + <position x="815" y="225"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="15"> + <position x="815" y="255"/> + <position x="762" y="255"/> + <position x="762" y="225"/> + <position x="750" y="225"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="21" formalParameter="OUT"> + <position x="815" y="275"/> + <position x="750" y="275"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="18" executionOrderId="0" height="30" width="75" negated="false"> + <position x="455" y="260"/> + <connectionPointOut> + <relPosition x="75" y="15"/> + </connectionPointOut> + <expression>Pressure</expression> + </inVariable> + <block localId="19" typeName="MOD" executionOrderId="0" height="60" width="65"> + <position x="585" y="245"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="18"> + <position x="585" y="275"/> + <position x="530" y="275"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="20"> + <position x="585" y="295"/> + <position x="555" y="295"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="20" executionOrderId="0" height="30" width="20" negated="false"> + <position x="535" y="280"/> + <connectionPointOut> + <relPosition x="20" y="15"/> + </connectionPointOut> + <expression>2</expression> + </inVariable> + <block localId="21" typeName="EQ" executionOrderId="0" height="60" width="65"> + <position x="685" y="245"/> + <inputVariables> + <variable formalParameter="IN1"> + <connectionPointIn> + <relPosition x="0" y="30"/> + <connection refLocalId="19" formalParameter="OUT"> + <position x="685" y="275"/> + <position x="650" y="275"/> + </connection> + </connectionPointIn> + </variable> + <variable formalParameter="IN2"> + <connectionPointIn> + <relPosition x="0" y="50"/> + <connection refLocalId="22"> + <position x="685" y="295"/> + <position x="670" y="295"/> + <position x="670" y="330"/> + <position x="650" y="330"/> + </connection> + </connectionPointIn> + </variable> + </inputVariables> + <inOutVariables/> + <outputVariables> + <variable formalParameter="OUT"> + <connectionPointOut> + <relPosition x="65" y="30"/> + </connectionPointOut> + </variable> + </outputVariables> + </block> + <inVariable localId="22" executionOrderId="0" height="30" width="20" negated="false"> + <position x="630" y="315"/> + <connectionPointOut> + <relPosition x="20" y="15"/> + </connectionPointOut> + <expression>0</expression> + </inVariable> + </FBD> + </body> + </pou> + </pous> + </types> + <instances> + <configurations> + <configuration name="config"> + <resource name="resource1"> + <task name="task0" priority="0" interval="T#20ms"> + <pouInstance name="instance0" typeName="MainStuff"/> + </task> + </resource> + </configuration> + </configurations> + </instances> +</project> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/py_ext_0@py_ext/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/py_ext_0@py_ext/baseconfnode.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='utf-8'?> +<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="1" Name="py_ext_0"/> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/py_ext_0@py_ext/pyfile.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/py_ext_0@py_ext/pyfile.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,30 @@ +<?xml version='1.0' encoding='utf-8'?> +<PyFile xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> + <variables> + <variable name="SomePLCglobal" type="HMI_STRING" initial="'blaf'" onchange="MyOnChangeFunc"/> + </variables> + <globals> + <xhtml:p><![CDATA[ + +def MyOnChangeFunc(changed_var_name): + print changed_var_name + ": " + getattr(PLCGlobals, changed_var_name) + +]]></xhtml:p> + </globals> + <init> + <xhtml:p><![CDATA[ +]]></xhtml:p> + </init> + <cleanup> + <xhtml:p><![CDATA[ +]]></xhtml:p> + </cleanup> + <start> + <xhtml:p><![CDATA[ +]]></xhtml:p> + </start> + <stop> + <xhtml:p><![CDATA[ +]]></xhtml:p> + </stop> +</PyFile> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/svghmi_0@svghmi/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/svghmi_0@svghmi/baseconfnode.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='utf-8'?> +<BaseParams xmlns:xsd="http://www.w3.org/2001/XMLSchema" IEC_Channel="0" Name="svghmi_0"/> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/svghmi_0@svghmi/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/svghmi_0@svghmi/confnode.xml Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='utf-8'?> +<SVGHMI xmlns:xsd="http://www.w3.org/2001/XMLSchema" OnWatchdog="echo Watchdog for {name} !" OnStart="xdg-open http://127.0.0.1:{port}/{name}" OnStop="echo Closing {name}" WatchdogInitial="10" WatchdogInterval="5"/> diff -r 407a0205405a -r 0a9f6f29b7dd tests/svghmi_v2/svghmi_0@svghmi/svghmi.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/svghmi_v2/svghmi_0@svghmi/svghmi.svg Wed Aug 12 15:24:02 2020 +0200 @@ -0,0 +1,1525 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1280" + height="720" + viewBox="0 0 1280 720" + version="1.1" + id="hmi0" + sodipodi:docname="svghmi.svg" + inkscape:version="0.92.3 (2405546, 2018-03-11)" + inkscape:label="Layer"> + <metadata + id="metadata4542"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs2"> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-688.56326 : 510.71991 : 1" + inkscape:vp_y="0 : 1306.0642 : 0" + inkscape:vp_z="662.62627 : 323.72015 : 1" + inkscape:persp3d-origin="147.31778 : 353.99223 : 1" + id="perspective258" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-457.78124 : 416.79285 : 1" + inkscape:vp_y="0 : 1000 : 0" + inkscape:vp_z="576.76945 : 273.61475 : 1" + inkscape:persp3d-origin="182.21876 : 296.79285 : 1" + id="perspective503" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-104 : 357 : 1" + inkscape:vp_y="0 : 1000 : 0" + 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"> + <stop + style="stop-color:#ff3000;stop-opacity:1;" + offset="0" + id="stop958" /> + <stop + style="stop-color:#0022ff;stop-opacity:1" + offset="1" + id="stop960" /> + </linearGradient> + <marker + inkscape:isstock="true" + style="overflow:visible" + id="marker926" + refX="0" + refY="0" + orient="auto" + inkscape:stockid="Arrow2Lend"> + <path + transform="matrix(-1.1,0,0,-1.1,-1.1,0)" + d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z" + style="fill:#ff3000;fill-opacity:1;fill-rule:evenodd;stroke:#ff3000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1" + 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" + id="linearGradient1407" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.5,0,0,0.03945396,73.07865,3.7693345)" + x1="113.38908" + y1="-62.210247" + x2="113.38908" + y2="4.0725975" /> + <inkscape:perspective + sodipodi:type="inkscape:persp3d" + inkscape:vp_x="-173.06414 : 591.30354 : 1" + inkscape:vp_y="0 : 1319.7648 : 0" + inkscape:vp_z="1192.2994 : 402.34211 : 1" + inkscape:persp3d-origin="671.58536 : 432.93175 : 1" + id="perspective503-6" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:document-units="px" + inkscape:current-layer="g110-0-9" + 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:window-maximized="1" + showguides="true" + inkscape:guide-bbox="true" /> + <rect + style="color:#000000;fill:#4d4d4d" + id="page0" + width="1280" + height="720" + x="0" + y="0" + inkscape:label="HMI:Page:Home" + sodipodi:insensitive="true" /> + <g + inkscape:label="HMI:Slider@/PUMP0/SLOTH" + transform="matrix(7.5590552,0,0,7.5590552,-710.78539,551.61779)" + id="g110-0"> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-width:0.52916664;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 113.38908,2.2017068 V -62.210247" + id="path90-9" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" + inkscape:label="range" /> + <path + inkscape:label="handle" + sodipodi:nodetypes="cc" + inkscape:connector-curvature="0" + id="path92-3" + d="m 113.32293,4.2048893 v -5.230241" + style="fill:none;fill-rule:evenodd;stroke:url(#linearGradient1407);stroke-width:5.28146696;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:29.63333321;stroke-opacity:1" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:5.29166651px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="115.07632" + y="9.3424692" + id="text96-6" + inkscape:label="min"><tspan + sodipodi:role="line" + id="tspan94-0" + x="115.07632" + y="9.3424692" + style="text-align:end;text-anchor:end;fill:#ff6600;stroke-width:0.26458332px">0</tspan></text> + <text + id="text100-6" + y="-64.195457" + x="113.27539" + style="font-style:normal;font-weight:normal;font-size:5.29166651px;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" + xml:space="preserve" + inkscape:label="max"><tspan + style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.26458332px" + y="-64.195457" + x="113.27539" + sodipodi:role="line" + id="tspan1409">100</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" + x="-24.72547" + y="-121.97556" + id="text104-6" + inkscape:label="value" + transform="rotate(90)"><tspan + sodipodi:role="line" + x="-24.72547" + y="-121.97556" + style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.26458332px" + id="tspan102-1">000</tspan></text> + </g> + <g + id="g4557" + inkscape:label="HMI:Input@/SOMEPLCGLOBAL"> + <text + inkscape:label="value" + transform="scale(1.1201068,0.89277203)" + id="text2398" + y="446.98395" + x="347.5253" + style="font-style:normal;font-weight:normal;font-size:124.08008575px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3.10200214px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="stroke-width:3.10200214px" + y="446.98395" + x="347.5253" + id="tspan2396" + sodipodi:role="line">Test</tspan></text> + <rect + style="opacity:0.18600003;fill:#de2cc9;fill-opacity:1;stroke:none;stroke-width:1.11699021" + id="rect4559" + width="323.85489" + height="132.93608" + x="369.10974" + y="299.97858" + inkscape:label="edit" /> + <rect + style="opacity:0;fill:#de2cc9;fill-opacity:1;stroke:none;stroke-width:3.45667744" + id="rect4561" + width="580.42413" + height="339.91623" + x="699.57587" + y="380.08374" + inkscape:label="key_pos" /> + </g> + <g + transform="matrix(1.5213157,0,0,1.4848913,-82.472173,789.79964)" + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4278" + inkscape:label="HMI:Keypad:HMI_STRING"> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.6;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.16776976;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="M 54.211084,1.2654702 H 435.7388 V 230.18209 H 54.211084 Z" + id="rect1006-3" + inkscape:connector-curvature="0" + inkscape:label="Background" + sodipodi:nodetypes="ccccc" /> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path185" + d="m 162,197 h -11 c -2,0 -3,1 -3,3 v 18 c 0,2 1,3 3,3 h 11 168 18 c 0,0 1,-1 1,-3 v -18 c 0,-2 -1,-3 -1,-3 h -18 z" + inkscape:connector-curvature="0" + inkscape:label="Space" /> + <g + id="g4380" + inkscape:label="Keys" + style="stroke-width:0.47631353" + transform="translate(0,-19.076386)"> + <g + id="g4283" + inkscape:label="q Q" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path41" + d="m 95,121 h 19 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 H 95 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + inkscape:connector-curvature="0" /> + <text + x="99.378708" + y="138.28395" + id="text203" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">Q</text> + </g> + <g + id="g4337" + inkscape:label="w W" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path43" + d="m 124,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + inkscape:connector-curvature="0" /> + <text + x="127.0709" + y="138.28395" + id="text207" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">W</text> + </g> + <g + id="g4332" + inkscape:label="e E" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path45" + d="m 154,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + inkscape:connector-curvature="0" /> + <text + x="159.70854" + y="138.28395" + id="text211" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">E</text> + </g> + <g + id="g4326" + inkscape:label="r R" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path47" + d="m 184,121 h 19 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -19 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + inkscape:connector-curvature="0" /> + <text + x="188.39003" + y="138.28395" + id="text215" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">R</text> + </g> + <g + id="g4321" + inkscape:label="t T" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path49" + d="m 213,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="219.04961" + y="138.28395" + id="text219" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">T</text> + </g> + <g + id="g4316" + inkscape:label="y Y" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path51" + d="m 243,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="248.72017" + y="138.28395" + id="text223" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">Y</text> + </g> + <g + id="g4311" + inkscape:label="u U" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path53" + d="m 273,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="278.39075" + y="138.28395" + id="text227" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">U</text> + </g> + <g + id="g4306" + inkscape:label="i I" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path55" + d="m 302,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="311.02859" + y="138.28395" + id="text231" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">I</text> + </g> + <g + id="g4301" + inkscape:label="o O" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path57" + d="m 332,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="336.74319" + y="138.28395" + id="text235" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">O</text> + </g> + <g + id="g4296" + inkscape:label="p P" + style="stroke-width:0.47631353" + transform="translate(0,-9.5381931)"> + <path + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path59" + d="m 362,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 v -18 c 0,-2 1,-3 2,-3 z" + inkscape:connector-curvature="0" /> + <text + x="367.40256" + y="138.28395" + id="text239" + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928514)">P</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4511" + inkscape:label="a A"> + <path + inkscape:connector-curvature="0" + d="m 103,147 h 19 c 1,0 3,1 3,2 v 19 c 0,1 -2,2 -3,2 h -19 c -2,0 -3,-1 -3,-2 v -19 c 0,-1 1,-2 3,-2 z" + id="path65" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text243" + y="163.99854" + x="107.29005" + transform="scale(1.0007154,0.99928514)">A</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4516" + inkscape:label="s S"> + <path + inkscape:connector-curvature="0" + d="m 132,147 h 20 c 1,0 3,1 3,2 v 19 c 0,1 -2,2 -3,2 h -20 c -2,0 -3,-1 -3,-2 v -19 c 0,-1 1,-2 3,-2 z" + id="path67" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text247" + y="163.99854" + x="137.95012" + transform="scale(1.0007154,0.99928514)">S</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4521" + inkscape:label="d D"> + <path + inkscape:connector-curvature="0" + d="m 162,147 h 20 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -20 c -2,0 -3,-1 -3,-2 v -19 c 0,-1 1,-2 3,-2 z" + id="path69" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text251" + y="163.99854" + x="166.63159" + transform="scale(1.0007154,0.99928514)">D</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4526" + inkscape:label="f F"> + <path + inkscape:connector-curvature="0" + d="m 192,147 h 19 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -19 c -2,0 -3,-1 -3,-2 v -19 c 0,-1 1,-2 3,-2 z" + id="path71" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text255" + y="163.99854" + x="197.29166" + transform="scale(1.0007154,0.99928514)">F</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4531" + inkscape:label="g G"> + <path + inkscape:connector-curvature="0" + d="m 221,147 h 20 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -20 c -2,0 -3,-1 -3,-2 v -19 c 0,-1 1,-2 3,-2 z" + id="path73" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text259" + y="163.99854" + x="225.97284" + transform="scale(1.0007154,0.99928514)">G</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4536" + inkscape:label="h H"> + <path + inkscape:connector-curvature="0" + d="m 251,147 h 20 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -20 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 z" + id="path75" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text263" + y="163.99854" + x="255.64342" + transform="scale(1.0007154,0.99928514)">H</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4541" + inkscape:label="j J"> + <path + inkscape:connector-curvature="0" + d="m 281,147 h 19 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -19 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 z" + id="path77" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text267" + y="163.99854" + x="287.29208" + transform="scale(1.0007154,0.99928514)">J</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4546" + inkscape:label="k K"> + <path + inkscape:connector-curvature="0" + d="m 310,147 h 20 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -20 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 z" + id="path79" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text271" + y="163.99854" + x="314.98465" + transform="scale(1.0007154,0.99928514)">K</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4551" + inkscape:label="l L"> + <path + inkscape:connector-curvature="0" + d="m 340,147 h 20 c 2,0 3,1 3,2 v 19 c 0,1 -1,2 -3,2 h -20 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 z" + id="path81" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text275" + y="163.99854" + x="345.64444" + transform="scale(1.0007154,0.99928514)">L</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4586" + inkscape:label="z Z" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 113,172 h 21 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -21 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 z" + id="path87-3" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text279" + y="188.72411" + x="119.15855" + transform="scale(1.0007154,0.99928514)">Z</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4581" + inkscape:label="x X" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 143,172 h 21 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -21 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 z" + id="path89-6" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text283" + y="188.72411" + x="148.82933" + transform="scale(1.0007154,0.99928514)">X</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4576" + inkscape:label="c C" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 173,172 h 21 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -21 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 z" + id="path91-7" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text287" + y="188.72411" + x="178.50011" + transform="scale(1.0007154,0.99928514)">C</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4571" + inkscape:label="v V" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 202,172 h 21 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -21 c 0,0 -1,-1 -1,-3 v -17 c 0,-1 1,-3 1,-3 z" + id="path195" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text291" + y="188.72411" + x="208.16988" + transform="scale(1.0007154,0.99928514)">V</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4566" + inkscape:label="b B" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 233,172 h 20 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -20 c -2,0 -3,-1 -3,-3 v -17 c 0,-1 1,-3 3,-3 z" + id="path93" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text295" + y="188.72411" + x="237.84096" + transform="scale(1.0007154,0.99928514)">B</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4561" + inkscape:label="n N" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 263,172 h 20 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -20 c -2,0 -3,-1 -3,-3 v -17 c 0,-1 1,-3 3,-3 z" + id="path95" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text299" + y="188.72411" + x="267.51193" + transform="scale(1.0007154,0.99928514)">N</text> + </g> + <g + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4556" + inkscape:label="m M" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 293,172 h 19 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -19 c -2,0 -3,-1 -3,-3 v -17 c 0,-1 1,-3 3,-3 z" + id="path97" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text303" + y="188.72411" + x="296.1933" + transform="scale(1.0007154,0.99928514)">M</text> + </g> + <g + id="g4818" + inkscape:label=". :" + style="stroke-width:0.47631353" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 352,172 h 20 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -20 c -2,0 -3,-1 -3,-3 v -17 c 0,-1 1,-3 3,-3 z" + id="path101" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + transform="scale(1.0007154,0.99928513)" + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + id="text719" + y="189.66107" + x="359.58276">.</text> + <text + x="359.58276" + y="181.64532" + id="text4834" + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928512)">:</text> + </g> + <g + id="g4813" + inkscape:label=", ;" + style="stroke-width:0.47631353" + transform="translate(0,9.5381929)"> + <path + inkscape:connector-curvature="0" + d="m 322,172 h 20 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 h -20 c -2,0 -3,-1 -3,-3 v -17 c 0,-1 1,-3 3,-3 z" + id="path99" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + id="text727" + y="181.64532" + x="330.00806" + transform="scale(1.0007154,0.99928512)">;</text> + <text + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + y="189.66107" + x="330.00806" + transform="scale(1.0007154,0.99928512)" + id="text4826">,</text> + </g> + <g + style="stroke-width:0.47631353" + inkscape:label="1" + id="g2845" + transform="translate(-13.353469,-45.783327)"> + <path + inkscape:connector-curvature="0" + d="m 95,121 h 19 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 H 95 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + id="path2839" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2841" + y="138.28395" + x="101.07153" + transform="scale(1.0007154,0.99928513)">1</text> + </g> + <g + style="stroke-width:0.47631353" + inkscape:label="2" + id="g2853" + transform="translate(-13.353469,-45.783327)"> + <path + inkscape:connector-curvature="0" + d="m 124,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + id="path2847" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2849" + y="138.28395" + x="130.18704" + transform="scale(1.0007154,0.99928513)">2</text> + </g> + <g + inkscape:label="3" + id="g2861" + style="stroke-width:0.47631353" + transform="translate(-13.353469,-45.783327)"> + <path + inkscape:connector-curvature="0" + d="m 154,121 h 20 c 2,0 3,1 3,3 v 18 c 0,1 -1,3 -3,3 h -20 c -1,0 -3,-2 -3,-3 v -18 c 0,-2 2,-3 3,-3 z" + id="path2855" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2857" + y="138.28395" + x="159.70854" + transform="scale(1.0007154,0.99928514)">3</text> + </g> + <g + id="g2957" + inkscape:label="4" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 170.64653,94.293059 h 19 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -19 c -1,0 -3,-2 -3,-3 V 97.293059 c 0,-2 2,-3 3,-3 z" + id="path2865" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2867" + y="111.55791" + x="176.39188" + transform="scale(1.0007154,0.99928514)">4</text> + </g> + <g + id="g2962" + inkscape:label="5" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 199.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2873" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2875" + y="111.55791" + x="205.70567" + transform="scale(1.0007154,0.99928514)">5</text> + </g> + <g + id="g2967" + inkscape:label="6" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 229.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2881" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2883" + y="111.55791" + x="236.15851" + transform="scale(1.0007154,0.99928514)">6</text> + </g> + <g + id="g2972" + inkscape:label="7" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 259.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2889" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2891" + y="111.55791" + x="266.06564" + transform="scale(1.0007154,0.99928514)">7</text> + </g> + <g + id="g2977" + inkscape:label="8" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 288.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2897" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2899" + y="111.55791" + x="295.08231" + transform="scale(1.0007154,0.99928514)">8</text> + </g> + <g + id="g2982" + inkscape:label="9 -" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 318.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2905" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2907" + y="111.55791" + x="325.05408" + transform="scale(1.0007154,0.99928514)">9</text> + <text + transform="scale(1.0007154,0.99928511)" + x="335.72681" + y="102.42173" + id="text806" + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826">-</text> + </g> + <g + id="g2987" + inkscape:label="0 +" + transform="translate(0,-19.076386)"> + <path + inkscape:connector-curvature="0" + d="m 348.64653,94.293059 h 20 c 2,0 3,1 3,3 v 18.000001 c 0,1 -1,3 -3,3 h -20 c -1,0 -2,-2 -2,-3 V 97.293059 c 0,-2 1,-3 2,-3 z" + id="path2913" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" /> + <text + style="font-weight:normal;font-size:13.93205929px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text2915" + y="111.55791" + x="355.05984" + transform="scale(1.0007154,0.99928514)">0</text> + <text + transform="scale(1.0007154,0.99928511)" + style="font-weight:normal;font-size:9.28803921px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + id="text804" + y="102.42173" + x="365.30151">+</text> + </g> + </g> + <g + transform="translate(335.89988,-58.934803)" + id="g3544" + inkscape:label="Esc" + style="stroke-width:0.47631353"> + <path + style="opacity:1;vector-effect:none;fill:#4f4c4d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.16824313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path105" + d="m 47.948645,115.07509 h 39.076386 c 1,0 3,1 3,3 v 18 c 0,1 -2,3 -3,3 H 47.948645 c -2,0 -3,-2 -3,-3 v -18 c 0,-2 1,-3 3,-3 z" + inkscape:connector-curvature="0" + sodipodi:nodetypes="sssssssss" /> + <text + transform="scale(1.0007154,0.99928512)" + style="font-weight:normal;font-size:9.37966251px;font-family:Arial;fill:#ffffff;fill-rule:evenodd;stroke-width:0.36866826" + id="text469" + y="130.02028" + x="59.288635">Esc</text> + </g> + <g + inkscape:label="Enter" + id="g4291" + style="stroke-width:0.47631353" + transform="translate(0,-19.076386)"> + <path + sodipodi:nodetypes="sssssssss" + style="opacity:1;vector-effect:none;fill:#4f4c4d;fill-opacity:1;stroke:none;stroke-width:0.16824313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3616" + d="m 368.68274,170 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 h 54.24217 c 2,0 3,2 3,3 v 17 c 0,2 -1,3 -3,3 z" + inkscape:connector-curvature="0" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -260.23633,1080.8125 v 15.7949 h -38.68555 v -3 l -6.91992,4 6.91992,4 v -3.0019 h 40.6836 v -17.793 z" + transform="matrix(0.47690966,0,0,0.47690966,531.12074,-361.18588)" + id="path6545" + inkscape:connector-curvature="0" /> + </g> + <g + inkscape:label="BackSpace" + id="g4287" + style="fill-rule:evenodd;stroke-width:0.47631353" + transform="translate(2.3648311e-6,-28.614579)"> + <path + sodipodi:nodetypes="sssssssss" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path3624" + d="m 391.97749,144 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 h 30.94742 c 2,0 3,2 3,3 v 17 c 0,2 -1,3 -3,3 z" + inkscape:connector-curvature="0" /> + <path + style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#2b2828;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m -268.72656,1011.1777 -6.91992,4 6.91992,4 v -3.0019 h 29.18945 v -1.9981 h -29.18945 z" + transform="matrix(0.47690966,0,0,0.47690966,531.12074,-351.64769)" + id="path11623-1-0" + inkscape:connector-curvature="0" /> + </g> + <g + id="g934" + inkscape:label="CapsLock"> + <g + inkscape:label="inactive" + id="g942" + style="display:inline;fill-rule:evenodd;stroke-width:0.47631353" + transform="translate(0,-19.076386)"> + <path + sodipodi:nodetypes="sssssssss" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path936-3" + d="m 67.025031,170 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 H 92 c 2,0 4,1 4,2 v 19 c 0,1 -2,2 -4,2 z" + inkscape:connector-curvature="0" /> + <text + x="69.789322" + y="156.71973" + id="text938-5" + style="font-weight:normal;font-size:8.66233635px;font-family:Arial;fill:#2b2828;stroke-width:0.36866823" + transform="scale(1.0007154,0.99928515)">Caps</text> + <text + x="69.789322" + y="166.5585" + id="text940" + style="font-weight:normal;font-size:8.66233635px;font-family:Arial;fill:#2b2828;stroke-width:0.36866823" + transform="scale(1.0007154,0.99928515)">Lock</text> + </g> + <g + transform="translate(0,-19.076386)" + style="fill-rule:evenodd;stroke-width:0.47631353" + id="g4429" + inkscape:label="active"> + <path + inkscape:connector-curvature="0" + d="m 67.025031,170 c -1,0 -3,-1 -3,-2 v -19 c 0,-1 2,-2 3,-2 H 92 c 2,0 4,1 4,2 v 19 c 0,1 -2,2 -4,2 z" + id="path199" + style="opacity:1;vector-effect:none;fill:#4f4c4d;fill-opacity:1;stroke:none;stroke-width:0.16824313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:nodetypes="sssssssss" /> + <text + transform="scale(1.0007154,0.99928515)" + style="font-weight:normal;font-size:8.66233635px;font-family:Arial;fill:#ffffff;stroke-width:0.36866823" + id="text647" + y="156.71973" + x="69.789322">Caps</text> + <text + transform="scale(1.0007154,0.99928515)" + style="font-weight:normal;font-size:8.66233635px;font-family:Arial;fill:#ffffff;stroke-width:0.36866823" + id="text651" + y="166.5585" + x="69.789322">Lock</text> + </g> + </g> + <rect + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#fffff5;fill-opacity:1;fill-rule:nonzero;stroke:#202326;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect2130" + width="361.89996" + height="30.150299" + x="64.024956" + y="15.771065" + rx="3.8152773" + ry="3.8152773" + inkscape:label="Field" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:19.0763855px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.47690967px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="72.50132" + y="38.296417" + id="text1309" + inkscape:label="Value"><tspan + sodipodi:role="line" + id="tspan1307" + x="72.50132" + y="38.296417" + style="text-align:start;text-anchor:start;stroke-width:0.47690967px">text</tspan></text> + <g + id="g437" + inkscape:label="Shift"> + <g + id="g421" + inkscape:label="inactive"> + <path + inkscape:connector-curvature="0" + d="m 379.96247,185.46181 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 h 42.96244 c 2,0 3,2 3,3 v 17 c 0,2 -1,3 -3,3 z" + id="path910" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:nodetypes="sssssssss" /> + <text + style="font-weight:normal;font-size:8.92098808px;font-family:Arial;fill:#2b2828;stroke-width:0.36866826" + id="text912" + y="177.90059" + x="392.55679" + transform="scale(1.0007154,0.99928513)">Shift</text> + <path + sodipodi:nodetypes="sssssssss" + style="opacity:1;vector-effect:none;fill:#d3d2d2;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.16824308;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path856" + d="m 67.025031,185.46181 c -1,0 -3,-1 -3,-3 v -17 c 0,-1 2,-3 3,-3 H 104 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 z" + inkscape:connector-curvature="0" /> + <text + x="75.85218" + y="177.90059" + id="text858" + style="font-weight:normal;font-size:8.92098808px;font-family:Arial;fill:#2b2828;fill-rule:evenodd;stroke-width:0.36866826" + transform="scale(1.0007154,0.99928513)">Shift</text> + </g> + <g + id="g413" + inkscape:label="active"> + <path + sodipodi:nodetypes="sssssssss" + style="opacity:1;vector-effect:none;fill:#4f4c4d;fill-opacity:1;stroke:none;stroke-width:0.16824313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + id="path551" + d="m 379.96247,185.46181 c -1,0 -2,-1 -2,-3 v -17 c 0,-1 1,-3 2,-3 h 42.96244 c 2,0 3,2 3,3 v 17 c 0,2 -1,3 -3,3 z" + inkscape:connector-curvature="0" /> + <text + transform="scale(1.0007154,0.99928513)" + x="392.55679" + y="177.90059" + id="text629" + style="font-weight:normal;font-size:8.92098808px;font-family:Arial;fill:#ffffff;stroke-width:0.36866826">Shift</text> + <path + inkscape:connector-curvature="0" + d="m 67.025031,185.46181 c -1,0 -3,-1 -3,-3 v -17 c 0,-1 2,-3 3,-3 H 104 c 1,0 2,2 2,3 v 17 c 0,2 -1,3 -2,3 z" + id="path879" + style="opacity:1;vector-effect:none;fill:#4f4c4d;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.16824313;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" + sodipodi:nodetypes="sssssssss" /> + <text + transform="scale(1.0007154,0.99928513)" + style="font-weight:normal;font-size:8.92098808px;font-family:Arial;fill:#ffffff;fill-rule:evenodd;stroke-width:0.36866826" + id="text881" + y="177.90059" + x="75.85218">Shift</text> + </g> + </g> + <text + transform="scale(0.96824588,1.0327955)" + id="text471" + y="12.333657" + x="252.9579" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.31375408px;line-height:125%;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.30784383px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve" + inkscape:label="Info"><tspan + style="stroke-width:0.30784383px" + y="12.333657" + x="252.9579" + id="tspan469" + sodipodi:role="line">information</tspan></text> + <rect + style="opacity:0.18600003;fill:#de2cc9;fill-opacity:1;stroke:none;stroke-width:0.31677353" + id="rect4563" + width="381.45959" + height="14.110301" + x="54.211086" + y="1.2654642" + inkscape:label="position" /> + </g> + <g + inkscape:label="HMI:Slider@/PUMP0/SLOTH" + transform="matrix(7.5590552,0,0,7.5590552,-248.554,584.0829)" + id="g110-0-9"> + <g + inkscape:label="setpoint" + style="opacity:0.5;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:0.76565915" + inkscape:corner7="-0.15304809 : -0.15652183 : 0.051043755 : 1" + inkscape:corner0="-0.13109479 : -0.13697746 : 0 : 1" + inkscape:perspectiveID="#perspective258" + id="g256" + sodipodi:type="inkscape:box3d"> + <path + style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="69.054145,5.4029493 71.910946,3.7246414 71.910946,0.053890203 69.054145,1.5165601 " + d="M 69.054145,1.5165601 V 5.4029493 L 71.910946,3.7246414 V 0.0538902 Z" + inkscape:box3dsidetype="6" + id="path244" + sodipodi:type="inkscape:box3dside" /> + <path + style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="72.352867,6.8282124 75.092002,5.0278603 71.910946,3.7246414 69.054145,5.4029493 " + d="M 69.054145,5.4029493 72.352867,6.8282124 75.092002,5.0278603 71.910946,3.7246414 Z" + inkscape:box3dsidetype="13" + id="path246" + sodipodi:type="inkscape:box3dside" /> + <path + style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="75.092002,1.2673703 75.092002,5.0278603 71.910946,3.7246414 71.910946,0.053890203 " + d="m 71.910946,0.0538902 3.181056,1.2134801 v 3.76049 L 71.910946,3.7246414 Z" + inkscape:box3dsidetype="11" + id="path248" + sodipodi:type="inkscape:box3dside" /> + <path + style="fill:#4d4d9f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="72.352867,2.8410867 75.092002,1.2673703 71.910946,0.053890203 69.054145,1.5165601 " + d="M 69.054145,1.5165601 72.352867,2.8410867 75.092002,1.2673703 71.910946,0.0538902 Z" + inkscape:box3dsidetype="5" + id="path250" + sodipodi:type="inkscape:box3dside" /> + <path + style="fill:#d7d7ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="72.352867,6.8282124 75.092002,5.0278603 75.092002,1.2673703 72.352867,2.8410867 " + d="m 72.352867,2.8410867 v 3.9871257 l 2.739135,-1.8003521 v -3.76049 z" + inkscape:box3dsidetype="14" + id="path252" + sodipodi:type="inkscape:box3dside" /> + <path + style="fill:#8686bf;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" + points="72.352867,2.8410867 72.352867,6.8282124 69.054145,5.4029493 69.054145,1.5165601 " + d="m 69.054145,1.5165601 3.298722,1.3245266 V 6.8282124 L 69.054145,5.4029493 Z" + inkscape:box3dsidetype="3" + id="path254" + sodipodi:type="inkscape:box3dside" /> + </g> + <path + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:#ff0000;stroke-width:0.52375954;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + d="m 71.94894,3.6581855 79.3256,0.040092" + id="path90-9-3" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cc" + inkscape:label="range" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:5.29166651px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="68.771873" + y="5.501111" + id="text96-6-0" + inkscape:label="min"><tspan + sodipodi:role="line" + id="tspan94-0-62" + x="68.771873" + y="5.501111" + style="text-align:end;text-anchor:end;fill:#ff6600;stroke-width:0.26458332px">0</tspan></text> + <text + id="text100-6-6" + y="5.501111" + x="159.67337" + style="font-style:normal;font-weight:normal;font-size:5.29166651px;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" + xml:space="preserve" + inkscape:label="max"><tspan + style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.26458332px" + y="5.501111" + x="159.67337" + sodipodi:role="line" + id="tspan1409-1">100</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" + x="-115.32294" + y="-9.0188799" + id="text104-6-8" + inkscape:label="value" + transform="scale(-1)"><tspan + sodipodi:role="line" + x="-115.32294" + y="-9.0188799" + style="text-align:center;text-anchor:middle;fill:#ff6600;stroke-width:0.19461991px" + id="tspan102-1-7">000</tspan></text> + <g + sodipodi:type="inkscape:box3d" + id="g930" + inkscape:perspectiveID="#perspective503" + inkscape:corner0="-0.13109479 : -0.13697746 : 0 : 1" + inkscape:corner7="-0.15304809 : -0.15652183 : 0.051043755 : 1" + style="fill:#ff0000;fill-opacity:1;stroke:none" + inkscape:label="handle" + transform="translate(0.01,0.01)"> + <path + sodipodi:type="inkscape:box3dside" + id="path932" + inkscape:box3dsidetype="6" + d="M 69.751604,1.9575481 V 4.9331975 L 71.93894,3.6481857 V 0.8376415 Z" + points="69.751604,4.9331975 71.93894,3.6481857 71.93894,0.8376415 69.751604,1.9575481 " + style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path940" + inkscape:box3dsidetype="13" + d="M 69.751604,4.9331975 72.2773,6.0244633 74.374544,4.6460073 71.93894,3.6481857 Z" + points="72.2773,6.0244633 74.374544,4.6460073 71.93894,3.6481857 69.751604,4.9331975 " + style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path942" + inkscape:box3dsidetype="11" + d="m 71.93894,0.8376415 2.435604,0.9291122 V 4.6460073 L 71.93894,3.6481857 Z" + points="74.374544,1.7667537 74.374544,4.6460073 71.93894,3.6481857 71.93894,0.8376415 " + style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path934" + inkscape:box3dsidetype="5" + d="M 69.751604,1.9575481 72.2773,2.971684 74.374544,1.7667537 71.93894,0.8376415 Z" + points="72.2773,2.971684 74.374544,1.7667537 71.93894,0.8376415 69.751604,1.9575481 " + style="fill:#4d4d9f;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path938" + inkscape:box3dsidetype="14" + d="m 72.2773,2.971684 v 3.0527793 l 2.097244,-1.378456 V 1.7667537 Z" + points="72.2773,6.0244633 74.374544,4.6460073 74.374544,1.7667537 72.2773,2.971684 " + style="fill:#d7d7ff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path936" + inkscape:box3dsidetype="3" + d="M 69.751604,1.9575481 72.2773,2.971684 V 6.0244633 L 69.751604,4.9331975 Z" + points="72.2773,2.971684 72.2773,6.0244633 69.751604,4.9331975 69.751604,1.9575481 " + style="fill:#8686bf;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + </g> + </g> + <g + id="g1292-3" + inkscape:label="HMI:Input@/RADIOSTATE" + transform="matrix(0.94144976,0,0,1.7212489,176.35468,-2117.077)"> + <g + id="g2530" + inkscape:label="=3"> + <rect + style="display:inline;fill:#0009ff;fill-opacity:1;stroke:none;stroke-width:0.24259248" + id="rect1273-6-3" + width="57.391823" + height="24.148804" + x="230.03636" + y="1238.2637" + inkscape:label="3" /> + <g + transform="translate(-213.152,55.750293)" + id="g2520-5" + inkscape:label="HMI:Switch@/RADIOSTATE" + style="fill:#0009ff;fill-opacity:1"> + <rect + inkscape:label="3" + y="1206.6622" + x="443.18835" + height="24.148754" + width="57.39183" + id="rect1273-6-9-9-9" + style="display:inline;fill:#0009ff;fill-opacity:1;stroke:none;stroke-width:0.24259226" /> + </g> + </g> + <g + id="g2527" + inkscape:label="=2"> + <rect + style="display:inline;fill:#00ffed;fill-opacity:1;stroke:none;stroke-width:0.24259254" + id="rect1273-6-56" + width="57.391857" + height="24.148804" + x="313.84549" + y="1238.2637" + inkscape:label="2" /> + <g + transform="translate(-303.62283,32.70105)" + id="g2520-2" + inkscape:label="HMI:Switch@/RADIOSTATE"> + <rect + inkscape:label="2" + y="1229.7114" + x="617.46832" + height="24.148754" + width="57.39183" + id="rect1273-6-9-9-0" + style="display:inline;fill:#00ffed;fill-opacity:1;stroke:none;stroke-width:0.24259226" /> + </g> + </g> + <g + id="g2524" + inkscape:label="=1"> + <rect + style="display:inline;fill:#3eff00;fill-opacity:1;stroke:none;stroke-width:0.24182089" + id="rect1273-6-2" + width="57.027344" + height="24.148796" + x="146.22725" + y="1238.2637" + inkscape:label="1" /> + <g + transform="translate(-213.152,55.750293)" + id="g2520-23" + inkscape:label="HMI:Switch@/RADIOSTATE"> + <rect + inkscape:label="1" + y="1206.6622" + x="359.37924" + height="24.148754" + width="57.39183" + id="rect1273-6-9-9-7" + style="display:inline;fill:#3eff00;fill-opacity:1;stroke:none;stroke-width:0.24259226" /> + </g> + </g> + <g + id="g2501" + inkscape:label="=0" + transform="translate(-260.62575)"> + <rect + inkscape:label="0" + y="1238.2637" + x="323.04385" + height="24.148754" + width="57.39183" + id="rect1273-6-9" + style="display:inline;fill:#ffea00;fill-opacity:1;stroke:none;stroke-width:0.24259225" /> + <g + id="g2520" + inkscape:label="HMI:Switch@/RADIOSTATE"> + <rect + inkscape:label="0" + y="1262.4125" + x="323.04385" + height="24.148754" + width="57.39183" + id="rect1273-6-9-9" + style="display:inline;fill:#ffea00;fill-opacity:1;stroke:none;stroke-width:0.24259226" /> + </g> + </g> + </g> + <g + id="g1047" + inkscape:label="HMI:CircularBar@/PUMP0/SLOTH" + transform="matrix(0.39840034,0,0,0.35920948,224.04409,96.134885)"> + <path + inkscape:label="range" + sodipodi:open="true" + d="M 1079.626,411.60913 A 184.25998,167.44942 0 0 1 874.51345,308.78336 184.25998,167.44942 0 0 1 946.20137,106.11681 184.25998,167.44942 0 0 1 1178.8257,131.16507" + sodipodi:end="5.5191826" + sodipodi:start="1.3860423" + sodipodi:ry="167.44942" + sodipodi:rx="184.25998" + sodipodi:cy="247.00946" + sodipodi:cx="1045.7766" + sodipodi:type="arc" + id="path1044" + style="opacity:1;fill:none;fill-opacity:1;stroke:#fe00dc;stroke-width:22.07197189;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:90.1384964px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff00ca;fill-opacity:1;stroke:none;stroke-width:2.25346255px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="1046.8701" + y="258.16129" + id="text1051" + transform="scale(0.91814752,1.0891496)" + inkscape:label="value"><tspan + sodipodi:role="line" + id="tspan1049" + x="1046.8701" + y="258.16129" + style="fill:#ff00ca;fill-opacity:1;stroke:none;stroke-width:2.25346255px;stroke-opacity:1">000</tspan></text> + <path + inkscape:label="path" + sodipodi:open="true" + d="M 1083.68,410.87778 A 184.25998,167.44942 0 0 1 875.42544,310.83196 184.25998,167.44942 0 0 1 945.58759,106.47662 184.25998,167.44942 0 0 1 1179.4956,131.8038" + sodipodi:end="5.524452" + sodipodi:start="1.3636114" + sodipodi:ry="167.44942" + sodipodi:rx="184.25998" + sodipodi:cy="247.00946" + sodipodi:cx="1045.7766" + sodipodi:type="arc" + id="path1044-3" + style="opacity:1;fill:none;fill-opacity:1;stroke:#00fff1;stroke-width:40;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" /> + </g> + <g + id="g1047-6" + inkscape:label="HMI:CircularSlider@/PUMP0/SLOTH" + transform="matrix(0.45707797,0,0,0.45707797,330.74411,340.99474)"> + <path + inkscape:label="range" + d="M 970.29569,399.76446 A 184.25998,167.44942 0 0 1 866.26395,284.77467 184.25998,167.44942 0 0 1 904.10823,139.93753" + sodipodi:end="3.8353474" + sodipodi:start="1.9928597" + sodipodi:ry="167.44942" + sodipodi:rx="184.25998" + sodipodi:cy="247.00946" + sodipodi:cx="1045.7766" + sodipodi:type="arc" + id="path1044-7" + style="opacity:1;fill:none;fill-opacity:1;stroke:#fe00dc;stroke-width:22.07197189;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + sodipodi:open="true" /> + <g + sodipodi:type="inkscape:box3d" + id="g930-2" + inkscape:perspectiveID="#perspective503-6" + inkscape:corner0="-0.086129988 : -0.14445971 : 0 : 1" + inkscape:corner7="-0.10808329 : -0.16400408 : 0.051043755 : 1" + style="fill:#ff0000;fill-opacity:1;stroke:none" + inkscape:label="handle" + inkscape:transform-center-x="8" + inkscape:transform-center-y="98"> + <path + sodipodi:type="inkscape:box3dside" + id="path932-9" + inkscape:box3dsidetype="6" + d="m 919.8592,371.09874 v 61.75093 l 51.05152,-25.59855 V 348.7668 Z" + points="919.8592,432.84967 970.91072,407.25112 970.91072,348.7668 919.8592,371.09874 " + style="fill:#353564;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path940-1" + inkscape:box3dsidetype="13" + d="m 919.8592,432.84967 49.77111,22.08625 49.54589,-27.39008 -48.26548,-20.29472 z" + points="969.63031,454.93592 1019.1762,427.54584 970.91072,407.25112 919.8592,432.84967 " + style="fill:#afafde;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path942-2" + inkscape:box3dsidetype="11" + d="m 970.91072,348.7668 48.26548,18.93314 v 59.8459 l -48.26548,-20.29472 z" + points="1019.1762,367.69994 1019.1762,427.54584 970.91072,407.25112 970.91072,348.7668 " + style="fill:#e9e9ff;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path934-7" + inkscape:box3dsidetype="5" + d="m 919.8592,371.09874 49.77111,20.56633 49.54589,-23.96513 -48.26548,-18.93314 z" + points="969.63031,391.66507 1019.1762,367.69994 970.91072,348.7668 919.8592,371.09874 " + style="fill:#4d4d9f;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path938-0" + inkscape:box3dsidetype="14" + d="m 969.63031,391.66507 v 63.27085 l 49.54589,-27.39008 v -59.8459 z" + points="969.63031,454.93592 1019.1762,427.54584 1019.1762,367.69994 969.63031,391.66507 " + style="fill:#d7d7ff;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + <path + sodipodi:type="inkscape:box3dside" + id="path936-9" + inkscape:box3dsidetype="3" + d="m 919.8592,371.09874 49.77111,20.56633 v 63.27085 L 919.8592,432.84967 Z" + points="969.63031,391.66507 969.63031,454.93592 919.8592,432.84967 919.8592,371.09874 " + style="fill:#8686bf;fill-rule:evenodd;stroke:none;stroke-width:21.82598114px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" /> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-weight:normal;font-size:90.1384964px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#ff00ca;fill-opacity:1;stroke:none;stroke-width:2.25346255px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="1046.8701" + y="258.16129" + id="text1051-5" + transform="scale(0.91814752,1.0891496)" + inkscape:label="value"><tspan + sodipodi:role="line" + id="tspan1049-3" + x="1046.8701" + y="258.16129" + style="fill:#ff00ca;fill-opacity:1;stroke:none;stroke-width:2.25346255px;stroke-opacity:1">000</tspan></text> + </g> +</svg>