More on JavaScript and UserControl

Example 1 : Changing text color of a text block in UserControl

 

Design of the user control:

=>Make a user control with a text block

=>Drag and drop it to the desktop view

=>Configure text block in the user control as shown in the following figure

=>Configure the .onAttached events

See the code for ChangeMyTextColor function:

(function (TcHmi) {
    var ChangeMyTextColor = function (par1) {
        var myColorRed = {
            color: "red" // or color:"rgba(255, 0, 0, 1)"

        };
        var myColorWhite = {
            color: 'white' //or color:"rgba(255, 255, 255, 1)"
        };
        console.log(`par1 = ${par1}`);
        if (par1 == true) {
            return myColorRed;
        } else {
            return myColorWhite;
        }
    };    
    TcHmi.Functions.registerFunction('ChangeMyTextColor', ChangeMyTextColor);
})(TcHmi);

 

See the code for FunctionJS1 function

(function (TcHmi) {
    var FunctionJS1 = function (PressureValue) {
        var ss = PressureValue.toString();
        return ss;
    };    
    TcHmi.Functions.registerFunction('FunctionJS1', FunctionJS1);
})(TcHmi);

See the related PLC code

//Header
PROGRAM MAIN
VAR
dataReal : REAL := 0.0;
anyAlarm : BOOL := FALSE;
END_VAR
//Body
IF  dataReal < 200.5  AND dataReal > -0.1 THEN
anyAlarm := FALSE;
ELSE
anyAlarm := TRUE;
END_IF

Example 2: Showing a popup when click on user control and make change to the symbol

We shall continue working on the previous project. 

Updated PLC code:

 

 


//Header
PROGRAM MAIN
VAR
dataRealPLC : REAL := 0.0;
anyAlarm : BOOL := FALSE;
dataInt : INT := 0;
machineTypeInt : INT :=0;  //New
myTimer : TON ;
startTimer : BOOL := FALSE;
END_VAR

//Body of the updated program
IF  dataRealPLC > 200.0  OR dataRealPLC < 0.0 THEN
anyAlarm := TRUE;
ELSE
anyAlarm := FALSE;
END_IF
myTimer(IN := startTimer, PT := T#100MS);
IF (myTimer.Q = TRUE)  THEN
myTimer(IN := FALSE);
dataInt := dataInt +4;
END_IF
IF  dataInt > 360  THEN
dataInt := 0;
END_IF

 

 

What the sample will do?

1 => a simple edit box where we can insert a real number and then if we press on the button that will be written to PLC and it will be updated to the dialog. If the dialog is open then the number will be updated to the edit box (5).

2 => the button takes the value  and write it to the user control 3 as well as to the PLC.

3 => whenever we change the value in the edit box (1 or in edit box 5) that will be changed to PLC as well as to this control. If we do mouse press on this control (3) a dialog will be open. The dialog can be drag anywhere in the desktop (4, Hello World) and can be close by clicking on the dialog header (8).

4 => Header of the dialog, can be drag by mouse press around Desktop.

5 => a simple edit box, same content will be displayed here as well here. Content of edit box 1, edit box 5 will be the same. If the value is above 200 or less than 0 then there will be an active alarm. The alarm will go away if we reset the value in the specified range (0 to 200).

6 => an image passed as a parameter. If we press on the image then it will change the icon as well as machineTypeInt variable in the PLC. The image will swap.

7 => Start/Stop button, if we press on the button then startTimer variable will be true and the dataInt will start increasing till 360, the image will start rotating (We could have used single text start or stop based on the rotation status, but anyway it is straight forward work).

8 => the dialog can be closed 

 

In the above image, we are showing 3 dialogs.

Download the project -> ask by email please.

Design steps:

 STEP 01:

Make sure when we press on the WriteReal button then PLC real variable is updated. The linking process is shown in the following image.

STEP 02:

Example 3: Showing a popup for the different setting popup

In the above example, we have explained how to show similar settings pop up for all devices. We assumed that all the settings are similar for all devices. But let’s say, we have different devices which need a different kind of settings. In this sample, we shall how to pop up different settings and explain them.

Updated PLC code:

 

//Header
PROGRAM MAIN VAR dataRealPLC : REAL := 0.0; anyAlarm : BOOL := FALSE; dataInt : INT := 0; machineTypeInt : INT :=0; myTimer : TON ; startTimer : BOOL := TRUE; machineSettingInt : INT :=0; //for device 1 machineSettingReal : REAL :=0; //for device 2 machineSettingString : STRING ; //for device 3 END_VAR

 

//Body of the updated program


IF  dataRealPLC > 200.0  OR dataRealPLC < 0.0 THEN
anyAlarm := TRUE;
ELSE
anyAlarm := FALSE;
END_IF
myTimer(IN := startTimer, PT := T#5000MS);
IF (myTimer.Q = TRUE)  THEN
myTimer(IN := FALSE);
dataInt := dataInt +4;
machineSettingInt := machineSettingInt +1;
machineSettingReal := machineSettingReal +0.25;
machineSettingString:= CONCAT('Hemelix ', INT_TO_STRING(machineSettingInt));
END_IF
IF  dataInt > 360  THEN
dataInt := 0; //Used for rotation of an image in other example
machineSettingInt := 0;
machineSettingReal := 0; //Just reset something to avoid over flow
END_IF

 

 

Three different types of settings dialog are shown. Though they look similar, those are independent popups. In this case, device 1 can have 2 settings, device 2 can have 4 settings, etc. We can handle these with a single popup as well but in this case, we have to hide fields based on the popup. In this case, we implement a single type of popup for each device.

 

In the above image, we are showing 3 dialogs.

Download the project 

Design steps:

 STEP 01: 

=> Name the setting name as header1 (for one similar set up settings for our first type is MachineOneSettingPopup.usercontrol)

=> more coming soon…



(function (TcHmi) {
    var OpenMachineOnePopup = function (PopupContent,SettingName,WhereToInsert,SettingIntegerData) {        
        var popupParametersControl = [];
        var PopupUuid = String("Popup_" + TcHmiEx.Utilities.generateUuidv4());
        var popup;
        var popupType = PopupContent.split(".");
        popupType = popupType[popupType.length - 1].toLowerCase();
        popupParametersControl['data-tchmi-left'] = TcHmiEx.Utilities.ClickCoordinates.x;
        popupParametersControl['data-tchmi-top'] = TcHmiEx.Utilities.ClickCoordinates.y;
        popupParametersControl['data-tchmi-height'] = 400;
        popupParametersControl['data-tchmi-width'] = 670;
        popupParametersControl['data-tchmi-zindex'] = "1000";
        popupParametersControl['data-tchmi-settingnameone'] = SettingName;
        var myControl = TcHmi.Controls.get(SettingName);
        if (myControl) {
            var symbolString = TcHmi.Binding.resolveEx('SettingInt', myControl);
            if (symbolString) {
                popupParametersControl['data-tchmi-settingint'] = symbolString.toString();
            }
        }
        if (WhereToInsert == undefined) { WhereToInsert = "Desktop" }
        if (popupType == "usercontrol") {
            popupParametersControl['data-tchmi-target-user-control'] = PopupContent;
            popup = TcHmi.ControlFactory.createEx(
                'tchmi-user-control-host',
                PopupUuid,
                popupParametersControl
            );
        }
        var desktop = TcHmi.Controls.get(WhereToInsert.__id);
        if (desktop && popup) {
            desktop.addChild(popup);
            TcHmiEx.Utilities.dragElement(document.getElementById(PopupUuid));
        }
        return;
    };
    
    TcHmi.Functions.registerFunction('OpenMachineOnePopup', OpenMachineOnePopup);
})(TcHmi);


Implementation of dragElement method

// Make elements draggable
TcHmiExampleCollection.Utilities.dragElement = function (elmnt) {
    var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
    console.log(`elmnt.id = ${elmnt.id}`);
    //console.log(`par1 = ${par1}`);
    if (document.getElementById(elmnt.id + ".header1")) {
        // if present, the header3 is where you move the DIV from:
        document.getElementById(elmnt.id + ".header1").onmousedown = dragMouseDown;
        //document.getElementById(elmnt.id + ".header1").ontouchstart = dragMouseDown;
    } else if (document.getElementById(elmnt.id + ".header2")) {
        // if present, the header3 is where you move the DIV from:
        document.getElementById(elmnt.id + ".header2").onmousedown = dragMouseDown;
        //document.getElementById(elmnt.id + ".header3").ontouchstart = dragMouseDown;
    } else if (document.getElementById(elmnt.id + ".header3")) {
        // if present, the header3 is where you move the DIV from:
        document.getElementById(elmnt.id + ".header3").onmousedown = dragMouseDown;
        //document.getElementById(elmnt.id + ".header3").ontouchstart = dragMouseDown;
    } else {
        // otherwise, move the DIV from anywhere inside the DIV: 
        elmnt.onmousedown = dragMouseDown;
        //elmnt.ontouchstart = dragMouseDown;
    }

STEP 02:




Know more about Z-Index : https://bitsofco.de/how-z-index-works/


JavaScript Binding with Control Property:

We shall go through what is binding between a HMI control property is related to symbol expression. We shall explain with an example. 

We have a desktop with 4 elements, now we make a JavaScript function and pass 2 controls.  We are getting parent information of the first element and then we see that we have really 4 elements on the desktop by JS. If we press on the content then we call the JS function where we can check some interested properties.

Text property of the TextBox control has been bound with the server symbol (%s%PLC1.MAIN.dataInt%/s%), so when we call the method TcHmi.Binding.resolveEx then we get the symbol expression. We can create symbol expression by code as well as shown in the next code snippet.

Execute the following code:

Do an analysis about ObjectParameter (ask width, height, parent etc). When we get the parent information we ask how many controls we have on the desktop.

    
(function (TcHmi) {

    var TestJavaScriptResolving = function (ObjectParameter, myControl) {

        console.log(typeof ObjectParameter);
        console.log(Object.keys(ObjectParameter));
        console.log(ObjectParameter.__top);  // top 128
        console.log(ObjectParameter.__width);  // width 150
        console.log(ObjectParameter.__parent.__id.toString());  // Desktop

        var myControl1 = TcHmi.Controls.get(ObjectParameter.__parent.__id.toString());
        if (myControl1) {
            console.log(ObjectParameter.__parent.__id.toString() + ' found'); // Desktop found
            console.log(`Left = ${myControl1.__left}`);

            var symbolExpression1 = TcHmi.Binding.resolveEx('Left', myControl1);
            if (symbolExpression1) {
                console.log('Left  and Desktop are bound');
            } else {
                console.log('Left  and Desktop are not bound');
            }

            var list = myControl1.__children;
            //See which are present on the desktop
            console.log(`Total element present on Desktop = ${list.length}`);
            for (let value of Object.values(list)) {
                myControl1 = TcHmi.Controls.get(value.__id.toString(list.values));
                if (myControl1) {
                    console.log(value.__id.toString() + ' found on Desktop');
                }
            }
        }


        var myControl = TcHmi.Controls.get('TcHmiTextbox_Testing');
        if (myControl) {

            TcHmi.Binding.createEx('%i%MyInternalSymbol%/i%', 'setText', myControl);
        }

        myControl = TcHmi.Controls.get('TcHmiTextbox_Testing');
        if (myControl) {
            var symbolExpression = TcHmi.Binding.resolveEx('Text', myControl);
            if (symbolExpression) {
                console.log(myControl.getId() + '::Text is bound to symbol expression: ' + symbolExpression.toString());
            } else {
                //console.log('No binding');
            }
        }

        var testControl = TcHmi.Controls.get('TcHmiTextbox_BindingTest');
        if (testControl) {

            console.log(' TcHmiTextbox_BindingTest  testControl found');

            var symbolExpression22 = TcHmi.Binding.resolveEx('Text', testControl);
            if (symbolExpression22) {
                console.log(testControl.getId() + ' Text is bound to symbol expression: ' + symbolExpression22.toString());
            } else {
                console.log('No binding');
            }
        }

    };
    
    TcHmi.Functions.registerFunction('TestJavaScriptResolving', TestJavaScriptResolving);
})(TcHmi);

Result will be shown as follows:

The interesting point to know:

TcHmiTextbox_BindingTest has been bound by the PLC variable (%s%PLC1.MAIN.dataInt%/s%)  and that we have checked with var symbolExpression22 = TcHmi.Binding.resolveEx(‘Text’, testControl); and the console output shows the same.

 

 

Download the sample

 

Important HMI Methods:

TcHmi.Binding.resolveEx:

Create a UI application and configure the button with the following code:

Create an internal server variable and init it with 12345

var myControl = TcHmi.Controls.get('TcHmiTextbox');
if(myControl){
    TcHmi.Binding.createEx('%i%MyInternalSymbol%/i%', 'setText', myControl);
}
 myControl = TcHmi.Controls.get('TcHmiTextbox');
if(myControl){
    var symbolExpression = TcHmi.Binding.resolveEx('Text',myControl);
    if(symbolExpression){
         // Binding exists
         console.log(myControl.getId() + '::Text is bound to symbol expression: ' + symbolExpression.toString());
    } else {
         // Binding exists not
         console.log('No binding');
    }
}

If we press on the button we have the following output in the console:

TcHmiTextbox::Text is bound to symbol expression: %i%MyInternalSymbol%/i%

How to Print Formatted String in Console by JavaScript:

 console.log(`par1 = ${par1}`);

The output will be  as par1 is Boolean variable.

par1 = true

Useful links:

Learn JavaScript (https://www.w3schools.com/js/default.asp)

Debug JavaScript (https://developers.google.com/web/tools/chrome-devtools/javascript)

https://javascript.info/object

https://www.javascripttutorial.net/object/convert-an-object-to-an-array-in-javascript/

https://api.jquery.com/