# HG changeset patch # User Edouard Tisserant <edouard.tisserant@gmail.com> # Date 1665043366 -7200 # Node ID 921f620577e8e586232ca5a9f142e1aab3c75838 # Parent 1cf21430ed4abd0023ed8b74ad65ea140ca37cc7# Parent f117526d41bae50c47bb782d17396c3111a9ede1 Merged changes from default diff -r 1cf21430ed4a -r 921f620577e8 exemples/svghmi_references/beremiz.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/beremiz.xml Thu Oct 06 10:02:46 2022 +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:61427"> + <TargetType/> + <Libraries Enable_SVGHMI_Library="true"/> +</BeremizRoot> diff -r 1cf21430ed4a -r 921f620577e8 exemples/svghmi_references/plc.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/plc.xml Thu Oct 06 10:02:46 2022 +0200 @@ -0,0 +1,71 @@ +<?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="2022-09-05T09:02:48"/> + <contentHeader name="Unnamed" modificationDateTime="2022-09-20T11:48:55"> + <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="program0" pouType="program"> + <interface> + <localVars> + <variable name="LocalVar0"> + <type> + <DINT/> + </type> + </variable> + <variable name="LocalVar1"> + <type> + <DINT/> + </type> + </variable> + </localVars> + </interface> + <body> + <FBD> + <inVariable localId="31" executionOrderId="0" height="25" width="85" negated="false"> + <position x="175" y="290"/> + <connectionPointOut> + <relPosition x="85" y="10"/> + </connectionPointOut> + <expression>LocalVar0</expression> + </inVariable> + <outVariable localId="30" executionOrderId="0" height="25" width="85" negated="false"> + <position x="330" y="290"/> + <connectionPointIn> + <relPosition x="0" y="10"/> + <connection refLocalId="31"> + <position x="330" y="300"/> + <position x="260" y="300"/> + </connection> + </connectionPointIn> + <expression>LocalVar1</expression> + </outVariable> + </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="program0"/> + </task> + </resource> + </configuration> + </configurations> + </instances> +</project> diff -r 1cf21430ed4a -r 921f620577e8 exemples/svghmi_references/svghmi_0@svghmi/baseconfnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/baseconfnode.xml Thu Oct 06 10:02:46 2022 +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 1cf21430ed4a -r 921f620577e8 exemples/svghmi_references/svghmi_0@svghmi/confnode.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/confnode.xml Thu Oct 06 10:02:46 2022 +0200 @@ -0,0 +1,2 @@ +<?xml version='1.0' encoding='utf-8'?> +<SVGHMI xmlns:xsd="http://www.w3.org/2001/XMLSchema"/> diff -r 1cf21430ed4a -r 921f620577e8 exemples/svghmi_references/svghmi_0@svghmi/svghmi.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/exemples/svghmi_references/svghmi_0@svghmi/svghmi.svg Thu Oct 06 10:02:46 2022 +0200 @@ -0,0 +1,1647 @@ +<?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" + inkscape:version="0.92.5 (2060ec1f9f, 2020-04-08)" + sodipodi:docname="svghmi.svg" + id="hmi0" + version="1.1" + viewBox="0 0 1280 720" + height="720" + width="1280"> + <metadata + id="metadata8"> + <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></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs6"> + <linearGradient + inkscape:collect="always" + id="linearGradient3492"> + <stop + style="stop-color:#ffe7c7;stop-opacity:1" + offset="0" + id="stop3488" /> + <stop + style="stop-color:#ffa32a;stop-opacity:1" + offset="1" + id="stop3490" /> + </linearGradient> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3492" + id="radialGradient3494" + cx="216.07262" + cy="502.80469" + fx="216.07262" + fy="502.80469" + r="163.49561" + gradientTransform="matrix(0.4681866,-0.01866012,0.01595521,0.40031975,730.16721,420.75466)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3492" + id="radialGradient3514" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4681866,-0.01866012,0.01595521,0.40031975,350.16721,420.75466)" + cx="958.08276" + cy="613.50269" + fx="958.08276" + fy="613.50269" + r="163.49561" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3492" + id="radialGradient3534" + gradientUnits="userSpaceOnUse" + gradientTransform="matrix(0.4681866,-0.01866012,0.01595521,0.40031975,-29.83279,420.75466)" + cx="1680.1648" + cy="652.54999" + fx="1680.1648" + fy="652.54999" + r="163.49561" /> + </defs> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="1600" + inkscape:window-height="836" + id="namedview4" + showgrid="false" + inkscape:zoom="0.23177389" + inkscape:cx="1502.9251" + inkscape:cy="-465.32787" + inkscape:window-x="0" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="hmi0" + showguides="true" + inkscape:guide-bbox="true" + borderlayer="true" + fit-margin-top="0" + fit-margin-left="0" + fit-margin-right="0" + fit-margin-bottom="0" + inkscape:pagecheckerboard="true" /> + <use + x="0" + y="0" + xlink:href="#g2496" + id="use2504" + transform="translate(3940,-2.1367187e-5)" + width="100%" + height="100%" + inkscape:label="HMI:Page:Buttons" + sodipodi:insensitive="true" /> + <use + inkscape:label="HMI:Page:Switch" + height="100%" + width="100%" + transform="translate(2640,-2.1367187e-5)" + id="use2502" + xlink:href="#g2496" + y="0" + x="0" + sodipodi:insensitive="true" /> + <use + x="0" + y="0" + xlink:href="#g2496" + id="use2498" + transform="translate(1320,-2.1367187e-5)" + width="100%" + height="100%" + inkscape:label="HMI:Page:Home" + sodipodi:insensitive="true" /> + <rect + y="382.29605" + x="14.419678" + height="190.44577" + width="240" + id="rect1189" + 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:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="2079.7461" + y="73.559319" + id="text106"><tspan + sodipodi:role="line" + id="tspan104" + x="2079.7461" + y="73.559319">Switch widget</tspan></text> + <g + style="stroke-width:1.42987263" + transform="translate(2248.2618,144.23794)" + id="g2775" + inkscape:label="HMI:ToggleButton@.boolvar"> + <g + inkscape:label="active" + id="g3474"> + <rect + rx="21.355932" + ry="21.355932" + y="618.00537" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect2781" + 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:url(#radialGradient3494);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:6;stroke-linecap:round;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" /> + <rect + inkscape:label="frame" + y="618.00537" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3470" + 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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + <rect + inkscape:label="inactive" + rx="21.355932" + ry="21.355932" + y="318.00534" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect2767" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77952766;stroke-linecap:round;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" /> + <text + id="text2773" + y="365.98633" + x="677.57697" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan2779" + y="365.98633" + x="677.57697" + sodipodi:role="line">Toggle</tspan><tspan + id="tspan3478" + y="415.98633" + x="677.57697" + sodipodi:role="line">button</tspan></text> + <rect + inkscape:label="reference" + y="318.00534" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3468" + 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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + <g + style="stroke-width:1.42987263" + inkscape:label="HMI:Button@.boolvar" + id="g3512" + transform="translate(2628.2624,144.23794)"> + <g + id="g3500" + inkscape:label="active"> + <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:url(#radialGradient3514);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:6;stroke-linecap:round;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="rect3496" + width="320.99121" + height="125.00497" + x="518.36066" + y="618.00537" + ry="21.355932" + rx="21.355932" /> + <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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect3498" + width="320.99121" + height="125.00497" + x="518.36066" + y="618.00537" + inkscape:label="frame" /> + </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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77952766;stroke-linecap:round;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="rect3502" + width="320.99121" + height="125.00497" + x="518.36066" + y="318.00534" + ry="21.355932" + rx="21.355932" + inkscape:label="inactive" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="677.57697" + y="365.98633" + id="text3508"><tspan + sodipodi:role="line" + x="677.57697" + y="365.98633" + id="tspan3506">Button</tspan></text> + <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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect3510" + width="320.99121" + height="125.00497" + x="518.36066" + y="318.00534" + inkscape:label="reference" /> + </g> + <g + style="stroke-width:1.42987263" + transform="translate(3008.263,144.23794)" + id="g3532" + inkscape:label="HMI:PushButton@.boolvar"> + <g + inkscape:label="active" + id="g3522"> + <rect + rx="21.355932" + ry="21.355932" + y="618.00537" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3518" + 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:url(#radialGradient3534);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:6;stroke-linecap:round;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" /> + <rect + inkscape:label="frame" + y="618.00537" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3520" + 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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </g> + <rect + inkscape:label="inactive" + rx="21.355932" + ry="21.355932" + y="318.00534" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3524" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77952766;stroke-linecap:round;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" /> + <text + id="text3528" + y="365.98633" + x="677.57697" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan3526" + y="365.98633" + x="677.57697" + sodipodi:role="line">Push</tspan><tspan + id="tspan3536" + y="415.98633" + x="677.57697" + sodipodi:role="line">Button</tspan></text> + <rect + inkscape:label="reference" + y="318.00534" + x="518.36066" + height="125.00497" + width="320.99121" + id="rect3530" + 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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.00000003, 3.00000003;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + </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:#999999;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:6;stroke-linecap:round;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="rect1807" + width="280" + height="81.859505" + x="-1705.9194" + y="17.83654" + ry="21.355932" + rx="21.355932" + inkscape:label="disabled_template" /> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.77952766;stroke-linecap:round;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="rect2311" + width="280" + height="81.859505" + x="-1705.9194" + y="17.83654" + ry="21.355932" + rx="21.355932" + inkscape:label="inactive_template" /> + <rect + inkscape:label="active_template" + rx="21.355932" + ry="21.355932" + y="17.83654" + x="-1705.9194" + height="81.859505" + width="280" + id="rect2313" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:6;stroke-linecap:round;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" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="703.23212" + y="588.7594" + id="text2948"><tspan + sodipodi:role="line" + id="tspan2946" + x="703.23212" + y="624.15002" /></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="44.283585" + y="-289.18591" + id="text2952"><tspan + sodipodi:role="line" + id="tspan2950" + x="44.283585" + y="-289.18591">Notes: </tspan><tspan + sodipodi:role="line" + x="44.283585" + y="-239.18593" + id="tspan2954">- Widget roles are described in objects labels.</tspan><tspan + sodipodi:role="line" + x="44.283585" + y="-189.18593" + id="tspan2956">- Press Ctrl+O to open object properties panel</tspan><tspan + sodipodi:role="line" + x="44.283585" + y="-139.18593" + id="tspan2962">- To see objects in a tree, select Object->Objects in menu</tspan><tspan + sodipodi:role="line" + x="44.283585" + y="-89.185921" + id="tspan2960">- Inkscape's "objects" are SVG elements</tspan><tspan + sodipodi:role="line" + x="44.283585" + y="-39.185921" + id="tspan2966">- Press Ctrl+X to edit SVG elements directly with XML editor</tspan></text> + <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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect376" + width="129.43649" + height="73.347351" + x="76.354057" + y="466.02609" /> + <rect + y="466.02609" + x="296.35406" + height="73.347351" + width="129.43649" + id="rect1183" + 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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <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:none;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect1185" + width="129.43649" + height="73.347351" + x="496.35406" + y="466.02609" /> + <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:#6d6d6d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect1187" + width="15.100924" + height="190.44576" + x="254.41968" + y="382.29605" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="141.59323" + y="402.47458" + id="text1193"><tspan + sodipodi:role="line" + id="tspan1191" + x="141.59323" + y="402.47458">Page (inkscape)</tspan></text> + <path + sodipodi:type="star" + 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:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path1199" + sodipodi:sides="6" + sodipodi:cx="141.13559" + sodipodi:cy="504.00531" + sodipodi:r1="26.350622" + sodipodi:r2="13.175311" + sodipodi:arg1="0.58235295" + sodipodi:arg2="1.1059517" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 163.14286,518.4979 -16.10099,-2.71529 -7.4536,14.52785 -5.69898,-15.30151 -16.30829,0.80892 10.40201,-12.58622 -8.85469,-13.71893 16.10099,2.7153 7.4536,-14.52786 5.69898,15.30151 16.30829,-0.80892 -10.40201,12.58622 z" + inkscape:transform-center-x="0.43452006" + inkscape:transform-center-y="-2.5530423" /> + <path + inkscape:transform-center-y="-2.5530423" + inkscape:transform-center-x="0.43452006" + d="m 383.14289,518.4979 -19.35043,-1.58794 -17.14943,9.10262 1.58794,-19.35043 -9.10262,-17.14943 19.35043,1.58794 17.14943,-9.10262 -1.58794,19.35043 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="false" + sodipodi:arg2="1.3677511" + sodipodi:arg1="0.58235295" + sodipodi:r2="13.175311" + sodipodi:r1="26.350622" + sodipodi:cy="504.00531" + sodipodi:cx="361.13562" + sodipodi:sides="4" + id="path1201" + 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:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + sodipodi:type="star" /> + <path + sodipodi:type="star" + 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:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path1203" + sodipodi:sides="5" + sodipodi:cx="561.13562" + sodipodi:cy="504.00531" + sodipodi:r1="26.350622" + sodipodi:r2="13.175311" + sodipodi:arg1="0.58235295" + sodipodi:arg2="1.2106715" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 583.14289,518.4979 -17.36441,-2.16244 -11.62551,13.07847 -3.3093,-17.18277 -16.03084,-7.01505 15.31915,-8.4571 1.71792,-17.414 12.77705,11.95599 17.09257,-3.74739 -7.4225,15.8463 z" + inkscape:transform-center-x="0.43452006" + inkscape:transform-center-y="-2.5530423" /> + <text + id="text1207" + y="440.47458" + x="137.59323" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="440.47458" + x="137.59323" + id="tspan1205" + sodipodi:role="line">final position in page</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="357.59326" + y="420.47458" + id="text1211"><tspan + sodipodi:role="line" + id="tspan1209" + x="357.59326" + y="420.47458">offset position</tspan><tspan + sodipodi:role="line" + x="357.59326" + y="445.47458" + id="tspan1251">for "B"</tspan></text> + <text + id="text1215" + y="420.47458" + x="557.59326" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="420.47458" + x="557.59326" + id="tspan1213" + sodipodi:role="line">offset position</tspan><tspan + y="445.47458" + x="557.59326" + sodipodi:role="line" + id="tspan1249">for "C"</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:monospace;-inkscape-font-specification:'monospace, 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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="664.59033" + y="440.47458" + id="text1219"><tspan + sodipodi:role="line" + id="tspan1217" + x="664.59033" + y="440.47458" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start">HMI:Switch@... (group)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="465.47458" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1225"> |-. "A" (group)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="490.47458" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1233"> | |- reference (rect)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="515.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1295"> | |- ...</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="540.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1229"> |-. "B" (group)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="565.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1235"> | |- frame (rect)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="590.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1237"> | |- ...</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="615.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1239"> |-. "C" (group)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="640.47461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan1241"> | |- frame (rect)</tspan><tspan + sodipodi:role="line" + x="664.59033" + y="665.47461" + id="tspan1221" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start"> | |- ...</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.3258667px;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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="317.2059" + y="63.193573" + id="text1257"><tspan + sodipodi:role="line" + id="tspan1255" + x="317.2059" + y="63.193573" + style="text-align:start;text-anchor:start;stroke-width:1px">Some widgets like Switch or Button are displaying one of many </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="99.850906" + id="tspan1263" + style="text-align:start;text-anchor:start;stroke-width:1px">groups that represent the possible states of the widget. Since </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="136.50824" + id="tspan1265" + style="text-align:start;text-anchor:start;stroke-width:1px">all groups need to appear in the same place, they overlap and </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="173.16557" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan486">the drawing becomes hard to understand and maintain.</tspan><tspan + sodipodi:role="line" + x="317.2059" + y="209.82291" + id="tspan1267" + style="text-align:start;text-anchor:start;stroke-width:1px" /><tspan + sodipodi:role="line" + x="317.2059" + y="246.48024" + id="tspan1269" + style="text-align:start;text-anchor:start;stroke-width:1px">Using specially labelled "reference" and "frame" rectangles, </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="283.13757" + id="tspan1271" + style="text-align:start;text-anchor:start;stroke-width:1px">groups can be spread out. Theses rectangles can be used </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="319.79492" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan474">in widget or anywhere in the drawing, and do not appear in </tspan><tspan + sodipodi:role="line" + x="317.2059" + y="356.45224" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan476">final result.</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="137.59323" + y="560.47461" + id="text1275"><tspan + sodipodi:role="line" + id="tspan1273" + x="137.59323" + y="560.47461">reference</tspan></text> + <text + id="text1281" + y="560.47461" + x="357.59326" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan1279" + y="560.47461" + x="357.59326" + sodipodi:role="line">frame</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="557.59326" + y="560.47461" + id="text1287"><tspan + id="tspan1285" + sodipodi:role="line" + x="557.59326" + y="560.47461">frame</tspan></text> + <text + id="text3607" + y="73.559319" + x="3359.7461" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="73.559319" + x="3359.7461" + id="tspan3605" + sodipodi:role="line">Button widgets</tspan></text> + <text + id="text222" + y="440.47458" + x="984.59027" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;line-height:125%;font-family:monospace;-inkscape-font-specification:'monospace, 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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="440.47458" + x="984.59027" + id="tspan202" + sodipodi:role="line">HMI:Switch@... (group)</tspan><tspan + id="tspan206" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="465.47458" + x="984.59027" + sodipodi:role="line"> |- reference (rect)</tspan><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="490.47458" + x="984.59027" + sodipodi:role="line" + id="tspan226"> |-. "A" (group)</tspan><tspan + id="tspan208" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="515.47461" + x="984.59027" + sodipodi:role="line"> | |- ...</tspan><tspan + id="tspan210" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="540.47461" + x="984.59027" + sodipodi:role="line"> |-. "B" (group)</tspan><tspan + id="tspan212" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="565.47461" + x="984.59027" + sodipodi:role="line"> | |- frame (rect)</tspan><tspan + id="tspan214" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="590.47461" + x="984.59027" + sodipodi:role="line"> | |- ...</tspan><tspan + id="tspan216" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="615.47461" + x="984.59027" + sodipodi:role="line"> |-. "C" (group)</tspan><tspan + id="tspan218" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + y="640.47461" + x="984.59027" + sodipodi:role="line"> | |- frame (rect)</tspan><tspan + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;font-family:monospace;-inkscape-font-specification:'monospace, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-feature-settings:normal;text-align:start;writing-mode:lr-tb;text-anchor:start" + id="tspan220" + y="665.47461" + x="984.59027" + sodipodi:role="line"> | |- ...</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="947.28815" + y="544.57629" + id="text230"><tspan + sodipodi:role="line" + id="tspan228" + x="947.28815" + y="544.57629">or</tspan></text> + <g + id="g213" + inkscape:label="HMI:Assign:dialog="simple":return="unknown"@dialog=selection@return=userchoice" + style="stroke-width:1.42987263" + transform="matrix(0.699363,0,0,0.699363,-393.71175,336.54679)"> + <rect + rx="30.536263" + ry="30.536263" + y="93.088097" + x="3373.916" + height="84.580788" + width="328.02896" + id="rect207" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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" /> + <text + id="text211" + y="149.95857" + x="3537.5791" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="149.95857" + x="3537.5791" + id="tspan209" + sodipodi:role="line" + style="stroke-width:1.42987263px">simple</tspan></text> + </g> + <g + id="g221" + inkscape:label="HMI:Assign:dialog="withWidgets":return="unknown"@dialog=selection@return=userchoice" + style="stroke-width:1.42987263" + transform="matrix(0.699363,0,0,0.699363,-463.64826,416.54679)"> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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="rect215" + width="328.02908" + height="84.58078" + x="3473.9163" + y="93.088097" + ry="30.536263" + rx="30.536263" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3637.5791" + y="149.95857" + id="text219"><tspan + sodipodi:role="line" + id="tspan217" + x="3637.5791" + y="149.95857" + style="stroke-width:1.42987263px">with widgets</tspan></text> + </g> + <g + id="g307" + inkscape:label="HMI:Display@userChoice" + transform="translate(-40.47583,140)"> + <text + inkscape:label="format" + id="text263" + y="564.4068" + x="2120.3391" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="564.4068" + x="2120.3391" + sodipodi:role="line" + id="tspan309">user choice : %s</tspan></text> + </g> + <g + id="g303" + inkscape:label="HMI:Display@selection" + transform="translate(0,140)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="2080.4785" + y="504.4068" + id="text267" + inkscape:label="format"><tspan + sodipodi:role="line" + id="tspan265" + x="2080.4785" + y="504.4068">selected dialog : %s</tspan></text> + </g> + <text + id="text329" + y="143.19357" + x="1637.2058" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.3258667px;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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="143.19357" + x="1637.2058" + sodipodi:role="line" + id="tspan394">Switch and Assign widgets can be used together to simulate </tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="179.85091" + x="1637.2058" + sodipodi:role="line" + id="tspan366">behavior modal dialog or "popup" with user feedback.</tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="216.50824" + x="1637.2058" + sodipodi:role="line" + id="tspan372">"selection" and "userChoice" local HMI are used to respectively</tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="253.16557" + x="1637.2058" + sodipodi:role="line" + id="tspan412">select dialog to be shown and store user choice.</tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="289.82291" + x="1637.2058" + sodipodi:role="line" + id="tspan404">Here, "reference" and "frame" rectangles are necessary to</tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="326.48022" + x="1637.2058" + sodipodi:role="line" + id="tspan406">to spread out dialogs and page, otherwise overlapping.</tspan><tspan + style="text-align:start;text-anchor:start;stroke-width:1px" + y="363.13757" + x="1637.2058" + sodipodi:role="line" + id="tspan370" /></text> + <g + id="g3108" + inkscape:label="HMI:Switch@selection"> + <g + id="g1318" + inkscape:label=""simple""> + <rect + inkscape:label="overlay_background" + y="760" + x="1320" + height="720" + width="1280" + id="rect1420-8" + style="color:#000000;opacity:0.28800001;fill:#000000;fill-opacity:1" /> + <rect + y="890.84747" + x="1607.7966" + height="442.37289" + width="668.13562" + id="rect1468" + 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:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:round;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" /> + <rect + inkscape:label="frame" + style="color:#000000;opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1" + id="rect1312" + width="1280" + height="720" + x="1320" + y="760" /> + <text + id="text1472" + y="1138.5114" + x="1943.3489" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="1138.5114" + x="1943.3489" + id="tspan1470" + sodipodi:role="line">A SIMPLE MODAL DIALOG</tspan></text> + <g + id="g242" + inkscape:label="HMI:Assign:dialog="None":return="OK"@dialog=selection@return=userChoice" + transform="matrix(0.699363,0,0,0.699363,-627.0073,1166.0479)" + style="stroke-width:1.42987263"> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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="rect236" + width="228.61551" + height="119.85847" + x="3445.5569" + y="75.449257" + ry="30.536264" + rx="30.536266" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:56.68354416px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987251px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3559.3662" + y="156.03976" + id="text240"><tspan + style="stroke-width:1.42987251px" + sodipodi:role="line" + id="tspan238" + x="3559.3662" + y="156.03976">OK</tspan></text> + </g> + <g + style="stroke-width:1.42987263" + transform="matrix(0.699363,0,0,0.699363,-447.0073,1166.0479)" + inkscape:label="HMI:Assign:dialog="None":return="Canceled"@dialog=selection@return=userChoice" + id="g250"> + <rect + rx="30.536266" + ry="30.536264" + y="75.449257" + x="3445.5569" + height="119.85847" + width="228.61551" + id="rect244" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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" /> + <text + id="text248" + y="156.03976" + x="3559.3662" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:56.68354416px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987251px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="156.03976" + x="3559.3662" + id="tspan246" + sodipodi:role="line" + style="stroke-width:1.42987251px">Cancel</tspan></text> + </g> + <g + id="g1488-3" + inkscape:label="HMI:Assign:dialog="None":return="closed"@dialog=selection@return=userChoice" + transform="matrix(0.699363,0,0,0.699363,-267.00732,846.0479)" + style="stroke-width:1.42987263"> + <g + id="g1486-6" + inkscape:label="=0" + style="stroke-width:1.00902128" + transform="matrix(1.4170886,0,0,1.4170886,-1453.6968,-56.464826)"> + <rect + rx="21.548594" + ry="21.548592" + y="103.0928" + x="3498.0554" + height="61.493015" + width="78.211609" + id="rect1480-7" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.81362391;stroke-linecap:round;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" /> + <text + id="text1484-5" + y="149.95857" + x="3537.5791" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00902128px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="149.95857" + x="3537.5791" + id="tspan1482-3" + sodipodi:role="line" + style="stroke-width:1.00902128px">X</tspan></text> + </g> + </g> + </g> + <rect + inkscape:label="reference" + style="color:#000000;opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1" + id="rect1314" + width="1280" + height="720" + x="1320" + y="0" /> + <g + transform="translate(0,760)" + id="g1494" + inkscape:label=""withWidgets""> + <rect + style="color:#000000;opacity:0.28800001;fill:#000000;fill-opacity:1" + id="rect1474" + width="1280" + height="720" + x="1320" + y="760" + inkscape:label="overlay_background" /> + <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:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:round;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="rect1476" + width="668.13562" + height="442.37289" + x="1607.7966" + y="890.84747" /> + <rect + y="760" + x="1320" + height="720" + width="1280" + id="rect1478" + style="color:#000000;opacity:1;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:3, 3;stroke-dashoffset:0;stroke-opacity:1" + inkscape:label="frame" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="1943.3489" + y="1018.5115" + id="text1492"><tspan + sodipodi:role="line" + id="tspan1490" + x="1943.3489" + y="1018.5115">A MODAL DIALOG</tspan><tspan + id="tspan1504" + sodipodi:role="line" + x="1943.3489" + y="1068.5115">with widgets</tspan></text> + <g + style="stroke-width:1.42987263" + transform="matrix(0.699363,0,0,0.699363,-672.97869,1158.0327)" + inkscape:label="HMI:Input@.position" + id="g2585-1-4"> + <g + style="stroke-width:1.42987263" + inkscape:label="=0" + id="g2763-2-7"> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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="rect114-6-7-2" + width="96.681831" + height="84.580788" + x="3489.5898" + y="93.088097" + ry="30.536263" + rx="30.536263" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3537.5791" + y="149.95857" + id="text118-3-0-4"><tspan + style="stroke-width:1.42987263px" + sodipodi:role="line" + id="tspan116-1-9-0" + x="3537.5791" + y="149.95857">0</tspan></text> + </g> + <g + style="stroke-width:1.42987263" + inkscape:label="=1" + id="g2758-3-6"> + <rect + rx="30.536263" + ry="30.536263" + y="93.088097" + x="3589.5898" + height="84.580788" + width="96.681831" + id="rect2531-6-2" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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" /> + <text + id="text2557-0-9" + y="149.95857" + x="3637.5791" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="stroke-width:1.42987263px" + y="149.95857" + x="3637.5791" + id="tspan2555-6-9" + sodipodi:role="line">1</tspan></text> + </g> + <g + style="stroke-width:1.42987263" + inkscape:label="=2" + id="g2753-2-0"> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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="rect2533-6-8" + width="96.681831" + height="84.580788" + x="3689.5898" + y="93.088097" + ry="30.536263" + rx="30.536263" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3737.5791" + y="149.95857" + id="text2561-1-1"><tspan + style="stroke-width:1.42987263px" + sodipodi:role="line" + id="tspan2559-8-3" + x="3737.5791" + y="149.95857">2</tspan></text> + </g> + <g + style="stroke-width:1.42987263" + inkscape:label="=3" + id="g2748-7-1"> + <rect + rx="30.536263" + ry="30.536263" + y="93.088097" + x="3789.5898" + height="84.580788" + width="96.681831" + id="rect2535-9-1" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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" /> + <text + id="text2565-2-0" + y="149.95857" + x="3837.5791" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + style="stroke-width:1.42987263px" + y="149.95857" + x="3837.5791" + id="tspan2563-0-3" + sodipodi:role="line">3</tspan></text> + </g> + <text + inkscape:label="value" + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:85.33333588px;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;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3687.7056" + y="56.031158" + id="text2569-2-4"><tspan + style="stroke-width:1.42987263px" + sodipodi:role="line" + id="tspan2567-3-0" + x="3687.7056" + y="56.031158">0</tspan></text> + <text + id="text2820-7-3" + y="216.03116" + x="3687.7056" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1.42987263px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan3031" + style="stroke-width:1.42987263px" + y="216.03116" + x="3687.7056" + sodipodi:role="line">position</tspan></text> + </g> + <g + transform="translate(1898.746,605.58892)" + inkscape:label="HMI:ScrollBar@.range@.position@.size" + id="g1766"> + <path + inkscape:label="pageup" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.42391574px;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 -234.01097,332.35504 21.18736,28.36866 h -42.37471 z" + id="path1266" + inkscape:connector-curvature="0" + sodipodi:nodetypes="cccc" /> + <path + inkscape:label="pagedown" + sodipodi:nodetypes="cccc" + inkscape:connector-curvature="0" + id="path1268" + d="m -234.01097,686.72773 21.18736,-27.45222 h -42.37471 z" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.4007318px;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" /> + <rect + inkscape:label="range" + ry="6.8822322" + rx="7.6034913" + y="371.91068" + x="-255.19838" + height="276.64423" + width="42.374725" + id="rect1264-3" + style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.30952382;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.03627348px;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" /> + <rect + inkscape:label="cursor" + ry="7" + rx="7.6034913" + y="371.91068" + x="-255.19838" + height="82.841492" + width="42.374725" + id="rect1264" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.11429262px;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" /> + </g> + <g + id="g1488" + inkscape:label="HMI:Assign:dialog="None":return="closed"@dialog=selection@return=userChoice" + transform="matrix(0.699363,0,0,0.699363,-267.00732,846.0479)" + style="stroke-width:1.42987263"> + <g + id="g1486" + inkscape:label="=0" + style="stroke-width:1.00902128" + transform="matrix(1.4170886,0,0,1.4170886,-1453.6968,-56.464826)"> + <rect + rx="21.548594" + ry="21.548592" + y="103.0928" + x="3498.0554" + height="61.493015" + width="78.211609" + id="rect1480" + 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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3.81362391;stroke-linecap:round;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" /> + <text + id="text1484" + y="149.95857" + x="3537.5791" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.00902128px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="149.95857" + x="3537.5791" + id="tspan1482" + sodipodi:role="line" + style="stroke-width:1.00902128px">X</tspan></text> + </g> + </g> + <g + id="g242-3" + inkscape:label="HMI:Assign\" + transform="matrix(0.699363,0,0,0.699363,-307.71004,1178.2943)" + style="stroke-width:1.42987263"> + <desc + id="desc755">:dialog="None" +:return="Applied" +:plcvar=uservar +@dialog=selection +@return=userChoice +@uservar=.position +@plcvar=/PLCHMIVAR</desc> + <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:#ffa32a;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:5.40424299;stroke-linecap:round;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="rect236-6" + width="228.61551" + height="119.85847" + x="3445.5569" + y="75.449257" + ry="30.536264" + rx="30.536266" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:56.68354416px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.42987251px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3559.3662" + y="156.03976" + id="text240-7"><tspan + style="stroke-width:1.42987251px" + sodipodi:role="line" + id="tspan238-5" + x="3559.3662" + y="156.03976">Apply</tspan></text> + </g> + </g> + </g> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:29.3258667px;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:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="2937.2058" + y="143.19357" + id="text392"><tspan + id="tspan390" + sodipodi:role="line" + x="2937.2058" + y="143.19357" + style="text-align:start;text-anchor:start;stroke-width:1px">In this example, 3 types of button ar connected to the same</tspan><tspan + sodipodi:role="line" + x="2937.2058" + y="179.85091" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan436">HMI local variable. </tspan><tspan + sodipodi:role="line" + x="2937.2058" + y="216.50824" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan438">Here, "reference" and "frame" rectangles are used to</tspan><tspan + sodipodi:role="line" + x="2937.2058" + y="253.16557" + style="text-align:start;text-anchor:start;stroke-width:1px" + id="tspan440">separate active and inactive state of buttons</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="141.14536" + y="518.58539" + id="text444"><tspan + sodipodi:role="line" + id="tspan442" + x="141.14536" + y="518.58539">A</tspan></text> + <text + id="text448" + y="518.58539" + x="361.14536" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="518.58539" + x="361.14536" + id="tspan446" + sodipodi:role="line">B</tspan></text> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="561.14539" + y="518.58539" + id="text452"><tspan + sodipodi:role="line" + id="tspan450" + x="561.14539" + y="518.58539">C</tspan></text> + <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:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.99999998, 3.99999998;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="rect488" + width="240" + height="101.20848" + x="14.419678" + y="602.29602" /> + <rect + y="602.29602" + x="254.41968" + height="101.20847" + width="15.100924" + id="rect496" + 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:#6d6d6d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.99999988;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:3.99999998, 3.99999998;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> + <text + id="text500" + y="622.47461" + x="141.26608" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:20px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="622.47461" + x="141.26608" + id="tspan498" + sodipodi:role="line">Page (final result)</tspan></text> + <g + id="g561" + transform="translate(0,-20)"> + <path + sodipodi:type="star" + 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:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path502" + sodipodi:sides="6" + sodipodi:cx="141.13559" + sodipodi:cy="684.00531" + sodipodi:r1="26.350622" + sodipodi:r2="13.175311" + sodipodi:arg1="0.58235295" + sodipodi:arg2="1.1059517" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 163.14286,698.4979 -16.10099,-2.71529 -7.4536,14.52785 -5.69898,-15.30151 -16.30829,0.80892 10.40201,-12.58622 -8.85469,-13.71893 16.10099,2.7153 7.4536,-14.52786 5.69898,15.30151 16.30829,-0.80892 -10.40201,12.58622 z" + inkscape:transform-center-x="0.43452006" + inkscape:transform-center-y="-2.5530423" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="141.14536" + y="698.58539" + id="text538"><tspan + sodipodi:role="line" + id="tspan536" + x="141.14536" + y="698.58539">A</tspan></text> + </g> + <g + id="g556" + transform="translate(-60,-20)"> + <path + inkscape:transform-center-y="-2.5530423" + inkscape:transform-center-x="0.43452006" + d="m 223.14289,698.4979 -19.35043,-1.58794 -17.14943,9.10262 1.58794,-19.35043 -9.10262,-17.14943 19.35043,1.58794 17.14943,-9.10262 -1.58794,19.35043 z" + inkscape:randomized="0" + inkscape:rounded="0" + inkscape:flatsided="false" + sodipodi:arg2="1.3677511" + sodipodi:arg1="0.58235295" + sodipodi:r2="13.175311" + sodipodi:r1="26.350622" + sodipodi:cy="684.00531" + sodipodi:cx="201.13562" + sodipodi:sides="4" + id="path504" + 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:#008000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + sodipodi:type="star" /> + <text + id="text542" + y="698.58539" + x="201.14536" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + y="698.58539" + x="201.14536" + id="tspan540" + sodipodi:role="line">B</tspan></text> + </g> + <g + id="g551" + transform="translate(-420,-20)"> + <path + sodipodi:type="star" + 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:#0000ff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1;marker:none;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" + id="path506" + sodipodi:sides="5" + sodipodi:cx="561.13562" + sodipodi:cy="684.00531" + sodipodi:r1="26.350622" + sodipodi:r2="13.175311" + sodipodi:arg1="0.58235295" + sodipodi:arg2="1.2106715" + inkscape:flatsided="false" + inkscape:rounded="0" + inkscape:randomized="0" + d="m 583.14289,698.4979 -17.36441,-2.16244 -11.62551,13.07847 -3.3093,-17.18277 -16.03084,-7.01505 15.31915,-8.4571 1.71792,-17.414 12.77705,11.95599 17.09257,-3.74739 -7.4225,15.8463 z" + inkscape:transform-center-x="0.43452006" + inkscape:transform-center-y="-2.5530423" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="561.14539" + y="698.58539" + id="text546"><tspan + sodipodi:role="line" + id="tspan544" + x="561.14539" + y="698.58539">C</tspan></text> + </g> + <g + id="g2496" + inkscape:label="page_template" + transform="translate(0,2.1367187e-5)"> + <rect + y="0" + x="-1320" + height="720" + width="1280" + id="rect1420" + style="color:#000000;opacity:1;fill:#d6d6d6;fill-opacity:1" /> + <g + inkscape:label="HMI:Jump:Home" + id="g2455"> + <use + x="0" + y="0" + xlink:href="#rect2313" + id="use2435" + transform="translate(400,-2.1367187e-5)" + width="100%" + height="100%" + inkscape:label="active" /> + <use + x="0" + y="0" + xlink:href="#rect2311" + id="use2437" + transform="translate(400,-2.1367187e-5)" + width="100%" + height="100%" + inkscape:label="inactive" /> + <text + id="text855-7-1" + y="70.251053" + x="-1166.8177" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan66-3" + y="70.251053" + x="-1166.8177" + sodipodi:role="line">Home</tspan></text> + </g> + <g + inkscape:label="HMI:Jump:Switch" + id="g2461" + transform="translate(0,20)"> + <use + height="100%" + width="100%" + transform="translate(400,99.999979)" + id="use2439" + xlink:href="#rect2313" + y="0" + x="0" + inkscape:label="active" /> + <use + height="100%" + width="100%" + transform="translate(400,99.999979)" + id="use2441" + xlink:href="#rect2311" + y="0" + x="0" + inkscape:label="inactive" /> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="-1165.1674" + y="170.25105" + id="text2349"><tspan + sodipodi:role="line" + x="-1165.1674" + y="170.25105" + id="tspan2347">Swith</tspan></text> + </g> + <g + inkscape:label="HMI:Jump:Buttons" + id="g2467" + transform="translate(0,40)"> + <use + x="0" + y="0" + xlink:href="#rect2313" + id="use2443" + transform="translate(400,199.99998)" + width="100%" + height="100%" + inkscape:label="active" /> + <use + x="0" + y="0" + xlink:href="#rect2311" + id="use2445" + transform="translate(400,199.99998)" + width="100%" + height="100%" + inkscape:label="inactive" /> + <text + id="text2357" + y="270.25104" + x="-1165.7826" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:40px;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:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan2355" + y="270.25104" + x="-1165.7826" + sodipodi:role="line">Buttons</tspan></text> + </g> + </g> + <g + id="g472" + inkscape:label="DECLARATIONS"> + <g + transform="translate(-875.45983,1716.017)" + inkscape:label="HMI:VarInit:1@.position" + id="g2590"> + <text + id="text2743" + y="-108.39357" + x="3726.6924" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan2765" + y="-108.39357" + x="3726.6924" + sodipodi:role="line">declaration of "position" HMI local variable</tspan></text> + </g> + <g + inkscape:label="HMI:VarInit:"None"@selection" + id="g251"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="2048.7263" + y="-133.4783" + id="text1500"><tspan + sodipodi:role="line" + x="2048.7263" + y="-133.4783" + id="tspan1498">declaration of 'selection' local variable</tspan></text> + </g> + <g + inkscape:label="HMI:VarInit:"unknown"@userChoice" + id="g255"> + <text + id="text235" + y="-93.478294" + x="2048.7263" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan257" + y="-93.478294" + x="2048.7263" + sodipodi:role="line">declaration of 'userChoice' local variable</tspan></text> + </g> + <g + id="g3041" + inkscape:label="HMI:VarInit:4@.range" + transform="translate(-875.45983,1776.017)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3726.6924" + y="-108.39357" + id="text3039"><tspan + sodipodi:role="line" + x="3726.6924" + y="-108.39357" + id="tspan3037">declaration of "range" HMI local variable</tspan></text> + </g> + <g + transform="translate(-875.45983,1836.017)" + inkscape:label="HMI:VarInit:1@.size" + id="g3049"> + <text + id="text3047" + y="-108.39357" + x="3726.6924" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + xml:space="preserve"><tspan + id="tspan3045" + y="-108.39357" + x="3726.6924" + sodipodi:role="line">declaration of "size" HMI local variable</tspan></text> + </g> + <g + id="g195" + inkscape:label="HMI:VarInit:true@.boolvar" + transform="translate(-155.45983,36.017)"> + <text + xml:space="preserve" + style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;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;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + x="3726.6924" + y="-108.39357" + id="text193"><tspan + sodipodi:role="line" + x="3726.6924" + y="-108.39357" + id="tspan191">declaration of "position" HMI local variable</tspan></text> + </g> + </g> +</svg> diff -r 1cf21430ed4a -r 921f620577e8 svghmi/analyse_widget.xslt --- a/svghmi/analyse_widget.xslt Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/analyse_widget.xslt Thu Oct 06 10:02:46 2022 +0200 @@ -262,6 +262,42 @@ <xsl:text>speed</xsl:text> </path> </xsl:template> + <xsl:template match="widget[@type='Assign']" mode="widget_desc"> + <type> + <xsl:value-of select="@type"/> + </type> + <longdesc> + <xsl:text> +</xsl:text> + <xsl:text>Arguments are either: +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>- name=value: setting variable with literal value. +</xsl:text> + <xsl:text>- name=other_name: copy variable content into another +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>"active"+"inactive" labeled elements can be provided to show feedback when pressed +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>Exemples: +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>HMI:Assign:notify=1@notify=/PLCVAR +</xsl:text> + <xsl:text>HMI:Assign:ack=2:notify=1@ack=.local_var@notify=/PLCVAR +</xsl:text> + <xsl:text> +</xsl:text> + </longdesc> + <shortdesc> + <xsl:text>Assign variables on click</xsl:text> + </shortdesc> + </xsl:template> <xsl:template match="widget[@type='Back']" mode="widget_desc"> <type> <xsl:value-of select="@type"/> diff -r 1cf21430ed4a -r 921f620577e8 svghmi/detachable_pages.ysl2 --- a/svghmi/detachable_pages.ysl2 Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/detachable_pages.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -68,7 +68,11 @@ param "page"; const "page_overlapping_geometry", "$overlapping_geometry/elt[@id = $page/@id]/*"; const "page_overlapping_elements", "//svg:*[@id = $page_overlapping_geometry/@Id]"; - const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements)"; + const "page_widgets_elements", """ + $hmi_elements[not(@id=$page/@id) + and descendant-or-self::svg:*/@id = $page_overlapping_elements/@id] + /descendant-or-self::svg:*"""; + const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements | $page_widgets_elements)"; result "$page_sub_elements"; } @@ -214,6 +218,10 @@ foreach "$detachable_elements"{ | «@id» } + | DISCARDABLES: + foreach "$discardable_elements"{ + | «@id» + } | In Foreach: foreach "$in_forEach_widget_ids"{ | «.» diff -r 1cf21430ed4a -r 921f620577e8 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/gen_index_xhtml.xslt Thu Oct 06 10:02:46 2022 +0200 @@ -555,6 +555,23 @@ <xsl:variable name="candidates" select="$geometry[@Id != $elt/@id]"/> <func:result select="$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or (not(@Id = $groups/@id) and (func:intersect($g, .) > 0 ))]"/> </func:function> + <func:function name="func:offset"> + <xsl:param name="elt1"/> + <xsl:param name="elt2"/> + <xsl:variable name="g1" select="$geometry[@Id = $elt1/@id]"/> + <xsl:variable name="g2" select="$geometry[@Id = $elt2/@id]"/> + <xsl:variable name="result"> + <vector> + <xsl:attribute name="x"> + <xsl:value-of select="$g2/@x - $g1/@x"/> + </xsl:attribute> + <xsl:attribute name="y"> + <xsl:value-of select="$g2/@y - $g1/@y"/> + </xsl:attribute> + </vector> + </xsl:variable> + <func:result select="exsl:node-set($result)"/> + </func:function> <xsl:variable name="hmi_lists_descs" select="$parsed_widgets/widget[@type = 'List']"/> <xsl:variable name="hmi_lists" select="$hmi_elements[@id = $hmi_lists_descs/@id]"/> <xsl:variable name="hmi_textlists_descs" select="$parsed_widgets/widget[@type = 'TextList']"/> @@ -657,7 +674,8 @@ <xsl:param name="page"/> <xsl:variable name="page_overlapping_geometry" select="$overlapping_geometry/elt[@id = $page/@id]/*"/> <xsl:variable name="page_overlapping_elements" select="//svg:*[@id = $page_overlapping_geometry/@Id]"/> - <xsl:variable name="page_sub_elements" select="func:refered_elements($page | $page_overlapping_elements)"/> + <xsl:variable name="page_widgets_elements" select=" $hmi_elements[not(@id=$page/@id) and descendant-or-self::svg:*/@id = $page_overlapping_elements/@id] /descendant-or-self::svg:*"/> + <xsl:variable name="page_sub_elements" select="func:refered_elements($page | $page_overlapping_elements | $page_widgets_elements)"/> <func:result select="$page_sub_elements"/> </func:function> <func:function name="func:required_elements"> @@ -890,6 +908,14 @@ <xsl:text> </xsl:text> </xsl:for-each> + <xsl:text>DISCARDABLES: +</xsl:text> + <xsl:for-each select="$discardable_elements"> + <xsl:text> </xsl:text> + <xsl:value-of select="@id"/> + <xsl:text> +</xsl:text> + </xsl:for-each> <xsl:text>In Foreach: </xsl:text> <xsl:for-each select="$in_forEach_widget_ids"> @@ -945,6 +971,21 @@ <xsl:value-of select="substring(., 2)"/> </xsl:attribute> </xsl:template> + <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="svg:rect[@inkscape:label='reference' or @inkscape:label='frame']"/> + <xsl:template xmlns="http://www.w3.org/2000/svg" mode="inline_svg" match="svg:g[svg:rect/@inkscape:label='frame']"> + <xsl:variable name="reference_rect" select="(../svg:rect | ../svg:g/svg:rect)[@inkscape:label='reference']"/> + <xsl:variable name="frame_rect" select="svg:rect[@inkscape:label='frame']"/> + <xsl:variable name="offset" select="func:offset($frame_rect, $reference_rect)"/> + <xsl:copy> + <xsl:attribute name="svghmi_x_offset"> + <xsl:value-of select="$offset/vector/@x"/> + </xsl:attribute> + <xsl:attribute name="svghmi_y_offset"> + <xsl:value-of select="$offset/vector/@y"/> + </xsl:attribute> + <xsl:apply-templates mode="inline_svg" select="@* | node()"/> + </xsl:copy> + </xsl:template> <xsl:variable name="targets_not_to_unlink" select="$hmi_lists/descendant-or-self::svg:*"/> <xsl:variable name="to_unlink" select="$hmi_widgets/descendant-or-self::svg:use"/> <func:function name="func:is_unlinkable"> @@ -1516,8 +1557,6 @@ </xsl:text> <xsl:text>var cache = hmitree_types.map(_ignored => undefined); </xsl:text> - <xsl:text>var updates = new Map(); -</xsl:text> <xsl:text> </xsl:text> <xsl:text>function page_local_index(varname, pagename){ @@ -1530,7 +1569,7 @@ </xsl:text> <xsl:text> new_index = next_available_index++; </xsl:text> - <xsl:text> hmi_locals[pagename] = {[varname]:new_index} + <xsl:text> hmi_locals[pagename] = {[varname]:new_index}; </xsl:text> <xsl:text> } else { </xsl:text> @@ -1556,8 +1595,6 @@ </xsl:text> <xsl:text> cache[new_index] = defaultval; </xsl:text> - <xsl:text> updates.set(new_index, defaultval); -</xsl:text> <xsl:text> if(persistent_locals.has(varname)) </xsl:text> <xsl:text> persistent_indexes.set(new_index, varname); @@ -2656,6 +2693,199 @@ <xsl:text>} </xsl:text> </xsl:template> + <xsl:template match="widget[@type='Assign']" mode="widget_desc"> + <type> + <xsl:value-of select="@type"/> + </type> + <longdesc> + <xsl:text> +</xsl:text> + <xsl:text>Arguments are either: +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>- name=value: setting variable with literal value. +</xsl:text> + <xsl:text>- name=other_name: copy variable content into another +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>"active"+"inactive" labeled elements can be provided to show feedback when pressed +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>Exemples: +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>HMI:Assign:notify=1@notify=/PLCVAR +</xsl:text> + <xsl:text>HMI:Assign:ack=2:notify=1@ack=.local_var@notify=/PLCVAR +</xsl:text> + <xsl:text> +</xsl:text> + </longdesc> + <shortdesc> + <xsl:text>Assign variables on click</xsl:text> + </shortdesc> + </xsl:template> + <xsl:template match="widget[@type='Assign']" mode="widget_class"> + <xsl:text>class </xsl:text> + <xsl:text>AssignWidget</xsl:text> + <xsl:text> extends Widget{ +</xsl:text> + <xsl:text> frequency = 2; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> onmouseup(evt) { +</xsl:text> + <xsl:text> svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); +</xsl:text> + <xsl:text> if(this.enable_state) { +</xsl:text> + <xsl:text> this.activity_state = false +</xsl:text> + <xsl:text> this.request_animate(); +</xsl:text> + <xsl:text> this.assign(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> onmousedown(){ +</xsl:text> + <xsl:text> if(this.enable_state) { +</xsl:text> + <xsl:text> svg_root.addEventListener("pointerup", this.bound_onmouseup, true); +</xsl:text> + <xsl:text> this.activity_state = true; +</xsl:text> + <xsl:text> this.request_animate(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>} +</xsl:text> + </xsl:template> + <xsl:template match="widget[@type='Assign']" mode="widget_defs"> + <xsl:param name="hmi_element"/> + <xsl:variable name="disability"> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>/disabled</xsl:text> + </xsl:with-param> + <xsl:with-param name="mandatory" select="'no'"/> + </xsl:call-template> + </xsl:variable> + <xsl:value-of select="$disability"/> + <xsl:variable name="has_disability" select="string-length($disability)>0"/> + <xsl:text> activable_sub:{ +</xsl:text> + <xsl:variable name="activity"> + <xsl:call-template name="defs_by_labels"> + <xsl:with-param name="hmi_element" select="$hmi_element"/> + <xsl:with-param name="labels"> + <xsl:text>/active /inactive</xsl:text> + </xsl:with-param> + <xsl:with-param name="mandatory"> + <xsl:text>no</xsl:text> + </xsl:with-param> + </xsl:call-template> + </xsl:variable> + <xsl:value-of select="$activity"/> + <xsl:variable name="has_activity" select="string-length($activity)>0"/> + <xsl:text> }, +</xsl:text> + <xsl:text> has_activity: </xsl:text> + <xsl:value-of select="$has_activity"/> + <xsl:text>, +</xsl:text> + <xsl:text> init: function() { +</xsl:text> + <xsl:text> this.bound_onmouseup = this.onmouseup.bind(this); +</xsl:text> + <xsl:text> this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); +</xsl:text> + <xsl:text> }, +</xsl:text> + <xsl:text> assignments: {}, +</xsl:text> + <xsl:text> dispatch: function(value, oldval, varnum) { +</xsl:text> + <xsl:variable name="widget" select="."/> + <xsl:for-each select="path"> + <xsl:variable name="varid" select="generate-id()"/> + <xsl:variable name="varnum" select="position()-1"/> + <xsl:if test="@assign"> + <xsl:for-each select="$widget/path[@assign]"> + <xsl:if test="$varid = generate-id()"> + <xsl:text> if(varnum == </xsl:text> + <xsl:value-of select="$varnum"/> + <xsl:text>) this.assignments["</xsl:text> + <xsl:value-of select="@assign"/> + <xsl:text>"] = value; +</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + </xsl:for-each> + <xsl:text> }, +</xsl:text> + <xsl:text> assign: function() { +</xsl:text> + <xsl:variable name="paths" select="path"/> + <xsl:for-each select="arg[contains(@value,'=')]"> + <xsl:variable name="name" select="substring-before(@value,'=')"/> + <xsl:variable name="value" select="substring-after(@value,'=')"/> + <xsl:variable name="index"> + <xsl:for-each select="$paths"> + <xsl:if test="@assign = $name"> + <xsl:value-of select="position()-1"/> + </xsl:if> + </xsl:for-each> + </xsl:variable> + <xsl:variable name="isVarName" select="regexp:test($value,'^[a-zA-Z_][a-zA-Z0-9_]+$')"/> + <xsl:choose> + <xsl:when test="$isVarName"> + <xsl:text> const </xsl:text> + <xsl:value-of select="$value"/> + <xsl:text> = this.assignments["</xsl:text> + <xsl:value-of select="$value"/> + <xsl:text>"]; +</xsl:text> + <xsl:text> if(</xsl:text> + <xsl:value-of select="$value"/> + <xsl:text> != undefined) +</xsl:text> + <xsl:text> this.apply_hmi_value(</xsl:text> + <xsl:value-of select="$index"/> + <xsl:text>, </xsl:text> + <xsl:value-of select="$value"/> + <xsl:text>); +</xsl:text> + </xsl:when> + <xsl:otherwise> + <xsl:text> this.apply_hmi_value(</xsl:text> + <xsl:value-of select="$index"/> + <xsl:text>, </xsl:text> + <xsl:value-of select="$value"/> + <xsl:text>); +</xsl:text> + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + <xsl:text> }, +</xsl:text> + </xsl:template> <xsl:template match="widget[@type='Back']" mode="widget_desc"> <type> <xsl:value-of select="@type"/> @@ -5923,39 +6153,31 @@ </xsl:text> <xsl:text> frequency = 2; </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> make_on_click() { -</xsl:text> - <xsl:text> let that = this; -</xsl:text> - <xsl:text> const name = this.args[0]; -</xsl:text> - <xsl:text> return function(evt){ -</xsl:text> - <xsl:text> /* TODO: in order to allow jumps to page selected through -</xsl:text> - <xsl:text> for exemple a dropdown, support path pointing to local -</xsl:text> - <xsl:text> variable whom value would be an HMI_TREE index and then -</xsl:text> - <xsl:text> jump to a relative page not hard-coded in advance -</xsl:text> - <xsl:text> */ -</xsl:text> - <xsl:text> if(that.enable_state) { -</xsl:text> - <xsl:text> const index = -</xsl:text> - <xsl:text> (that.is_relative && that.indexes.length > 0) ? -</xsl:text> - <xsl:text> that.indexes[0] + that.offset : undefined; -</xsl:text> - <xsl:text> fading_page_switch(name, index); -</xsl:text> - <xsl:text> that.notify(); -</xsl:text> - <xsl:text> } + <xsl:text> target_page_is_current_page = false; +</xsl:text> + <xsl:text> button_beeing_pressed = false; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> onmouseup(evt) { +</xsl:text> + <xsl:text> svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); +</xsl:text> + <xsl:text> if(this.enable_state) { +</xsl:text> + <xsl:text> const index = +</xsl:text> + <xsl:text> (this.is_relative && this.indexes.length > 0) ? +</xsl:text> + <xsl:text> this.indexes[0] + this.offset : undefined; +</xsl:text> + <xsl:text> this.button_beeing_pressed = false; +</xsl:text> + <xsl:text> this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed; +</xsl:text> + <xsl:text> fading_page_switch(this.args[0], index); +</xsl:text> + <xsl:text> this.notify(); </xsl:text> <xsl:text> } </xsl:text> @@ -5963,6 +6185,24 @@ </xsl:text> <xsl:text> </xsl:text> + <xsl:text> onmousedown(){ +</xsl:text> + <xsl:text> if(this.enable_state) { +</xsl:text> + <xsl:text> svg_root.addEventListener("pointerup", this.bound_onmouseup, true); +</xsl:text> + <xsl:text> this.button_beeing_pressed = true; +</xsl:text> + <xsl:text> this.activity_state = true; +</xsl:text> + <xsl:text> this.request_animate(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> <xsl:text> notify_page_change(page_name, index) { </xsl:text> <xsl:text> // called from animate() @@ -5973,7 +6213,9 @@ </xsl:text> <xsl:text> const ref_name = this.args[0]; </xsl:text> - <xsl:text> this.activity_state = ((ref_name == undefined || ref_name == page_name) && index == ref_index); + <xsl:text> this.target_page_is_current_page = ((ref_name == undefined || ref_name == page_name) && index == ref_index); +</xsl:text> + <xsl:text> this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed; </xsl:text> <xsl:text> // Since called from animate, update activity directly </xsl:text> @@ -6031,7 +6273,9 @@ <xsl:variable name="jump_disability" select="$has_activity and $has_disability"/> <xsl:text> init: function() { </xsl:text> - <xsl:text> this.element.onclick = this.make_on_click(); + <xsl:text> this.bound_onmouseup = this.onmouseup.bind(this); +</xsl:text> + <xsl:text> this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); </xsl:text> <xsl:if test="$has_activity"> <xsl:text> this.activable = true; @@ -11091,1000 +11335,1064 @@ </xsl:text> <xsl:text> </xsl:text> - <xsl:text>// Apply updates recieved through ws.onmessage to subscribed widgets -</xsl:text> - <xsl:text>function apply_updates() { -</xsl:text> - <xsl:text> updates.forEach((value, index) => { + <xsl:text>// Called on requestAnimationFrame, modifies DOM +</xsl:text> + <xsl:text>var requestAnimationFrameID = null; +</xsl:text> + <xsl:text>function animate() { +</xsl:text> + <xsl:text> let rearm = true; +</xsl:text> + <xsl:text> do{ +</xsl:text> + <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ +</xsl:text> + <xsl:text> if(page_fading == "pending") +</xsl:text> + <xsl:text> svg_root.classList.add("fade-out-page"); +</xsl:text> + <xsl:text> page_fading = "in_progress"; +</xsl:text> + <xsl:text> if(page_fading_args.length) +</xsl:text> + <xsl:text> setTimeout(function(){ +</xsl:text> + <xsl:text> switch_page(...page_fading_args); +</xsl:text> + <xsl:text> },1); +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // Do the page swith if pending +</xsl:text> + <xsl:text> if(page_switch_in_progress){ +</xsl:text> + <xsl:text> if(current_subscribed_page != current_visible_page){ +</xsl:text> + <xsl:text> switch_visible_page(current_subscribed_page); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> page_switch_in_progress = false; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(page_fading == "in_progress"){ +</xsl:text> + <xsl:text> svg_root.classList.remove("fade-out-page"); +</xsl:text> + <xsl:text> page_fading = "off"; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(jumps_need_update) update_jumps(); +</xsl:text> + <xsl:text> +</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> rearm = false; +</xsl:text> + <xsl:text> } while(0); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> requestAnimationFrameID = null; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(rearm) requestHMIAnimation(); +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function requestHMIAnimation() { +</xsl:text> + <xsl:text> if(requestAnimationFrameID == null){ +</xsl:text> + <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// Message reception handler +</xsl:text> + <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing +</xsl:text> + <xsl:text>// are stored until browser can compute next frame, DOM is left untouched +</xsl:text> + <xsl:text>ws.onmessage = function (evt) { +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let data = evt.data; +</xsl:text> + <xsl:text> let dv = new DataView(data); +</xsl:text> + <xsl:text> let i = 0; +</xsl:text> + <xsl:text> try { +</xsl:text> + <xsl:text> for(let hash_int of hmi_hash) { +</xsl:text> + <xsl:text> if(hash_int != dv.getUint8(i)){ +</xsl:text> + <xsl:text> throw new Error("Hash doesn't match"); +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text> i++; +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> while(i < data.byteLength){ +</xsl:text> + <xsl:text> let index = dv.getUint32(i, true); +</xsl:text> + <xsl:text> i += 4; +</xsl:text> + <xsl:text> let iectype = hmitree_types[index]; +</xsl:text> + <xsl:text> if(iectype != undefined){ +</xsl:text> + <xsl:text> let dvgetter = dvgetters[iectype]; +</xsl:text> + <xsl:text> let [value, bytesize] = dvgetter(dv,i); +</xsl:text> + <xsl:text> dispatch_value(index, value); +</xsl:text> + <xsl:text> i += bytesize; +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> throw new Error("Unknown index "+index); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // register for rendering on next frame, since there are updates +</xsl:text> + <xsl:text> } catch(err) { +</xsl:text> + <xsl:text> // 1003 is for "Unsupported Data" +</xsl:text> + <xsl:text> // ws.close(1003, err.message); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // TODO : remove debug alert ? +</xsl:text> + <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // force reload ignoring cache +</xsl:text> + <xsl:text> location.reload(true); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_blob(data) { +</xsl:text> + <xsl:text> if(data.length > 0) { +</xsl:text> + <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); +</xsl:text> + <xsl:text> }; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>const typedarray_types = { +</xsl:text> + <xsl:text> INT: (number) => new Int16Array([number]), +</xsl:text> + <xsl:text> BOOL: (truth) => new Int16Array([truth]), +</xsl:text> + <xsl:text> NODE: (truth) => new Int16Array([truth]), +</xsl:text> + <xsl:text> REAL: (number) => new Float32Array([number]), +</xsl:text> + <xsl:text> STRING: (str) => { +</xsl:text> + <xsl:text> // beremiz default string max size is 128 +</xsl:text> + <xsl:text> str = str.slice(0,128); +</xsl:text> + <xsl:text> binary = new Uint8Array(str.length + 1); +</xsl:text> + <xsl:text> binary[0] = str.length; +</xsl:text> + <xsl:text> for(let i = 0; i < str.length; i++){ +</xsl:text> + <xsl:text> binary[i+1] = str.charCodeAt(i); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> return binary; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> /* TODO */ +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_reset() { +</xsl:text> + <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>var subscriptions = []; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function subscribers(index) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> let res; +</xsl:text> + <xsl:text> if(entry == undefined){ +</xsl:text> + <xsl:text> res = new Set(); +</xsl:text> + <xsl:text> subscriptions[index] = [res,0]; +</xsl:text> + <xsl:text> }else{ +</xsl:text> + <xsl:text> [res, _ign] = entry; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> return res +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function get_subscription_period(index) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> if(entry == undefined) +</xsl:text> + <xsl:text> return 0; +</xsl:text> + <xsl:text> let [_ign, period] = entry; +</xsl:text> + <xsl:text> return period; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function set_subscription_period(index, period) { +</xsl:text> + <xsl:text> let entry = subscriptions[index]; +</xsl:text> + <xsl:text> if(entry == undefined){ +</xsl:text> + <xsl:text> subscriptions[index] = [new Set(), period]; +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> entry[1] = period; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>if(has_watchdog){ +</xsl:text> + <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable +</xsl:text> + <xsl:text> // Since dispatch directly calls change_hmi_value, +</xsl:text> + <xsl:text> // PLC will periodically send variable at given frequency +</xsl:text> + <xsl:text> subscribers(heartbeat_index).add({ +</xsl:text> + <xsl:text> /* type: "Watchdog", */ +</xsl:text> + <xsl:text> frequency: 1, +</xsl:text> + <xsl:text> indexes: [heartbeat_index], +</xsl:text> + <xsl:text> new_hmi_value: function(index, value, oldval) { +</xsl:text> + <xsl:text> apply_hmi_value(heartbeat_index, value+1); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> }); +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>var page_fading = "off"; +</xsl:text> + <xsl:text>var page_fading_args = "off"; +</xsl:text> + <xsl:text>function fading_page_switch(...args){ +</xsl:text> + <xsl:text> if(page_fading == "in_progress") +</xsl:text> + <xsl:text> page_fading = "forced"; +</xsl:text> + <xsl:text> else +</xsl:text> + <xsl:text> page_fading = "pending"; +</xsl:text> + <xsl:text> page_fading_args = args; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> requestHMIAnimation(); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text>document.body.style.backgroundColor = "black"; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// subscribe to per instance current page hmi variable +</xsl:text> + <xsl:text>// PLC must prefix page name with "!" for page switch to happen +</xsl:text> + <xsl:text>subscribers(current_page_var_index).add({ +</xsl:text> + <xsl:text> frequency: 1, +</xsl:text> + <xsl:text> indexes: [current_page_var_index], +</xsl:text> + <xsl:text> new_hmi_value: function(index, value, oldval) { +</xsl:text> + <xsl:text> if(value.startsWith("!")) +</xsl:text> + <xsl:text> fading_page_switch(value.slice(1)); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function svg_text_to_multiline(elt) { +</xsl:text> + <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function multiline_to_svg_text(elt, str, blank) { +</xsl:text> + <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function switch_langnum(langnum) { +</xsl:text> + <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> for (let translation of translations) { +</xsl:text> + <xsl:text> let [objs, msgs] = translation; +</xsl:text> + <xsl:text> let msg = msgs[langnum]; +</xsl:text> + <xsl:text> for (let obj of objs) { +</xsl:text> + <xsl:text> multiline_to_svg_text(obj, msg); +</xsl:text> + <xsl:text> obj.setAttribute("lang",langnum); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> return langnum; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// backup original texts +</xsl:text> + <xsl:text>for (let translation of translations) { +</xsl:text> + <xsl:text> let [objs, msgs] = translation; +</xsl:text> + <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>var lang_local_index = hmi_local_index("lang"); +</xsl:text> + <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); +</xsl:text> + <xsl:text>var langname_local_index = hmi_local_index("lang_name"); +</xsl:text> + <xsl:text>subscribers(lang_local_index).add({ +</xsl:text> + <xsl:text> indexes: [lang_local_index], +</xsl:text> + <xsl:text> new_hmi_value: function(index, value, oldval) { +</xsl:text> + <xsl:text> let current_lang = switch_langnum(value); +</xsl:text> + <xsl:text> let [langname,langcode] = langs[current_lang]; +</xsl:text> + <xsl:text> apply_hmi_value(langcode_local_index, langcode); +</xsl:text> + <xsl:text> apply_hmi_value(langname_local_index, langname); +</xsl:text> + <xsl:text> switch_page(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language +</xsl:text> + <xsl:text>function get_current_lang_code(){ +</xsl:text> + <xsl:text> return cache[langcode_local_index]; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function setup_lang(){ +</xsl:text> + <xsl:text> let current_lang = cache[lang_local_index]; +</xsl:text> + <xsl:text> let new_lang = switch_langnum(current_lang); +</xsl:text> + <xsl:text> if(current_lang != new_lang){ +</xsl:text> + <xsl:text> apply_hmi_value(lang_local_index, new_lang); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>setup_lang(); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function update_subscriptions() { +</xsl:text> + <xsl:text> let delta = []; +</xsl:text> + <xsl:text> for(let index in subscriptions){ +</xsl:text> + <xsl:text> let widgets = subscribers(index); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // periods are in ms +</xsl:text> + <xsl:text> let previous_period = get_subscription_period(index); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // subscribing with a zero period is unsubscribing +</xsl:text> + <xsl:text> let new_period = 0; +</xsl:text> + <xsl:text> if(widgets.size > 0) { +</xsl:text> + <xsl:text> let maxfreq = 0; +</xsl:text> + <xsl:text> for(let widget of widgets){ +</xsl:text> + <xsl:text> let wf = widget.frequency; +</xsl:text> + <xsl:text> if(wf != undefined && maxfreq < wf) +</xsl:text> + <xsl:text> maxfreq = wf; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(maxfreq != 0) +</xsl:text> + <xsl:text> new_period = 1000/maxfreq; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(previous_period != new_period) { +</xsl:text> + <xsl:text> set_subscription_period(index, new_period); +</xsl:text> + <xsl:text> if(index <= last_remote_index){ +</xsl:text> + <xsl:text> delta.push( +</xsl:text> + <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ +</xsl:text> + <xsl:text> new Uint32Array([index]), +</xsl:text> + <xsl:text> new Uint16Array([new_period])); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> send_blob(delta); +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function send_hmi_value(index, value) { +</xsl:text> + <xsl:text> if(index > last_remote_index){ </xsl:text> <xsl:text> dispatch_value(index, value); </xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(persistent_indexes.has(index)){ +</xsl:text> + <xsl:text> let varname = persistent_indexes.get(index); +</xsl:text> + <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> return; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let iectype = hmitree_types[index]; +</xsl:text> + <xsl:text> let tobinary = typedarray_types[iectype]; +</xsl:text> + <xsl:text> send_blob([ +</xsl:text> + <xsl:text> new Uint8Array([0]), /* setval = 0 */ +</xsl:text> + <xsl:text> new Uint32Array([index]), +</xsl:text> + <xsl:text> tobinary(value)]); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf +</xsl:text> + <xsl:text> // cache[index] = value; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function apply_hmi_value(index, new_val) { +</xsl:text> + <xsl:text> // Similarly to previous comment, taking decision to update based +</xsl:text> + <xsl:text> // on cache content is bad and can lead to inconsistency +</xsl:text> + <xsl:text> /*let old_val = cache[index];*/ +</xsl:text> + <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) +</xsl:text> + <xsl:text> send_hmi_value(index, new_val); +</xsl:text> + <xsl:text> return new_val; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>const quotes = {"'":null, '"':null}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function eval_operation_string(old_val, opstr) { +</xsl:text> + <xsl:text> let op = opstr[0]; +</xsl:text> + <xsl:text> let given_val; +</xsl:text> + <xsl:text> if(opstr.length < 2) +</xsl:text> + <xsl:text> return undefined; +</xsl:text> + <xsl:text> if(opstr[1] in quotes){ +</xsl:text> + <xsl:text> if(opstr.length < 3) +</xsl:text> + <xsl:text> return undefined; +</xsl:text> + <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ +</xsl:text> + <xsl:text> given_val = opstr.slice(2,opstr.length-1); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> given_val = Number(opstr.slice(1)); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> let new_val; +</xsl:text> + <xsl:text> switch(op){ +</xsl:text> + <xsl:text> case "=": +</xsl:text> + <xsl:text> new_val = given_val; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> case "+": +</xsl:text> + <xsl:text> new_val = old_val + given_val; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> case "-": +</xsl:text> + <xsl:text> new_val = old_val - given_val; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> case "*": +</xsl:text> + <xsl:text> new_val = old_val * given_val; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> case "/": +</xsl:text> + <xsl:text> new_val = old_val / given_val; +</xsl:text> + <xsl:text> break; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> return new_val; +</xsl:text> + <xsl:text>} +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>var current_visible_page; +</xsl:text> + <xsl:text>var current_subscribed_page; +</xsl:text> + <xsl:text>var current_page_index; +</xsl:text> + <xsl:text>var page_node_local_index = hmi_local_index("page_node"); +</xsl:text> + <xsl:text>var page_switch_in_progress = false; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function toggleFullscreen() { +</xsl:text> + <xsl:text> let elem = document.documentElement; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if (!document.fullscreenElement) { +</xsl:text> + <xsl:text> elem.requestFullscreen().catch(err => { +</xsl:text> + <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); +</xsl:text> <xsl:text> }); </xsl:text> - <xsl:text> updates.clear(); + <xsl:text> } else { +</xsl:text> + <xsl:text> document.exitFullscreen(); +</xsl:text> + <xsl:text> } </xsl:text> <xsl:text>} </xsl:text> <xsl:text> </xsl:text> - <xsl:text>// Called on requestAnimationFrame, modifies DOM -</xsl:text> - <xsl:text>var requestAnimationFrameID = null; -</xsl:text> - <xsl:text>function animate() { -</xsl:text> - <xsl:text> let rearm = true; -</xsl:text> - <xsl:text> do{ -</xsl:text> - <xsl:text> if(page_fading == "pending" || page_fading == "forced"){ -</xsl:text> - <xsl:text> if(page_fading == "pending") -</xsl:text> - <xsl:text> svg_root.classList.add("fade-out-page"); -</xsl:text> - <xsl:text> page_fading = "in_progress"; -</xsl:text> - <xsl:text> if(page_fading_args.length) -</xsl:text> - <xsl:text> setTimeout(function(){ -</xsl:text> - <xsl:text> switch_page(...page_fading_args); -</xsl:text> - <xsl:text> },1); -</xsl:text> - <xsl:text> break; + <xsl:text>function prepare_svg() { +</xsl:text> + <xsl:text> // prevents context menu from appearing on right click and long touch +</xsl:text> + <xsl:text> document.body.addEventListener('contextmenu', e => { +</xsl:text> + <xsl:text> toggleFullscreen(); +</xsl:text> + <xsl:text> e.preventDefault(); +</xsl:text> + <xsl:text> }); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> for(let eltid in detachable_elements){ +</xsl:text> + <xsl:text> let [element,parent] = detachable_elements[eltid]; +</xsl:text> + <xsl:text> parent.removeChild(element); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function switch_page(page_name, page_index) { +</xsl:text> + <xsl:text> if(page_switch_in_progress){ +</xsl:text> + <xsl:text> /* page switch already going */ +</xsl:text> + <xsl:text> /* TODO LOG ERROR */ +</xsl:text> + <xsl:text> return false; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> page_switch_in_progress = true; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(page_name == undefined) +</xsl:text> + <xsl:text> page_name = current_subscribed_page; +</xsl:text> + <xsl:text> else if(page_index == undefined){ +</xsl:text> + <xsl:text> [page_name, page_index] = page_name.split('@') +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let old_desc = page_desc[current_subscribed_page]; +</xsl:text> + <xsl:text> let new_desc = page_desc[page_name]; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(new_desc == undefined){ +</xsl:text> + <xsl:text> /* TODO LOG ERROR */ +</xsl:text> + <xsl:text> return false; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(page_index == undefined) +</xsl:text> + <xsl:text> page_index = new_desc.page_index; +</xsl:text> + <xsl:text> else if(typeof(page_index) == "string") { +</xsl:text> + <xsl:text> let hmitree_node = hmitree_nodes[page_index]; +</xsl:text> + <xsl:text> if(hmitree_node !== undefined){ +</xsl:text> + <xsl:text> let [int_index, hmiclass] = hmitree_node; +</xsl:text> + <xsl:text> if(hmiclass == new_desc.page_class) +</xsl:text> + <xsl:text> page_index = int_index; +</xsl:text> + <xsl:text> else +</xsl:text> + <xsl:text> page_index = new_desc.page_index; +</xsl:text> + <xsl:text> } else { +</xsl:text> + <xsl:text> page_index = new_desc.page_index; </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // Do the page swith if pending -</xsl:text> - <xsl:text> if(page_switch_in_progress){ -</xsl:text> - <xsl:text> if(current_subscribed_page != current_visible_page){ -</xsl:text> - <xsl:text> switch_visible_page(current_subscribed_page); + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(old_desc){ +</xsl:text> + <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> update_subscriptions(); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> current_subscribed_page = page_name; +</xsl:text> + <xsl:text> current_page_index = page_index; +</xsl:text> + <xsl:text> let page_node; +</xsl:text> + <xsl:text> if(page_index != undefined){ +</xsl:text> + <xsl:text> page_node = hmitree_paths[page_index]; +</xsl:text> + <xsl:text> }else{ +</xsl:text> + <xsl:text> page_node = ""; +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> apply_hmi_value(page_node_local_index, page_node); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> jumps_need_update = true; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> requestHMIAnimation(); +</xsl:text> + <xsl:text> jump_history.push([page_name, page_index]); +</xsl:text> + <xsl:text> if(jump_history.length > 42) +</xsl:text> + <xsl:text> jump_history.shift(); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined +</xsl:text> + <xsl:text> ? page_name +</xsl:text> + <xsl:text> : page_name + "@" + hmitree_paths[page_index]); +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> return true; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>function switch_visible_page(page_name) { +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> let old_desc = page_desc[current_visible_page]; +</xsl:text> + <xsl:text> let new_desc = page_desc[page_name]; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> if(old_desc){ +</xsl:text> + <xsl:text> for(let eltid in old_desc.required_detachables){ +</xsl:text> + <xsl:text> if(!(eltid in new_desc.required_detachables)){ +</xsl:text> + <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; +</xsl:text> + <xsl:text> parent.removeChild(element); </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> page_switch_in_progress = false; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(page_fading == "in_progress"){ -</xsl:text> - <xsl:text> svg_root.classList.remove("fade-out-page"); -</xsl:text> - <xsl:text> page_fading = "off"; + <xsl:text> } +</xsl:text> + <xsl:text> for(let eltid in new_desc.required_detachables){ +</xsl:text> + <xsl:text> if(!(eltid in old_desc.required_detachables)){ +</xsl:text> + <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; +</xsl:text> + <xsl:text> parent.appendChild(element); </xsl:text> <xsl:text> } </xsl:text> <xsl:text> } </xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(jumps_need_update) update_jumps(); -</xsl:text> - <xsl:text> -</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> rearm = false; -</xsl:text> - <xsl:text> } while(0); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> requestAnimationFrameID = null; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(rearm) requestHMIAnimation(); + <xsl:text> }else{ +</xsl:text> + <xsl:text> for(let eltid in new_desc.required_detachables){ +</xsl:text> + <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; +</xsl:text> + <xsl:text> parent.appendChild(element); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); +</xsl:text> + <xsl:text> current_visible_page = page_name; +</xsl:text> + <xsl:text>}; +</xsl:text> + <xsl:text> +</xsl:text> + <xsl:text>/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ +</xsl:text> + <xsl:text>function getAbsoluteCTM(element){ +</xsl:text> + <xsl:text> var height = svg_root.height.baseVal.value, +</xsl:text> + <xsl:text> width = svg_root.width.baseVal.value, +</xsl:text> + <xsl:text> viewBoxRect = svg_root.viewBox.baseVal, +</xsl:text> + <xsl:text> vHeight = viewBoxRect.height, +</xsl:text> + <xsl:text> vWidth = viewBoxRect.width; +</xsl:text> + <xsl:text> if(!vWidth || !vHeight){ +</xsl:text> + <xsl:text> return element.getCTM(); +</xsl:text> + <xsl:text> } +</xsl:text> + <xsl:text> var sH = height/vHeight, +</xsl:text> + <xsl:text> sW = width/vWidth, +</xsl:text> + <xsl:text> matrix = svg_root.createSVGMatrix(); +</xsl:text> + <xsl:text> matrix.a = sW; +</xsl:text> + <xsl:text> matrix.d = sH +</xsl:text> + <xsl:text> var realCTM = element.getCTM().multiply(matrix.inverse()); +</xsl:text> + <xsl:text> realCTM.e = realCTM.e/sW + viewBoxRect.x; +</xsl:text> + <xsl:text> realCTM.f = realCTM.f/sH + viewBoxRect.y; +</xsl:text> + <xsl:text> return realCTM; </xsl:text> <xsl:text>} </xsl:text> <xsl:text> </xsl:text> - <xsl:text>function requestHMIAnimation() { -</xsl:text> - <xsl:text> if(requestAnimationFrameID == null){ -</xsl:text> - <xsl:text> requestAnimationFrameID = window.requestAnimationFrame(animate); -</xsl:text> - <xsl:text> } + <xsl:text>function apply_reference_frames(){ +</xsl:text> + <xsl:text> const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); +</xsl:text> + <xsl:text> matches.forEach((group) => { +</xsl:text> + <xsl:text> let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); +</xsl:text> + <xsl:text> let ctm = getAbsoluteCTM(group); +</xsl:text> + <xsl:text> // zero translation part of CTM +</xsl:text> + <xsl:text> // to only apply rotation/skewing to offset vector +</xsl:text> + <xsl:text> ctm.e = 0; +</xsl:text> + <xsl:text> ctm.f = 0; +</xsl:text> + <xsl:text> let invctm = ctm.inverse(); +</xsl:text> + <xsl:text> let vect = new DOMPoint(x, y); +</xsl:text> + <xsl:text> let newvect = vect.matrixTransform(invctm); +</xsl:text> + <xsl:text> let transform = svg_root.createSVGTransform(); +</xsl:text> + <xsl:text> transform.setTranslate(newvect.x, newvect.y); +</xsl:text> + <xsl:text> group.transform.baseVal.appendItem(transform); +</xsl:text> + <xsl:text> ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); +</xsl:text> + <xsl:text> }); </xsl:text> <xsl:text>} </xsl:text> <xsl:text> </xsl:text> - <xsl:text>// Message reception handler -</xsl:text> - <xsl:text>// Hash is verified and HMI values updates resulting from binary parsing -</xsl:text> - <xsl:text>// are stored until browser can compute next frame, DOM is left untouched -</xsl:text> - <xsl:text>ws.onmessage = function (evt) { -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let data = evt.data; -</xsl:text> - <xsl:text> let dv = new DataView(data); -</xsl:text> - <xsl:text> let i = 0; -</xsl:text> - <xsl:text> try { -</xsl:text> - <xsl:text> for(let hash_int of hmi_hash) { -</xsl:text> - <xsl:text> if(hash_int != dv.getUint8(i)){ -</xsl:text> - <xsl:text> throw new Error("Hash doesn't match"); -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> i++; -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> while(i < data.byteLength){ -</xsl:text> - <xsl:text> let index = dv.getUint32(i, true); -</xsl:text> - <xsl:text> i += 4; -</xsl:text> - <xsl:text> let iectype = hmitree_types[index]; -</xsl:text> - <xsl:text> if(iectype != undefined){ -</xsl:text> - <xsl:text> let dvgetter = dvgetters[iectype]; -</xsl:text> - <xsl:text> let [value, bytesize] = dvgetter(dv,i); -</xsl:text> - <xsl:text> updates.set(index, value); -</xsl:text> - <xsl:text> i += bytesize; -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> throw new Error("Unknown index "+index); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> apply_updates(); -</xsl:text> - <xsl:text> // register for rendering on next frame, since there are updates -</xsl:text> - <xsl:text> } catch(err) { -</xsl:text> - <xsl:text> // 1003 is for "Unsupported Data" -</xsl:text> - <xsl:text> // ws.close(1003, err.message); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // TODO : remove debug alert ? -</xsl:text> - <xsl:text> alert("Error : "+err.message+"\nHMI will be reloaded."); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // force reload ignoring cache -</xsl:text> - <xsl:text> location.reload(true); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>hmi_hash_u8 = new Uint8Array(hmi_hash); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_blob(data) { -</xsl:text> - <xsl:text> if(data.length > 0) { -</xsl:text> - <xsl:text> ws.send(new Blob([hmi_hash_u8].concat(data))); -</xsl:text> - <xsl:text> }; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>const typedarray_types = { -</xsl:text> - <xsl:text> INT: (number) => new Int16Array([number]), -</xsl:text> - <xsl:text> BOOL: (truth) => new Int16Array([truth]), -</xsl:text> - <xsl:text> NODE: (truth) => new Int16Array([truth]), -</xsl:text> - <xsl:text> REAL: (number) => new Float32Array([number]), -</xsl:text> - <xsl:text> STRING: (str) => { -</xsl:text> - <xsl:text> // beremiz default string max size is 128 -</xsl:text> - <xsl:text> str = str.slice(0,128); -</xsl:text> - <xsl:text> binary = new Uint8Array(str.length + 1); -</xsl:text> - <xsl:text> binary[0] = str.length; -</xsl:text> - <xsl:text> for(let i = 0; i < str.length; i++){ -</xsl:text> - <xsl:text> binary[i+1] = str.charCodeAt(i); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> return binary; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> /* TODO */ -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_reset() { -</xsl:text> - <xsl:text> send_blob(new Uint8Array([1])); /* reset = 1 */ -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>var subscriptions = []; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function subscribers(index) { -</xsl:text> - <xsl:text> let entry = subscriptions[index]; -</xsl:text> - <xsl:text> let res; -</xsl:text> - <xsl:text> if(entry == undefined){ -</xsl:text> - <xsl:text> res = new Set(); -</xsl:text> - <xsl:text> subscriptions[index] = [res,0]; -</xsl:text> - <xsl:text> }else{ -</xsl:text> - <xsl:text> [res, _ign] = entry; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> return res -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function get_subscription_period(index) { -</xsl:text> - <xsl:text> let entry = subscriptions[index]; -</xsl:text> - <xsl:text> if(entry == undefined) -</xsl:text> - <xsl:text> return 0; -</xsl:text> - <xsl:text> let [_ign, period] = entry; -</xsl:text> - <xsl:text> return period; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function set_subscription_period(index, period) { -</xsl:text> - <xsl:text> let entry = subscriptions[index]; -</xsl:text> - <xsl:text> if(entry == undefined){ -</xsl:text> - <xsl:text> subscriptions[index] = [new Set(), period]; -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> entry[1] = period; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>if(has_watchdog){ -</xsl:text> - <xsl:text> // artificially subscribe the watchdog widget to "/heartbeat" hmi variable -</xsl:text> - <xsl:text> // Since dispatch directly calls change_hmi_value, -</xsl:text> - <xsl:text> // PLC will periodically send variable at given frequency -</xsl:text> - <xsl:text> subscribers(heartbeat_index).add({ -</xsl:text> - <xsl:text> /* type: "Watchdog", */ -</xsl:text> - <xsl:text> frequency: 1, -</xsl:text> - <xsl:text> indexes: [heartbeat_index], -</xsl:text> - <xsl:text> new_hmi_value: function(index, value, oldval) { -</xsl:text> - <xsl:text> apply_hmi_value(heartbeat_index, value+1); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> }); -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>var page_fading = "off"; -</xsl:text> - <xsl:text>var page_fading_args = "off"; -</xsl:text> - <xsl:text>function fading_page_switch(...args){ -</xsl:text> - <xsl:text> if(page_fading == "in_progress") -</xsl:text> - <xsl:text> page_fading = "forced"; -</xsl:text> - <xsl:text> else -</xsl:text> - <xsl:text> page_fading = "pending"; -</xsl:text> - <xsl:text> page_fading_args = args; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> requestHMIAnimation(); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text>document.body.style.backgroundColor = "black"; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// subscribe to per instance current page hmi variable -</xsl:text> - <xsl:text>// PLC must prefix page name with "!" for page switch to happen -</xsl:text> - <xsl:text>subscribers(current_page_var_index).add({ -</xsl:text> - <xsl:text> frequency: 1, -</xsl:text> - <xsl:text> indexes: [current_page_var_index], -</xsl:text> - <xsl:text> new_hmi_value: function(index, value, oldval) { -</xsl:text> - <xsl:text> if(value.startsWith("!")) -</xsl:text> - <xsl:text> fading_page_switch(value.slice(1)); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>}); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function svg_text_to_multiline(elt) { -</xsl:text> - <xsl:text> return(Array.prototype.map.call(elt.children, x=>x.textContent).join("\n")); -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function multiline_to_svg_text(elt, str, blank) { -</xsl:text> - <xsl:text> str.split('\n').map((line,i) => {elt.children[i].textContent = blank?"":line;}); -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function switch_langnum(langnum) { -</xsl:text> - <xsl:text> langnum = Math.max(0, Math.min(langs.length - 1, langnum)); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> for (let translation of translations) { -</xsl:text> - <xsl:text> let [objs, msgs] = translation; -</xsl:text> - <xsl:text> let msg = msgs[langnum]; -</xsl:text> - <xsl:text> for (let obj of objs) { -</xsl:text> - <xsl:text> multiline_to_svg_text(obj, msg); -</xsl:text> - <xsl:text> obj.setAttribute("lang",langnum); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> return langnum; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// backup original texts -</xsl:text> - <xsl:text>for (let translation of translations) { -</xsl:text> - <xsl:text> let [objs, msgs] = translation; -</xsl:text> - <xsl:text> msgs.unshift(svg_text_to_multiline(objs[0])); -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>var lang_local_index = hmi_local_index("lang"); -</xsl:text> - <xsl:text>var langcode_local_index = hmi_local_index("lang_code"); -</xsl:text> - <xsl:text>var langname_local_index = hmi_local_index("lang_name"); -</xsl:text> - <xsl:text>subscribers(lang_local_index).add({ -</xsl:text> - <xsl:text> indexes: [lang_local_index], -</xsl:text> - <xsl:text> new_hmi_value: function(index, value, oldval) { -</xsl:text> - <xsl:text> let current_lang = switch_langnum(value); -</xsl:text> - <xsl:text> let [langname,langcode] = langs[current_lang]; -</xsl:text> - <xsl:text> apply_hmi_value(langcode_local_index, langcode); -</xsl:text> - <xsl:text> apply_hmi_value(langname_local_index, langname); -</xsl:text> - <xsl:text> switch_page(); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>}); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>// returns en_US, fr_FR or en_UK depending on selected language -</xsl:text> - <xsl:text>function get_current_lang_code(){ -</xsl:text> - <xsl:text> return cache[langcode_local_index]; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function setup_lang(){ -</xsl:text> - <xsl:text> let current_lang = cache[lang_local_index]; -</xsl:text> - <xsl:text> let new_lang = switch_langnum(current_lang); -</xsl:text> - <xsl:text> if(current_lang != new_lang){ -</xsl:text> - <xsl:text> apply_hmi_value(lang_local_index, new_lang); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>setup_lang(); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function update_subscriptions() { -</xsl:text> - <xsl:text> let delta = []; -</xsl:text> - <xsl:text> for(let index in subscriptions){ -</xsl:text> - <xsl:text> let widgets = subscribers(index); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // periods are in ms -</xsl:text> - <xsl:text> let previous_period = get_subscription_period(index); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // subscribing with a zero period is unsubscribing -</xsl:text> - <xsl:text> let new_period = 0; -</xsl:text> - <xsl:text> if(widgets.size > 0) { -</xsl:text> - <xsl:text> let maxfreq = 0; -</xsl:text> - <xsl:text> for(let widget of widgets){ -</xsl:text> - <xsl:text> let wf = widget.frequency; -</xsl:text> - <xsl:text> if(wf != undefined && maxfreq < wf) -</xsl:text> - <xsl:text> maxfreq = wf; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(maxfreq != 0) -</xsl:text> - <xsl:text> new_period = 1000/maxfreq; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(previous_period != new_period) { -</xsl:text> - <xsl:text> set_subscription_period(index, new_period); -</xsl:text> - <xsl:text> if(index <= last_remote_index){ -</xsl:text> - <xsl:text> delta.push( -</xsl:text> - <xsl:text> new Uint8Array([2]), /* subscribe = 2 */ -</xsl:text> - <xsl:text> new Uint32Array([index]), -</xsl:text> - <xsl:text> new Uint16Array([new_period])); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> send_blob(delta); -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function send_hmi_value(index, value) { -</xsl:text> - <xsl:text> if(index > last_remote_index){ -</xsl:text> - <xsl:text> dispatch_value(index, value); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(persistent_indexes.has(index)){ -</xsl:text> - <xsl:text> let varname = persistent_indexes.get(index); -</xsl:text> - <xsl:text> document.cookie = varname+"="+value+"; max-age=3153600000"; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> return; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let iectype = hmitree_types[index]; -</xsl:text> - <xsl:text> let tobinary = typedarray_types[iectype]; -</xsl:text> - <xsl:text> send_blob([ -</xsl:text> - <xsl:text> new Uint8Array([0]), /* setval = 0 */ -</xsl:text> - <xsl:text> new Uint32Array([index]), -</xsl:text> - <xsl:text> tobinary(value)]); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> // DON'T DO THAT unless read_iterator in svghmi.c modifies wbuf as well, not only rbuf -</xsl:text> - <xsl:text> // cache[index] = value; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function apply_hmi_value(index, new_val) { -</xsl:text> - <xsl:text> // Similarly to previous comment, taking decision to update based -</xsl:text> - <xsl:text> // on cache content is bad and can lead to inconsistency -</xsl:text> - <xsl:text> /*let old_val = cache[index];*/ -</xsl:text> - <xsl:text> if(new_val != undefined /*&& old_val != new_val*/) -</xsl:text> - <xsl:text> send_hmi_value(index, new_val); -</xsl:text> - <xsl:text> return new_val; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>const quotes = {"'":null, '"':null}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function eval_operation_string(old_val, opstr) { -</xsl:text> - <xsl:text> let op = opstr[0]; -</xsl:text> - <xsl:text> let given_val; -</xsl:text> - <xsl:text> if(opstr.length < 2) -</xsl:text> - <xsl:text> return undefined; -</xsl:text> - <xsl:text> if(opstr[1] in quotes){ -</xsl:text> - <xsl:text> if(opstr.length < 3) -</xsl:text> - <xsl:text> return undefined; -</xsl:text> - <xsl:text> if(opstr[opstr.length-1] == opstr[1]){ -</xsl:text> - <xsl:text> given_val = opstr.slice(2,opstr.length-1); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> given_val = Number(opstr.slice(1)); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> let new_val; -</xsl:text> - <xsl:text> switch(op){ -</xsl:text> - <xsl:text> case "=": -</xsl:text> - <xsl:text> new_val = given_val; -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> case "+": -</xsl:text> - <xsl:text> new_val = old_val + given_val; -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> case "-": -</xsl:text> - <xsl:text> new_val = old_val - given_val; -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> case "*": -</xsl:text> - <xsl:text> new_val = old_val * given_val; -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> case "/": -</xsl:text> - <xsl:text> new_val = old_val / given_val; -</xsl:text> - <xsl:text> break; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> return new_val; -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>var current_visible_page; -</xsl:text> - <xsl:text>var current_subscribed_page; -</xsl:text> - <xsl:text>var current_page_index; -</xsl:text> - <xsl:text>var page_node_local_index = hmi_local_index("page_node"); -</xsl:text> - <xsl:text>var page_switch_in_progress = false; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function toggleFullscreen() { -</xsl:text> - <xsl:text> let elem = document.documentElement; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if (!document.fullscreenElement) { -</xsl:text> - <xsl:text> elem.requestFullscreen().catch(err => { -</xsl:text> - <xsl:text> console.log("Error attempting to enable full-screen mode: "+err.message+" ("+err.name+")"); -</xsl:text> - <xsl:text> }); -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> document.exitFullscreen(); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>} -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function prepare_svg() { -</xsl:text> - <xsl:text> // prevents context menu from appearing on right click and long touch -</xsl:text> - <xsl:text> document.body.addEventListener('contextmenu', e => { -</xsl:text> - <xsl:text> toggleFullscreen(); -</xsl:text> - <xsl:text> e.preventDefault(); -</xsl:text> - <xsl:text> }); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> for(let eltid in detachable_elements){ -</xsl:text> - <xsl:text> let [element,parent] = detachable_elements[eltid]; -</xsl:text> - <xsl:text> parent.removeChild(element); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function switch_page(page_name, page_index) { -</xsl:text> - <xsl:text> if(page_switch_in_progress){ -</xsl:text> - <xsl:text> /* page switch already going */ -</xsl:text> - <xsl:text> /* TODO LOG ERROR */ -</xsl:text> - <xsl:text> return false; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> page_switch_in_progress = true; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(page_name == undefined) -</xsl:text> - <xsl:text> page_name = current_subscribed_page; -</xsl:text> - <xsl:text> else if(page_index == undefined){ -</xsl:text> - <xsl:text> [page_name, page_index] = page_name.split('@') -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let old_desc = page_desc[current_subscribed_page]; -</xsl:text> - <xsl:text> let new_desc = page_desc[page_name]; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(new_desc == undefined){ -</xsl:text> - <xsl:text> /* TODO LOG ERROR */ -</xsl:text> - <xsl:text> return false; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(page_index == undefined) -</xsl:text> - <xsl:text> page_index = new_desc.page_index; -</xsl:text> - <xsl:text> else if(typeof(page_index) == "string") { -</xsl:text> - <xsl:text> let hmitree_node = hmitree_nodes[page_index]; -</xsl:text> - <xsl:text> if(hmitree_node !== undefined){ -</xsl:text> - <xsl:text> let [int_index, hmiclass] = hmitree_node; -</xsl:text> - <xsl:text> if(hmiclass == new_desc.page_class) -</xsl:text> - <xsl:text> page_index = int_index; -</xsl:text> - <xsl:text> else -</xsl:text> - <xsl:text> page_index = new_desc.page_index; -</xsl:text> - <xsl:text> } else { -</xsl:text> - <xsl:text> page_index = new_desc.page_index; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(old_desc){ -</xsl:text> - <xsl:text> old_desc.widgets.map(([widget,relativeness])=>widget.unsub()); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> const new_offset = page_index == undefined ? 0 : page_index - new_desc.page_index; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> const container_id = page_name + (page_index != undefined ? page_index : ""); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> new_desc.widgets.map(([widget,relativeness])=>widget.sub(new_offset,relativeness,container_id)); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> update_subscriptions(); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> current_subscribed_page = page_name; -</xsl:text> - <xsl:text> current_page_index = page_index; -</xsl:text> - <xsl:text> let page_node; -</xsl:text> - <xsl:text> if(page_index != undefined){ -</xsl:text> - <xsl:text> page_node = hmitree_paths[page_index]; -</xsl:text> - <xsl:text> }else{ -</xsl:text> - <xsl:text> page_node = ""; -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> apply_hmi_value(page_node_local_index, page_node); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> jumps_need_update = true; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> requestHMIAnimation(); -</xsl:text> - <xsl:text> jump_history.push([page_name, page_index]); -</xsl:text> - <xsl:text> if(jump_history.length > 42) -</xsl:text> - <xsl:text> jump_history.shift(); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> apply_hmi_value(current_page_var_index, page_index == undefined -</xsl:text> - <xsl:text> ? page_name -</xsl:text> - <xsl:text> : page_name + "@" + hmitree_paths[page_index]); -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> return true; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text>function switch_visible_page(page_name) { -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> let old_desc = page_desc[current_visible_page]; -</xsl:text> - <xsl:text> let new_desc = page_desc[page_name]; -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> if(old_desc){ -</xsl:text> - <xsl:text> for(let eltid in old_desc.required_detachables){ -</xsl:text> - <xsl:text> if(!(eltid in new_desc.required_detachables)){ -</xsl:text> - <xsl:text> let [element, parent] = old_desc.required_detachables[eltid]; -</xsl:text> - <xsl:text> parent.removeChild(element); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> for(let eltid in new_desc.required_detachables){ -</xsl:text> - <xsl:text> if(!(eltid in old_desc.required_detachables)){ -</xsl:text> - <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; -</xsl:text> - <xsl:text> parent.appendChild(element); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> }else{ -</xsl:text> - <xsl:text> for(let eltid in new_desc.required_detachables){ -</xsl:text> - <xsl:text> let [element, parent] = new_desc.required_detachables[eltid]; -</xsl:text> - <xsl:text> parent.appendChild(element); -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> } -</xsl:text> - <xsl:text> -</xsl:text> - <xsl:text> svg_root.setAttribute('viewBox',new_desc.bbox.join(" ")); -</xsl:text> - <xsl:text> current_visible_page = page_name; -</xsl:text> - <xsl:text>}; -</xsl:text> - <xsl:text> -</xsl:text> <xsl:text>// Once connection established </xsl:text> <xsl:text>ws.onopen = function (evt) { </xsl:text> + <xsl:text> apply_reference_frames(); +</xsl:text> <xsl:text> init_widgets(); </xsl:text> <xsl:text> send_reset(); diff -r 1cf21430ed4a -r 921f620577e8 svghmi/geometry.ysl2 --- a/svghmi/geometry.ysl2 Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/geometry.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -145,3 +145,16 @@ result """$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or (not(@Id = $groups/@id) and (func:intersect($g, .) > 0 ))]"""; } + +def "func:offset" { + param "elt1"; + param "elt2"; + const "g1", "$geometry[@Id = $elt1/@id]"; + const "g2", "$geometry[@Id = $elt2/@id]"; + const "result" vector { + attrib "x" value "$g2/@x - $g1/@x"; + attrib "y" value "$g2/@y - $g1/@y"; + } + result "exsl:node-set($result)"; +} + diff -r 1cf21430ed4a -r 921f620577e8 svghmi/inline_svg.ysl2 --- a/svghmi/inline_svg.ysl2 Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/inline_svg.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -42,6 +42,23 @@ attrib "{name()}" > «substring(., 2)» } +// remove "reference" and "frame" rectangles +svgtmpl "svg:rect[@inkscape:label='reference' or @inkscape:label='frame']", mode="inline_svg" { + // nothing +} + +svgtmpl "svg:g[svg:rect/@inkscape:label='frame']", mode="inline_svg" { + const "reference_rect","(../svg:rect | ../svg:g/svg:rect)[@inkscape:label='reference']"; + const "frame_rect","svg:rect[@inkscape:label='frame']"; + const "offset","func:offset($frame_rect, $reference_rect)"; + + xsl:copy { + attrib "svghmi_x_offset" value "$offset/vector/@x"; + attrib "svghmi_y_offset" value "$offset/vector/@y"; + apply "@* | node()", mode="inline_svg"; + } +} + ////// Clone unlinking // // svg:use (inkscape's clones) inside a widgets are diff -r 1cf21430ed4a -r 921f620577e8 svghmi/svghmi.js --- a/svghmi/svghmi.js Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/svghmi.js Thu Oct 06 10:02:46 2022 +0200 @@ -46,14 +46,6 @@ } }; -// Apply updates recieved through ws.onmessage to subscribed widgets -function apply_updates() { - updates.forEach((value, index) => { - dispatch_value(index, value); - }); - updates.clear(); -} - // Called on requestAnimationFrame, modifies DOM var requestAnimationFrameID = null; function animate() { @@ -126,14 +118,13 @@ if(iectype != undefined){ let dvgetter = dvgetters[iectype]; let [value, bytesize] = dvgetter(dv,i); - updates.set(index, value); + dispatch_value(index, value); i += bytesize; } else { throw new Error("Unknown index "+index); } }; - apply_updates(); // register for rendering on next frame, since there are updates } catch(err) { // 1003 is for "Unsupported Data" @@ -541,8 +532,49 @@ current_visible_page = page_name; }; +/* From https://jsfiddle.net/ibowankenobi/1mmh7rs6/6/ */ +function getAbsoluteCTM(element){ + var height = svg_root.height.baseVal.value, + width = svg_root.width.baseVal.value, + viewBoxRect = svg_root.viewBox.baseVal, + vHeight = viewBoxRect.height, + vWidth = viewBoxRect.width; + if(!vWidth || !vHeight){ + return element.getCTM(); + } + var sH = height/vHeight, + sW = width/vWidth, + matrix = svg_root.createSVGMatrix(); + matrix.a = sW; + matrix.d = sH + var realCTM = element.getCTM().multiply(matrix.inverse()); + realCTM.e = realCTM.e/sW + viewBoxRect.x; + realCTM.f = realCTM.f/sH + viewBoxRect.y; + return realCTM; +} + +function apply_reference_frames(){ + const matches = svg_root.querySelectorAll("g[svghmi_x_offset]"); + matches.forEach((group) => { + let [x,y] = ["x", "y"].map((axis) => Number(group.getAttribute("svghmi_"+axis+"_offset"))); + let ctm = getAbsoluteCTM(group); + // zero translation part of CTM + // to only apply rotation/skewing to offset vector + ctm.e = 0; + ctm.f = 0; + let invctm = ctm.inverse(); + let vect = new DOMPoint(x, y); + let newvect = vect.matrixTransform(invctm); + let transform = svg_root.createSVGTransform(); + transform.setTranslate(newvect.x, newvect.y); + group.transform.baseVal.appendItem(transform); + ["x", "y"].forEach((axis) => group.removeAttribute("svghmi_"+axis+"_offset")); + }); +} + // Once connection established ws.onopen = function (evt) { + apply_reference_frames(); init_widgets(); send_reset(); // show main page diff -r 1cf21430ed4a -r 921f620577e8 svghmi/widget_assign.ysl2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svghmi/widget_assign.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -0,0 +1,88 @@ +// widget_assign.ysl2 + +widget_desc("Assign") { + longdesc + || + + Arguments are either: + + - name=value: setting variable with literal value. + - name=other_name: copy variable content into another + + "active"+"inactive" labeled elements can be provided to show feedback when pressed + + Exemples: + + HMI:Assign:notify=1@notify=/PLCVAR + HMI:Assign:ack=2:notify=1@ack=.local_var@notify=/PLCVAR + + || + + shortdesc > Assign variables on click + +} + +widget_class("Assign") { +|| + frequency = 2; + + onmouseup(evt) { + svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); + if(this.enable_state) { + this.activity_state = false + this.request_animate(); + this.assign(); + } + } + + onmousedown(){ + if(this.enable_state) { + svg_root.addEventListener("pointerup", this.bound_onmouseup, true); + this.activity_state = true; + this.request_animate(); + } + } + +|| +} + +widget_defs("Assign") { + optional_activable(); + + | init: function() { + | this.bound_onmouseup = this.onmouseup.bind(this); + | this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); + | }, + + | assignments: {}, + | dispatch: function(value, oldval, varnum) { + const "widget", "."; + foreach "path" { + const "varid","generate-id()"; + const "varnum","position()-1"; + if "@assign" foreach "$widget/path[@assign]" if "$varid = generate-id()" { + | if(varnum == «$varnum») this.assignments["«@assign»"] = value; + } + } + | }, + | assign: function() { + const "paths","path"; + foreach "arg[contains(@value,'=')]"{ + const "name","substring-before(@value,'=')"; + const "value","substring-after(@value,'=')"; + const "index" foreach "$paths" if "@assign = $name" value "position()-1"; + const "isVarName", "regexp:test($value,'^[a-zA-Z_][a-zA-Z0-9_]+$')"; + choose { + when "$isVarName"{ + | const «$value» = this.assignments["«$value»"]; + | if(«$value» != undefined) + | this.apply_hmi_value(«$index», «$value»); + } + otherwise { + | this.apply_hmi_value(«$index», «$value»); + } + } + } + | }, +} + diff -r 1cf21430ed4a -r 921f620577e8 svghmi/widget_jump.ysl2 --- a/svghmi/widget_jump.ysl2 Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/widget_jump.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -52,23 +52,28 @@ || activable = false; frequency = 2; + target_page_is_current_page = false; + button_beeing_pressed = false; - make_on_click() { - let that = this; - const name = this.args[0]; - return function(evt){ - /* TODO: in order to allow jumps to page selected through - for exemple a dropdown, support path pointing to local - variable whom value would be an HMI_TREE index and then - jump to a relative page not hard-coded in advance - */ - if(that.enable_state) { - const index = - (that.is_relative && that.indexes.length > 0) ? - that.indexes[0] + that.offset : undefined; - fading_page_switch(name, index); - that.notify(); - } + onmouseup(evt) { + svg_root.removeEventListener("pointerup", this.bound_onmouseup, true); + if(this.enable_state) { + const index = + (this.is_relative && this.indexes.length > 0) ? + this.indexes[0] + this.offset : undefined; + this.button_beeing_pressed = false; + this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed; + fading_page_switch(this.args[0], index); + this.notify(); + } + } + + onmousedown(){ + if(this.enable_state) { + svg_root.addEventListener("pointerup", this.bound_onmouseup, true); + this.button_beeing_pressed = true; + this.activity_state = true; + this.request_animate(); } } @@ -77,7 +82,8 @@ if(this.activable) { const ref_index = this.indexes.length > 0 ? this.indexes[0] + this.offset : undefined; const ref_name = this.args[0]; - this.activity_state = ((ref_name == undefined || ref_name == page_name) && index == ref_index); + this.target_page_is_current_page = ((ref_name == undefined || ref_name == page_name) && index == ref_index); + this.activity_state = this.target_page_is_current_page || this.button_beeing_pressed; // Since called from animate, update activity directly if(this.enable_displayed_state && this.has_activity) { this.animate_activity(); @@ -98,7 +104,8 @@ const "jump_disability","$has_activity and $has_disability"; | init: function() { - | this.element.onclick = this.make_on_click(); + | this.bound_onmouseup = this.onmouseup.bind(this); + | this.element.addEventListener("pointerdown", this.onmousedown.bind(this)); if "$has_activity" { | this.activable = true; } diff -r 1cf21430ed4a -r 921f620577e8 svghmi/widgets_common.ysl2 --- a/svghmi/widgets_common.ysl2 Wed Oct 05 20:44:01 2022 +0200 +++ b/svghmi/widgets_common.ysl2 Thu Oct 06 10:02:46 2022 +0200 @@ -189,14 +189,13 @@ ]); var persistent_indexes = new Map(); var cache = hmitree_types.map(_ignored => undefined); - var updates = new Map(); function page_local_index(varname, pagename){ let pagevars = hmi_locals[pagename]; let new_index; if(pagevars == undefined){ new_index = next_available_index++; - hmi_locals[pagename] = {[varname]:new_index} + hmi_locals[pagename] = {[varname]:new_index}; } else { let result = pagevars[varname]; if(result != undefined) { @@ -209,7 +208,6 @@ let defaultval = local_defaults[varname]; if(defaultval != undefined) { cache[new_index] = defaultval; - updates.set(new_index, defaultval); if(persistent_locals.has(varname)) persistent_indexes.set(new_index, varname); }