Vorto abstracts device data using Function Blocks, thus helping to reduce tight-coupling of devices in IoT solutions.
But how does Vorto help to map the actual device data to these abstract Function Blocks?
Payloads sent in different formats from the devices can be piped through the Vorto Payload Mapping Engine which will use the Mapping Specification and the provided Converters. The advantage of using this approach is that target platforms do not have to care about the different payloads of the devices but only need to handle the normalized Vorto compliant payloads.
This normalized format can then be used as a harmonized starting point for more specific, target platform compliant formats. In addition to that, custom Converters can be easily plugged into the Payload Mapping Engine in order to allow for more complex conversion of proprietary payloads.
In this tutorial, we are going to walk you through the process of creating a Vorto mapping specification for an Information Model and execute it with the Vorto Payload Mapping Engine.
To work through this tutorial, you will need:
- BoschID or GitHub account to log in to the Vorto Repository
- You have described a device as a Vorto Information Model and it is managed in the Vorto Repository
- You are a collaborator/owner of a namespace
The Vorto mapping-engine has been load-tested regarding performance and speed according to different mapping scenarios. A report and detailed description can be found here.
A mapping adds platform specific information to an Information Model. Since the representation of data can vary from platform to platform.
To create a mapping go to your newly created model and press the Create Mapping Spec.
Button
Now the web editor opens and allows you to add mapping expression for the Function Blocks you added. You can write XPath 2.0 like notation. Behind the scenes the engine uses JXPath to apply XPath expressions on a java object graph. To add functionality that may not be possible using jxpath, you can also add custom JavaScript or java functions (see the custom functions section).
Once you have written your xpath expressions, press Save.
On the right handside, select "JSON' as content type and paste the device raw data in the JSON editor. Test your mapping rule by clicking Test Mapping:
The test result will give you the mapped data for different IoT platforms.
The Vorto (canonical) format looks like:
The AWS IoT Shadow update command format looks like:
The Eclipse Ditto update command format looks like:
Download and save the Mapping Specification to start integrating it with the engine:
1. Add Maven dependency
<dependency>
<groupId>org.eclipse.vorto</groupId>
<artifactId>mapping-engine-all</artifactId>
<version>LATEST</version>
</dependency>
2. Initialize the mapping engine with the downloaded specification
MappingEngine engine = MappingEngine.createFromInputStream(FileUtils.openInputStream(new File("src/main/resources/mappingspec.json")));
3. Pass the arbitrary device payload to the engine to get it converted to Vorto compliant data
Object deviceData = ...;
InfomodelValue mappedData = engine.map(deviceData);
4. Optionally validate the mapped data to check if it complies to the Vorto model
ValidationReport validationReport = mappedData.validate();
if (!validationReport.isValid()) {
// handle invalid data
}
5. Convert mapped data to Digital Twin IoT compliant data
Convert the normalized Vorto compliant data to a target IoT platform format.
Currently supported target IoT Platform formats
- AWS IoT Shadow Service
- Eclipse Ditto Service
The following example shows how to convert the normalized data to AWS IoT Shadow Update Command:
import com.google.gson.JsonObject;
import org.eclipse.vorto.mapping.targetplatform.awsiot.TwinPayloadFactory;
...
JSONObject updateRequestAWSIoT = TwinPayloadFactory.toShadowUpdateRequest(mappedData);
sendToAWSIoTShadow(updateRequestAWSIoT,"com.acme:4711");
The following example shows how to convert the normalized data to Eclipse Ditto Update Command:
import com.google.gson.JsonObject;
import org.eclipse.vorto.mapping.targetplatform.ditto.TwinPayloadFactory;
...
JSONObject updateRequestDitto = TwinPayloadFactory.toDittoProtocol(mappedData,"com.acme:4711");
sendToEclipseDitto(updateRequestDitto);
The Vorto Mapping Engine has extension points in order to plug-in converter functions that can be used as part of your mapping rules.
Custom functions adds the power to write your own converter functions that can be used in your mapping rules. Each function belongs to a specific namespace.
You have two options to add custom converter functions:
- As a native Java Function
- As a Javascript Function
First, create a class containing static converter functions
public class MyStringConverterFunctions {
public static String concatStrings(String s1, String s2) {
return s1 + s2;
}
...
}
Then, register your functions in the mapping engine:
private static final IFunction FUNC_STRINGS = new ClassFunction("org_mycompany_strings", MyStringConverterFunctions.class);
IDataMapper.newBuilder().registerConverterFunction(FUNC_STRINGS);
The Vorto Mapping engine uses Nashorn as a Javascript engine to execute custom JS converter functions. These functions are stored and versioned in the Vorto Repository and are executed by the Mapping Engine.
For security reasons, the following restrictions apply when processing these converters:
- access to Java packages and classes is not possible
- using exit, quit, is not possible
- file access is not possible
- using loops are not allowed
- no JS libraries can be loaded
In the following example, a custom (Javascript) converter is defined in a Function Block mapping, that converts a click amount as a String to an Integer value:
namespace devices.aws.button.mapping
version 1.0.0
displayname "buttonPayloadMapping"
description "Payload Mapping for the button property of the AWS IoT Button"
category payloadmapping
using com.ipso.smartobjects.Push_button;0.0.1
functionblockmapping ButtonPayloadMapping {
targetplatform aws_ipso
// Definition of Converter functions which can be used from within the function block mapping
from Push_button to functions with
{convertClickType: "function convertClickType(clickType) { if (clickType === 'SINGLE') return 1; else if (clickType === 'DOUBLE') return 2; else return -1;}"}
// Usage of the converter function in the mapping rule expression
from Push_button.status.digital_input_count to source with {xpath: "button:convertClickType(/clickType)"}
}
If you want to specify a condition, when mapping rules for a Function Block should be applied, you can do this easily with mapping conditions.
Here is an example of using conditions to map to either temperature or illuminance Function Block based on the device payload header.
In this example, only the Temperature Function Block will be mapped, if the type field of header matches the 'T' value.
Function Block Temperature Mapping
...
from Temperature to condition with {value:"header.type == 'T'"}
//mapping rules for Temperature properties
Function Block Illuminance Mapping
...
from Illuminance to condition with {value:"header.type == 'I'"}
//mapping rules for Illuminance properties
- Vorto Mapping Engine in action. Tutorial that uses Vorto Mappings to convert Geolocation sensor data.
- Understand the Mapping Specification Syntax