Objects Savings in TwinCAT HMI

The Configuration window is the central configuration window in the TwinCAT HMI. It offers an overview of all elements in the project and enables the configuration of these elements. Furthermore, the user management and all data types can be configured via the window. 

In this article, we shall examine how to update and manage a JavaScript object. If you have issues to understand JavaScript you could review the page at https://www.hemelix.com/scada-hmi/twincat-hmi/twincat-hmi-async-function/

Download the sample

We shall save a simple object and update it by the read and write method with the following key-value pair.

var address = {
    'building no': 3960,
    street: 'North 1st street',
    state: 'CA',
    country: 'USA'
};

The UI is shown in the following image. The ID for all those controls (which we are manipulating) is as follows:

Building => TcHmiTextbox_Building (the textBox control)

Street => TcHmiTextbox_Street (the textBox control)

State => TcHmiTextbox_State (the textBox control)

Country => TcHmiTextbox_Country (the textBox control)

TO/From HMI Server => TcHmiCheckbox (checkbox control, should we load from the object or HMI Server)

Read => TcHmiButton_Read (Read the object)

Write => TcHmiButton_Write (Write the object)

Figure 01: The test application UI

We declare the address variable in a code behind the file. The code behind the file will be loaded first so we have a chance to declare it. These 4 textboxes are updated from the JavaScript object and vice versa. We shall update the address field and save the address object to HMI Server and read it back. Let’s do the following steps:

=> Create an HMI project name HMIObjectTest

=> Bring the all controls as shown in the above image ( 4 textblocks, 4 testboxes, 1 checkbox, and 2 buttons)

=> Rename those control IDs as mentioned previously.

=> Add a code behind the JavaScript file (Select Project | Add New Item (from the context menu) add CodeBehind1.js file)

=> Add the following code at the beginning of the file

function ReadDataFromServer(symbolParameter) {
    return new Promise((resolve, reject) => {
        var symbol = new TcHmi.Symbol(symbolParameter);
        symbol.readEx(function (data) {
            if (data.error === TcHmi.Errors.NONE) {
                resolve(data.value);
            } else {
                console.log(`Error in ReadDataFromServer ${symbolParameter}`);
                console.log(`data.details.code = ${data.details.code}`);
                reject(data.error);
            }
        });
    });
}
function WriteDataToServer(symbolParameter, dataParameter) {
    return new Promise((resolve, reject) => {
        var symbol = new TcHmi.Symbol(symbolParameter);
        TcHmi.Symbol.writeEx(symbolParameter, dataParameter, function (data) {
            if (data.error === TcHmi.Errors.NONE) {
                resolve(data.error);
            } else {
                console.log(`Error in WriteDataToServer ${symbolParameter}, ${dataParameter} `);
                console.log(`data.details.code = ${data.details.code}`);
                reject(data.error);
            }
        });
    });
}
var address = {
    'building no': 3960,
    street: 'North 1st street',
    state: 'CA',
    country: 'USA'
};
=> Build and run the application, all should be OK
Now we do the actual fun part.

Figure 02: Full development view of the sample application

=> Select the Country textblock control and bring the event editor (F4 | Show Events, lighting bolt)

=> Press on the pencil icon, see the next image

Figure 03: Show Events and .onAttached Event

Figure 04: Steps for inserting JavaScript code

=> Drag and drop the JavaScript from the left pane to the right pane 2 times for inserting JavaScript code, the first one for initializing the controls in our UI, 2nd one for doing some extra experiments related to asynchronous function calls (more in the JavaScript sections)

=> Press on the first Edit Source Code and get an editor and insert the following code (make sure IDs are the same and include some error checking)

var textBuildingControl = TcHmi.Controls.get('TcHmiTextbox_Building');
var textStreetControl = TcHmi.Controls.get('TcHmiTextbox_Street');
var textStateControl = TcHmi.Controls.get('TcHmiTextbox_State');
var textCountryControl = TcHmi.Controls.get('TcHmiTextbox_Country');
textBuildingControl.setText(address['building no']);
textStreetControl.setText(address.street);
textStateControl.setText(address.state);
textCountryControl.setText(address.country);
console.log('Attached Read Done!');

=> Create a HMI server symbol (name serverObject) as described at https://www.hemelix.com/scada-hmi/twincat-hmi/twincat-hmi-data-savings/, Datatype must be Object type.

=> Press on the second Edit Source Code and get an editor and insert the following code (we shall observe the behavior of async call)

console.log('#1JS code with Attached event');
async function LoadData() {
    try {
 var dataRead = await ReadDataFromServer('%s%serverObject%/s%');
console.log(`#2JS dataRead = ${dataRead}` );
console.log(`#3JS dataRead = ` + JSON.stringify(dataRead) );
let x = {greeting: "hello"};
let str = "I would like to say the greeting " + x.greeting;
console.log('#4JS '+str);
        return dataRead;
    }
    catch (error) {
        console.log(`#5JS error occurred while calling LoadData error = ${error}` );
    }
}
console.log('#6JS *****************');
var vv = LoadData();
console.log(`#7JS vv = ${vv}` );
console.log(`#8JS vv = ` + JSON.stringify(vv) );
vv.then((value) => {
  console.log(value);
  console.log(`#9JS value = ` + JSON.stringify(value) );
});

=> Now run the application and see that the fields are updated with the address defined in the code behind file (address object)

=> Observe that we have added some tags to the above code for the following call sequences (#1 etc)

=> Now open the debug console and examine the output, why those #1, #2, etc are not in order?

Figure 05: Output of JavaScript asynchronous call (for understanding)

We are not talking more about asynchronous functions here, if you have questions ask our Google group and check the JavaScript section. Now we continue developing the application.

=> Press on the Read button and bring the Show Event tab, edit .onPressed event, and insert the following code. Note that we are missing many error checking for simplification.

console.log('handleReadButtonPress');
var textBuildingControl = TcHmi.Controls.get('TcHmiTextbox_Building');
var textStreetControl = TcHmi.Controls.get('TcHmiTextbox_Street');
var textStateControl = TcHmi.Controls.get('TcHmiTextbox_State');
var textCountryControl = TcHmi.Controls.get('TcHmiTextbox_Country');
var checkBoxControl = TcHmi.Controls.get('TcHmiCheckbox');
async function handleReadButtonPress() {
  try {
var toggleState = checkBoxControl.getToggleState();
if(toggleState == 'Normal') {
textBuildingControl.setText(address['building no']);
textStreetControl.setText(address.street);
textStateControl.setText(address.state);
textCountryControl.setText(address.country);
} else {
var dataRead = await ReadDataFromServer('%s%serverObject%/s%');
textBuildingControl.setText(dataRead['building no']);
textStreetControl.setText(dataRead.street);
textStateControl.setText(dataRead.state);
textCountryControl.setText(dataRead.country);
}
  }
  catch(error) {
    console.log(`error occured in handleReadButtonPress = ${error}`);
  }
}
handleReadButtonPress();

=> Do the same for the Write button and add the following code

console.log('handleWriteButtonPress');
var textBuildingControl = TcHmi.Controls.get('TcHmiTextbox_Building');
var textStreetControl = TcHmi.Controls.get('TcHmiTextbox_Street');
var textStateControl = TcHmi.Controls.get('TcHmiTextbox_State');
var textCountryControl = TcHmi.Controls.get('TcHmiTextbox_Country');
var checkBoxControl = TcHmi.Controls.get('TcHmiCheckbox');
async function handleWriteButtonPress() {
  try {
var toggleState = checkBoxControl.getToggleState();
    var textBuilding = textBuildingControl.getText();
    var textStreet = textStreetControl.getText();
    var textState = textStateControl.getText();
    var textCountry = textCountryControl.getText();
if(toggleState == 'Normal') {
        address['building no'] = textBuilding;
        address['street'] = textStreet;
        address['state'] = textState;
        address['country'] = textCountry;
} else {
        let address2 = {
            'building no': textBuilding,
            street: textStreet,
            state: textState,
            country: textCountry
            };        
var dataRead = await WriteDataToServer('%s%serverObject%/s%', address2);
        console.log(`Write data to server dataRead = ${dataRead}`);
}
  }
  catch(error) {
    console.log(`error occured in handleWriteButtonPress = ${error}`);
  }
}
handleWriteButtonPress();

How the sample works

If we checked the TO/From HMI Server, the data will be read from the server otherwise it will be read from the global JavaScript object. The same is true for the write button. If the checkbox is checked it will write to the HMI Server otherwise it will update the global JavaScript object.

When we start the application it will read data from the global object and display it on the UI. We can change the UI content and save it by write button and Read back.

Download

Download the sample from the link given above and share it please!

References

Download the sample from the link given above.

See next our Linear Gauge example at https://www.hemelix.com/scada-hmi/twincat-hmi/twincat-hmi-historize-symbol/

Ask questions related to Hemelix sample code and design at Google group https://groups.google.com/g/hemelix