Template Basics

How to get started with writing a simple template?

Let's have a look at the basic concepts in creating a template.

State Object

Every IoT device has several important parameters to be reported to the cloud platform. An example could be a temperature value read through a sensor or the current CPU load. The device has also some internal parameters such as available SSD and memory usage, which are required to be tracked. While modelling the device, you will need a local store to save these values and any other intermediate variable which needs to be preserved through the life of the simulation. state is a JSON object which can be used to save device status. E.g. consider following code snippet of a simple template:

{
// if temperature has not been defined previously
if (state.temperature === undefined ) {
state.temperature = 50;
}
state.temperature++;
return JSON.stringify(state);
}

In the very first iteration when this function is invoked, the state object will be empty. The if condition checks this and initializes the temperature with a default value of 50. Since state is preserved across iterations, upon next call, the temperature value will be found (to be set 51) and hence in each subsequent call of the function, the temperature variable will increment by 1 and returned as a payload. Here is how the first payload sent from this function will look like

{
temperature: 51
}

The state object is available through all of the function call. It is important not to use a similar named variable in your code to avoid confusion and namespace conflicts.

Sometimes state may hold variables which are purely for internal use and should not be sent back to the backend. In that case you could simply copy the properties of the state and send them as a payload.

{
var retVal = {
myval: state.myval,
myString: state.myString
}
return JSON.stringify(retVal);
}

Getting current iteration via index() method

When the template is running, it might be required to do some actions based on a specific iteration, e.g. for every 10th call, you would like to send a special message. Simply call index() function to find out which iteration you are into. The iteration starts from 0 and continues upto the number of iterations you have specified in simulate tab.

{
if (index() == 0 ) {
return "This is the first iteration"
}
if (index() % 10 == 0 ) {
return "Special Message at every 10th iteration"
}
if (index() % 2 == 0 ) {
return "Special Message at every even iteration"
}
var randomIteration = Math.floor(Math.random()*100);
if (index() == random ) {
return "This is a random message triggered one time in 100 iteration"
}
return "Normal message";
}

Know your client ID

If you are simulating more than 1 clients, you could find the current client ID in the template by accessing the function client(). This returns an integer starting from 0 and going upto the maximum number of clients specified. If you have passed a client offset while running simulation, that offset will be added to the client.

{
if (client() === 0 ) {
return "I am the leader with ID 0"
}
return "I am the follower with id "+ client();
}

A combination of client() and index() function could be very powerful to create some complex scenarios.

Logging

Anywhere in the function you could call console.log() which will be saved in the results.

{
console.log("This is a log", state.myvariable);
}

Sending Binary Payload

Instead of string, the message function could also return binary payload. This is really helpful when dealing with TCP/UDP payload types. E.g. lets create a raw payload and send it as a Buffer.

{
state.content = [0x62, 0x75, 0x66, 0x66, 0x65, 0x72];
state.content.push(0);
return Buffer.from(state.content);
}

This will result in the following hex content being sent to the server

62 75 66 66 65 72 00

Skipping message sending

It is important that at the end of processing, function body must return a value to be sent to the cloud platform. The return value could either be a string or a binary Buffer, depending upon what values are accepted by your cloud platform provider. If you would like to skip sending anything for that particular iteration, simply return ‘undefined’ as a string.

{
if (index() == 2){
// returning this will cause current message to be skipped.
return 'undefined'
}
else {
return 'normal payload'
}
}

When a payload 'undefined' is encountered by the back-end engine, the message sending part will be skipped for the iteration. However, this will be counted as a failure in the result collection system.

If the syntax of the function is getting complex, you could break up the functionality in several inline functions as well.