IoTIFY Helper Functions
There are several helper functions and libraries provided by IoTIFY which help you simulate your solutions faster.
We have written several helper functions which will help you model and simulate complex device behaviours easily.

Getting Current Simulation Job Information

When a test is running, it is called a simulation job. When you write a test, you don't have any information on how it will be run. You could use some test helper functions which will provide the currently running simulation job details to create more dynamic functionality.
Here are a few job information helper functions:

getRunSettings()

Returns an object with all the current run settings.

jobClients()

Returns number of clients being launched in the current simulation job.

jobId()

Returns the assigned name of the simulation job. This could be used to store the results in a database.

jobInterval()

Returns the time interval in seconds between each iteration.

jobRepeat()

Returns the total number of iterations specified in the job.
Let's look at an example:
1
{
2
if (client() == 0 && index() == 0){
3
console.log(
4
`Current Job name ${jobId()} \
5
with ${jobClients()} clients repeating \
6
${jobRepeat()} times with a gap of
7
${jobInterval()} seconds`)
8
}
9
}
Copied!
The above code snippet will print the following when the test will run. (The actual values will depend upon your specified parameters)
Current Job name test123-demo-users with 10 clients repeating 5 times with a gap of 5 seconds

index() or iteration()

While simulating, you will specify the number of iterations for the test. The current iteration can be determined within the template by using keyword index() or iteration(). Here is an example:
1
{
2
return "This is the "+index()+" iteration";
3
}
Copied!
The above template will send a message containing the current iteration index, starting from 0.

client()

While simulating, you may need to implement client-specific behaviours. The current client ID can be determined within the template by using the keyword client(). Here is an example:
1
{
2
return "This is the client "+client()+ " on "+index()+" iteration";
3
}
Copied!
The above template will send a message containing the current client with iteration index, starting from 0.

Setting Iteration result via assert()

Usually, an iteration for a client will be marked as failed, when the client is unable to publish a message to the server within the given timeout period. However, there may be cases where you would like to declare a test as failed when certain criteria are not met.
When using IOTIFY for testing, you could force a test iteration to be failed even if message sending is successful using test assertion with the assert() function.
assert(condition, message) function takes two parameters:
condition: A boolean condition that should be tested for truth (true) message: A string describing the cause of assertion failure which can be stored in test results.
For example, the following statement checks for an assertion failure in your template and marks the test result as failed, if retval does not equal to 123.
1
assert(retval == 123, "Return value doesn't equal "+123);
Copied!
The assertion will overwrite the usual results, i.e. Even if the message has been successfully sent to the server endpoint, the failure of assertion will mark the test result as failed.

glob APIs (glob.[get|set|delete])

IoTIFY provides a simple, fast and persistent key-value storage that is private to your workspace and accessible to all your running jobs. The key-value storage could be used for multiple purposes, For example, to sync between multiple jobs, control the simulation behaviour dynamically or revert back from the last state of the device. The API takes a string value as a key and a string/integer/object as a value. Here is an example:
1
{
2
glob.set('mykey', 55);
3
4
let val = glob.get('mykey'); // val will be 55
5
6
glob.delete('mykey');
7
8
glob.set('client_'+client(), 21124); // specfic to the client
9
}
Copied!
Any existing glob key values could be seen and modified in the UI under the Glob tab in Datastore. These glob values will also be accessible via APIs.

Metrics API (metric.add)

When the simulation is running, you may want to store some performance parameters for analysis. Normally, you would need to create a separate backend application that could store such metrics, however with a very large scale dataset, this could become difficult. IoTIFY provides a simple and easy to use time-series database for this purpose via our Metrics API.
1
{
2
metric.add('latency', 55);
3
metric.add('count'); // Default value of 1 is taken
4
}
Copied!
The API takes a string value as a key and an integer as a value. If no value is explicitly passed, a default value of 1 is given to the metric.
Metrics could be visualized and aggregated in the Metrics tab under Datastore. Metrics will also be available via APIs.

Mailbox API (mailbox.[post|pop|count|dump|delete])

The mailbox API allows us to have an internal message bus for our tests. The mailbox is a key value store which can store an array of values. It acts as a Last in, first out (LIFO) stack, so the last message that was posted to the mailbox will be popped the first.
The following example demonstrate the usage of mailbox:
1
{
2
mailbox.post("key", newData) //This will add the newData to the mentioned key
3
4
mailbox.pop("key") //This will pop the last data sent to the key
5
6
mailbox.count("key") //Gives a count of the data available in the key
7
8
mailbox.dump("key") //Dumps the whole content of key instead of
9
//popping one by one
10
11
mailbox.delete("key") //Deletes all the data for the mentioned key
12
13
}
Copied!

Simulate moving vehicles with drive()

To simulate a vehicle driving from one location to another is simply a matter of specifying the starting and end address. Use the following function in your template:
1
{
2
state['location'] = drive({start:'Munich,DE',end:'Berlin,DE',accuracy:5});
3
return JSON.stringify(state, null, 2);
4
}
Copied!
The above template will generate driving coordinates starting from Munich to Berlin in real-time traffic conditions. Upon each iteration, the function will automatically output the updated GPS coordinates.
You should never call this function within a loop.
Following are the input parameters:
  • start, end : A starting and ending address where you would like to drive. It could be a city name, a street address or even a point of interest, such as:
1
{
2
drive({start:'Disneyland, CA',end:'Universal Studios, CA'})
3
}
Copied!
  • accuracy: Since real-world GPS devices are not perfect, we will need to simulate the defects as well. Accuracy specifies how much maximum accuracy we should provide when simulating coordinates. In simple words, an accuracy of 5 meters specifies that the resulting coordinate could be anywhere within the 5-meter radius of the actual coordinate.
  • key: Provide your own Google maps direction key, if required.
The output format of the drive() function
The output of the drive() function is a JSON object, that’s why it is important to save this into a string with JSON.stringify(). Here is what the output looks like:
1
{
2
latitude: 34.123456, // decimal
3
longitude: 23.123455, // decimal
4
speed : 6, // meter per second
5
accuracy: 2 // meter radius
6
finished: // undefined, set to true for the last step
7
}
Copied!
Once the drive() function has been finished, the output will contain an additional field called finished set to true to indicate that the current drive has been finished.
Use the finished field to calculate the next set of drive parameters or return.

Get GPS location

Location function converts an address to its GPS coordinates, with a given accuracy parameter.
1
state['location'] = location({address:'Delhi,IN',accuracy:5})
Copied!
This will generate a GPS coordinate near New Delhi with an accuracy of 5 meters. Similar to the drive() function, you could also pass the actual address in the parameter and it will convert that to a set of GPS coordinates. The accuracy of this could be still controlled by the accuracy parameter. The output of the location() function is following:
1
{
2
latitude: 34.123456, // decimal
3
longitude: 23.123455 // decimal
4
}
Copied!

Get the real-world weather

weather.temperature() and weather.humidity() functions will query weather API to get the current weather of a city. For example:
1
{
2
var temperature = weather.temperature({location: 'London, UK', unit: 'f'});
3
var humidity = weather.humidity({location: 'London, UK');
4
}
Copied!
This will return the current temperature of London in degrees Fahrenheit and relative humidity in percentages.
If the weather can not be found or if the location is incorrect, an error message will be returned. By default, if no unit is specified, the output is in centigrade. To change the temperature to Fahrenheit, provide unit: 'f' as an argument.

Mocking CPU and Memory

cpu() and memory() functions will return mock CPU and memory usages of a running system in percentage points.
1
{
2
state['cpu'] = cpu();
3
state['memory'] = memory();
4
}
Copied!

Simulating continuous variables

A volatile variable is useful while modelling a complex value such as stock prices or temperature. The value of the volatile variable is dependent upon its last value and can move up or down to max +/- delta steps from the last value at random.
1
{
2
state['temperature'] = volatile({min : -10, max: 100, delta: 4, key:"myvar"});
3
}
Copied!
A volatile variable takes 3 parameters, min range, max range and delta value as an input.
The value of volatile will start from the middle of min and max, i.e. min+max/2 Upon each iteration, volatile will change by a minimum of 0 to a maximum of delta in either positive or negative direction from its last value.
Let's understand it with an example:
1
var chart = volatile({min : 10, max: 20, delta: 3, key:"myvar"})
Copied!
In the above example, chart will be initialized to a value of 15 at iteration 0. (10+20)/2
At iteration 1, chart can be anywhere +/-3 of 15, i.e. min 12 and max 18. Let’s say it reaches 14.
At iteration 2, chart can be anywhere +/-3 of 14, i.e. min 11 and max 17 and so on.
Volatile is useful for modelling dependent values such as stock prices and temperature graphs, where the next value depends upon the last value.
Always provide a unique key for each volatile variable within an object, so that they do not collide in value.

HTTP REST APIs (rest.[get|post|put|patch|delete])

Within your device template, you could call a REST API and get data from an external source, or push data to an external service. Following helper APIs are available in the tests.
1
rest.get({url:'https://httpbin.org/get'})
2
3
rest.post({url:'https://httpbin.org/post', json: { hello: 'world'}})
4
5
rest.put({url:'https://httpbin.org/put', json: { hello: 'world'}})
6
7
rest.patch({url:'https://httpbin.org/patch', json: { hello: 'world'}})
8
9
rest.delete({url:'https://httpbin.org/delete'})
Copied!
All API returns the response as a string. In the following example, we will get the current public IP address of our node and send this address to our backend application to whitelist our client.
1
{
2
//Getting the current IP address of the server executing the test
3
state['myip'] = rest.get({url: 'https://ifconfig.co/ip'});
4
5
//Posting the IP to my application to whitelist the source
6
let response = rest.post({url: 'https://httpbin.org/post', json: { ip: state.myip }});
7
8
//Return a string value which will be sent as the message payload
9
return JSON.stringify(state, null, 2);
10
}
Copied!
The rest APIs accept a lot of optional parameters. For the full documentation, please refer to the optional parameters for the NPM package request.

MQTT APIs (protocol.[publish|resubscribe|forceDisconnect|forceConnect])

Within the device template, you can use the MQTT APIs to interact with the MQTT connections.
1
{
2
protocol.publish(payload, topic); //Publish a payload to any topic
3
4
protocol.resubscribe(topics); //Resubscribe to a set of topics
5
6
protocol.forceDisconnect(); //Disconnect all MQTT connections
7
8
protocol.forceConnect(); //Reconnect MQTT connections
9
}
Copied!