📜 ⬆️ ⬇️

We write the mnemonic editor for SCADA-system on Fabric.js. Part 2

In the last article, we began to write the editor mnemonic. Today is an article about the finished editor. Here is what happened:



We stopped at the fact that when the SVG is loaded from a file, the elements with the transform = “translate (XY)” property are ungrouped with the resize pointers in the upper left corner, while the image itself is in X Y coordinates. . It was experimentally observed that if you set the coordinates to 0 0, remembering their transformMatrix before that, and then restore them to XY, the pointers for resizing will coincide with the image. And for this the following function was written:

Move the pointers to resize where necessary
function After_load() { var kol = 0; canvas.forEachObject(function(obj){ var transformMatrix1 = [1,0,0,1,0,0]; var str_x; var str_y; var tr_y; var tx_sg = obj.toSVG(); if ((tx_sg.indexOf('transform="translate')>=0) && (tx_sg.indexOf('<g ')>=0)) { obj.setOriginX('left'); obj.set('lockScalingX','false'); obj.set('lockScalingY','false'); } var transformMatrix2 = obj.get('transformMatrix'); var strokeWidth1 = obj.getStrokeWidth(); var m_x = obj.getLeft(); var m_y = obj.getTop(); var calcTransformMatrix2 = obj.calcTransformMatrix(); transformMatrix1[1] = transformMatrix2[1]; transformMatrix1[2] = transformMatrix2[2]; if (!((tx_sg.indexOf('transform="translate')>=0) && (tx_sg.indexOf('<g ')>=0))) { obj.setTransformMatrix(transformMatrix1); } obj.setTop(transformMatrix2[5]+m_y*transformMatrix2[3]);*/ if (tx_sg.indexOf('<line x1="0"')>=0){ obj.setLeft(transformMatrix2[4]+(m_x)*transformMatrix2[0]-(strokeWidth1/2)*transformMatrix2[0]);//(m_x-0.5)*transformMatrix2[0] obj.setTop(transformMatrix2[5]+(m_y)*transformMatrix2[3]-(strokeWidth1/2)*transformMatrix2[3]); //(m_y-0.5)*transformMatrix2[3] } else if ((tx_sg.indexOf('<rect ')>=0) || (tx_sg.indexOf('<polygon ')>=0) || (tx_sg.indexOf('<line x1="-')>=0)|| (tx_sg.indexOf('<circle cx=')>=0)) { obj.setLeft(transformMatrix2[4]+(m_x)*transformMatrix2[0]-(strokeWidth1/2)*transformMatrix2[0]);//(m_x-0.5)*transformMatrix2[0] obj.setTop(transformMatrix2[5]+(m_y)*transformMatrix2[3]-(strokeWidth1/2)*transformMatrix2[3]); //(m_y-0.5)*transformMatrix2[3] } else if (tx_sg.indexOf('<path ')>=0) { obj.setLeft(transformMatrix2[4]+(m_x-strokeWidth1/2)*transformMatrix2[0]); obj.setTop(transformMatrix2[5]+(m_y-strokeWidth1/2)*transformMatrix2[3]); } else if ((tx_sg.indexOf('transform="translate')>=0) && (tx_sg.indexOf('<g ')>=0))//if (tx_sg.indexOf('<g transform="translate')>=0) { var poz2 = tx_sg.indexOf("</tspan>"); var poz1 = tx_sg.indexOf("<tspan "); if ((poz2>0) && (poz1>0)) { var poz3 = tx_sg.indexOf(">",poz1+1); if (poz3>0){ var str = tx_sg.substring(poz3+1,poz2); } }// if ((poz2>0) && (poz1>0)) tx_sg = obj.toSVG(); poz2 = tx_sg.indexOf("</tspan>"); poz1 = tx_sg.indexOf("<tspan "); if ((poz2>0) && (poz1>0)) { var poz3 = tx_sg.indexOf('x="',poz1+1); if (poz3>0){ var poz4 = tx_sg.indexOf('"',poz3+4); str_x = tx_sg.substring(poz3+3,poz4); } poz3 = tx_sg.indexOf('y="',poz1+1); if (poz3>0){ var poz4 = tx_sg.indexOf('"',poz3+4); str_y = tx_sg.substring(poz3+3,poz4); } }// if ((poz2>0) && (poz1>0)) poz1 = tx_sg.indexOf('transform="translate('); if (poz1>0) { poz2 = tx_sg.indexOf(" ",poz1+21); var poz3 = tx_sg.indexOf(')',poz2+1); tr_y = tx_sg.substring(poz2,poz3); } m_x = obj.getLeft(); m_y = obj.getTop(); transformMatrix2 = obj.get('transformMatrix'); obj.setTransformMatrix(transformMatrix1); obj.setTop(transformMatrix2[5]-parseFloat(tr_y)-parseFloat(str_y)-strokeWidth1*0.58); obj.setLeft(transformMatrix2[4] + parseFloat(str_x) - (strokeWidth1/2) ); } else { obj.setLeft(transformMatrix2[4]+(m_x)*transformMatrix2[0]-(strokeWidth1/2)*transformMatrix2[0]);//(m_x-0.5)*transformMatrix2[0] obj.setTop(transformMatrix2[5]+(m_y)*transformMatrix2[3]-(strokeWidth1/2)*transformMatrix2[3]); //(m_y-0.5)*transformMatrix2[3] } obj.setScaleX(transformMatrix2[0]); obj.setScaleY(transformMatrix2[3]); obj.set('transparentCorners','true'); obj.setCoords(); kol = kol + 1; }); if (kol === 0) { $.post("After_load", kol); } else { Is_After_load = true; } }; 


After loading a document, canvas did not always contain elements. They appeared on it a little later. I had to do this:
')
Call this function 5 seconds after the page loads.
 setTimeout(function() { if (!Is_After_load){ After_load(); } }, 5000); 


Now from launching in the browser, let's move on to our own editor. HTML pages will be given through the idhttpserver component. For the display, we will use Chromium, or rather the DcefBrowser component. Idhttpserver opens port 15500, Chromium opens page 127.0.0.1 : 15500 /. You can, for example, open 127.0.0.1 : 15500 / with any other browser and edit it using it. Flipping your Chromium to the user, and not forcing him to use his browser, we give him a browser in which java-scripts are guaranteed to work as we need.

Index.html via idhttpserver we give a little modified:

  1. Canvas becomes the size that the user specified in the settings.
  2. The SVG file that the user is editing now is loaded.
  3. Custom SVG files from the image library are loaded.
  4. The list of variables received from OPC servers is populated.

Using the $.post and toSVG you can do the following things:

Saving schema in SVG:

 function Post_sheme() { $.post("save.php", canvas.toSVG()); }; 

In the http-server, we catch the post-request and save it in SVG ARequestInfo.UnparsedParams .
Adding SVG images to the library. Select several items, click on the button “Add selected to the library,” runs $.post("new.php?"+$("input[name=namenewsvg]").val(), Buff_clipb);
Where $("input[name=namenewsvg]").val() is the name of the SVG image.
Copy to operating system clipboard. Select several elements, click on the "copy" button, the following script is executed:

Spoiler header
 var tx = canvas.getActiveGroup().toSVG(); tx = '<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">' +tx+ '</svg>'; $.post("copy.php", tx); 


In the http-server, we catch the post-request and copy to the clipboard

 Clipboard.AsText := ARequestInfo.UnparsedParams; 

Now you can open another scheme and insert SVG into it. To do this, go to the tab “Load SVG” - paste the contents of the clipboard into the text box, click on the “Load” button.

We now turn to establishing links between the variable that receives its value from the OPC server and the SVG image.

Anchor text element is used to bind analog variables. It is necessary to create the text on the tab "text". Highlight it. On the Binding tab, select the variable name. The text will be {{val._}} . Those. text binding is carried out through the text content.

To display a discrete variable, you must use 2 elements. One will be used to display the enabled state, the other for the disabled state. Those. when the item is on, the item showing the enabled state will be visible. The element showing the disabled state will be invisible.

To bind an element that displays the enabled state, you must select it, in the drop-down list select __on . To bind an element that displays a disabled state, you need to select it, in the drop-down list, select __off . Arrows on the keyboard move the elements so that one is under the other.

The binding of discrete variables is carried out through id.
 function setIDObj() { var activeObject = canvas.getActiveObject(); if (activeObject) { activeObject.set({ id : $("input[name=nameobj]").val() }); var tx_sg = activeObject.toSVG(); var poz2 = tx_sg.indexOf("</tspan>"); var poz1 = tx_sg.indexOf("<tspan "); if ((poz2>0) && (poz1>0)) { var ttx = $("input[name=nameobj]").val(); if (ttx.indexOf("{{")<=0) { ttx = "{{val."+ttx+"}}"; } activeObject.set({ text: ttx }); canvas.renderAll(); } } }; 


Now about drawing. Many graphic editors allow you to save in SVG format. For now, I use the following scheme.



I open an existing scheme in Visio, copy it in Inkscape, save it in SVG, open it in a notebook, copy it, paste it in the editor via the tab “Load SVG” - text field - the “download” button.

From Visio to Inkscape you have to copy, because Visio creates an SVG that FabricJS cannot render normally.

You can view the editor online without saving here .

Download the SCADA prototype with the editor here .

And in the next article we will animate the drawn scheme.

Source: https://habr.com/ru/post/325640/


All Articles