Skip to content

Commit

Permalink
Include support for conditions in the templating mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
gtorodelvalle committed Oct 25, 2016
1 parent 4917562 commit c4865f3
Show file tree
Hide file tree
Showing 16 changed files with 1,356 additions and 245 deletions.
1 change: 1 addition & 0 deletions CHANGES_NEXT_RELEASE
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [FEATURE] Include support for conditions in the templating mechanism
153 changes: 152 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ To run the FIWARE Device Simulator CLI tool just run:
./bin/fiwareDeviceSimulatorCLI
```

This will show the FIWARE Device Simulator CLI tool help:
This will show the FIWARE Device Simulator CLI tool help which will guide you to learn how to properly use it:

```
Usage: fiwareDeviceSimulatorCLI [options]
Expand Down Expand Up @@ -390,8 +390,159 @@ Let's see this `imports()` directive mechanism with an example. The next one is

For example, the import directives: `import(contextBroker_NGSIv1)`, `import(every 5 seconds)` and `import(autoincrement_1)` will be substituted by the corresponding values declared in the `exports` property of the simulation configuration file, whereas the `import(authentication)` (since it is not declared in the `exports`) property will be `require`d as the file `authentication.json` from the root of the FIWARE Device Simulator application (this is, it is equivalent to `require(${FIWARE_Device_Simulator_Root_Path}/authentication.json))`.

The previous and preliminary support for importing content into specific parts of the simulation configuration files has been recently extended to support conditional imports. In this case, it is possible to impose conditions which must be satisfied for the import to take place. The format of the conditional imports is the following one:

```json
"<template-name>": [
{
"condition": "${{<entity-property-1>==<regular-expression-1>}}",
"content": "the-content-to-import-a-string-in-this-case"
},
{
"condition": "${{<entity-property-2>==<regular-expression-2>}{<attribute-property-2>==<regular-expression-2>}}",
"content": "the-content-to-import-a-string-in-this-case"
}
]
```

As you can see, the templates can now be an array of objects including a `condition` and a `content` properties in which case the import will only take place if the `import()` directive appears inside an entity which satisfies the `<entity-property-1>==<regular-expression-1>` condition (this is, the `<entity-property1->` value satisfies the `<regular-expression-1>`) OR appears inside an attribute which satisfies the `<attribute-property-2>==<regular-expression-2>` condition (this is, the `<attribute-property-2>` value satisfies the `<regular-expression-2>`) inside an entity which satisfies the `<entity-property-2>==<regular-expression-2>` condition (this is, the `<entity-property-2>` value satisfies the `<regular-expression-2>`).

Let's see it in a concrete example. Considering a simulation configuration file such as the following one:

```json
{
"exports": {
"every 5 seconds": "*/5 * * * * *",
"parking from 6 to 22": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})",
"now": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})",
"entity-type": [
{
"content": "ParkingSpot",
"condition": "${{entity_name==pe-moraleja-01-group-0[0-9]:0[0-9]}}"
}
],
"attribute-type-1": [
{
"content": "Text",
"condition": "${{entity_name==pe-moraleja-01-group-02:0[0-9]}}"
},
{
"content": "DateTime",
"condition": "${{entity_name==pe-moraleja-01-group-01:0[0-9]}{name==dateModifie[a-z]}}"
}
]
},
...
"entities": [
{
"schedule": "import(every 5 seconds)",
"entity_name": "pe-moraleja-01-group-01:01",
"entity_type": "import(entity-type)",
"active": [
{
"name": "status",
"type": "Text",
"value": "import(parking from 6 to 22)"
},
{
"name": "dateModified",
"type": "import(attribute-type-1)",
"value": "import(now)"
}
]
},
{
"schedule": "import(every 5 seconds)",
"entity_name": "pe-moraleja-01-group-02:01",
"entity_type": "import(entity-type)",
"active": [
{
"name": "status",
"type": "import(attribute-type-1)",
"value": "import(parking from 6 to 22)"
},
{
"name": "dateModified",
"type": "DateTime",
"value": "import(now)"
}
]
}
]
...
}
```

After resolving the imports, the simulation configuration file will end up as the following one:

```json
{
...
"entities": [
{
"schedule": "*/5 * * * * *", // -> IMPORTED
"entity_name": "pe-moraleja-01-group-01:01",
"entity_type": "ParkingSpot", // -> IMPORTED
"active": [
{
"name": "status",
"type": "Text",
"value": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})" // -> IMPORTED
},
{
"name": "dateModified",
"type": "DateTime", // -> IMPORTED
"value": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})" // -> IMPORTED
}
]
},
{
"schedule": "*/5 * * * * *", // -> IMPORTED
"entity_name": "pe-moraleja-01-group-02:01",
"entity_type": "ParkingSpot", // -> IMPORTED
"active": [
{
"name": "status",
"type": "Text", // -> IMPORTED
"value": "text-rotation-interpolator({\"units\": \"hours\", \"text\": [[0,\"closed\"],[6,[[40,\"free\"],[60,\"occupied\"]]],[19,[[80,\"free\"],[20,\"occupied\"]]],[22,\"closed\"]]})" // -> IMPORTED
},
{
"name": "dateModified",
"type": "DateTime",
"value": "date-increment-interpolator({\"origin\": \"now\", \"increment\": 0})" // -> IMPORTED
}
]
}
]
...
}
```

Just as in the case of the textual imports, the conditional imports can be declared in the `exports` property of the simulation configuration file or in external JSON files which can be imported.

Obviously, if an import directive refers to a template not declared either in the `exports` property or in an external JSON file, an error is thrown and the simulation is not run. On the other hand, if all the substitutions take place fine and the resulting simulation configuration file is valid, the simulation is run.

Although the `fiwareDeviceSimulatorCLI` command line tool previously detailed includes support for the import mechanism just described, we have also included a specific command line tool for the import mechanism which transpiles an input simulation configuration file into an output configuration file including the resolved imports.

To run the FIWARE Device Simulator Transpiler CLI tool just run:

```bash
./bin/fiwareDeviceSimulatorTranspilerCLI
```

This will show the FIWARE Device Simulator Transpiler CLI tool help which will guide you to learn how to properly use it:

```
Usage: fiwareDeviceSimulatorTranspilerCLI [options]
Options:
-h, --help output usage information
-V, --version output the version number
-c, --configuration <configuration-file-path> Absolute or relative path (from the root of the Node application) to the device simulator configuration input file (mandatory)
-o, --output <output-file-path> Absolute or relative path (from the root of the Node application) to the output device simulator configuration file (mandatory)
```

Following the description of the simulation configuration file accepted properties and leaning on the [FIWARE waste management harmonized data models](http://fiware-datamodels.readthedocs.io/en/latest/WasteManagement/doc/introduction/index.html), we provide a simulation configuration real example file to automatically generate waste management data, more concretely simulating the dynamic filling levels for 8 waste containers spread out at 4 areas (`Oeste` (i.e., West), `Norte` (i.e., North), `Este` (i.e., East) and `Sur` (i.e., South) of the Distrito Telefónica area (where the Telefónica headquarters are located) in Madrid.

```json
Expand Down
3 changes: 2 additions & 1 deletion bin/fiwareDeviceSimulatorCLI
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ var commander = require('commander');
var fs = require('fs');
var logops = require('logops');
var humanizeDuration = require('humanize-duration');
var path = require('path');
var deviceSimulator = require(ROOT_PATH + '/lib/fiwareDeviceSimulator');

process.on('SIGINT', function() {
Expand Down Expand Up @@ -65,7 +66,7 @@ function executeCommand() {
} else if (commander.configuration[0] === '.') {
configurationFilePath = ROOT_PATH + commander.configuration.substring(1);
} else {
commander.help();
configurationFilePath = ROOT_PATH + path.sep + commander.configuration;
}

if (!fs.existsSync(configurationFilePath)) {
Expand Down
100 changes: 100 additions & 0 deletions bin/fiwareDeviceSimulatorTranspilerCLI
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env node

/*
* Copyright 2016 Telefónica Investigación y Desarrollo, S.A.U
*
* This file is part of the Short Time Historic (STH) component
*
* STH is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* STH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public
* License along with STH.
* If not, see http://www.gnu.org/licenses/.
*
* For those usages not covered by the GNU Affero General Public License
* please contact with: [[email protected]]
*/

'use strict';

var ROOT_PATH = require('app-root-path');
var commander = require('commander');
var fs = require('fs');
var logops = require('logops');
var path = require('path');
var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler');

process.on('SIGINT', function() {
process.exit(0);
});

process.on('uncaughtException', function() {
process.exit(1);
});

/**
* Returns the absolute path for a file name or relative path
* @param {String} file File name or file path
* @return {String} The absolute path
*/
function getFilePath(file) {
return ROOT_PATH + (file.charAt(0) === '.' ? file.substring(1) : path.sep + file);
}

/**
* Executes the requested commander
*/
function executeCommand() {
if (!commander.configuration) {
commander.help();
}

var inputConfigurationFilePath = getFilePath(commander.configuration);
if (!fs.existsSync(inputConfigurationFilePath)) {
return logops.error('The input file path (\'' + inputConfigurationFilePath + '\') does not exist');
}

if (!commander.output) {
commander.help();
}

var outputConfigurationFilePath = getFilePath(commander.output);
if (fs.existsSync(outputConfigurationFilePath)) {
return logops.error('The output file path (\'' + outputConfigurationFilePath + '\') already exists');
}

fiwareDeviceSimulatorTranspiler.compose(require(inputConfigurationFilePath), function(err, newConfigurationObj) {
if (err) {
return logops.error('Error when transpiling the simulation configuration file (\'' +
inputConfigurationFilePath + '\'): ' + err);
}
fs.writeFile(outputConfigurationFilePath, JSON.stringify(newConfigurationObj, null, ' '), function(err) {
if (err) {
return logops.error('Error when writing to the output simulation configuration file \'' +
outputConfigurationFilePath + '\'): ' + err);
}
return logops.info('Output simulation configuration file \'' + outputConfigurationFilePath + '\') ' +
'successfully created');
});
});
}

commander.
version(require(ROOT_PATH + '/package.json').version).
option('-c, --configuration <configuration-file-path>',
'Absolute or relative path (from the root of the Node application) to the device simulator configuration ' +
'input file (mandatory)').
option('-o, --output <output-file-path>',
'Absolute or relative path (from the root of the Node application) to the output device simulator ' +
'configuration file (mandatory)').
parse(process.argv);

executeCommand();
86 changes: 0 additions & 86 deletions lib/composers/fiwareDeviceSimulatorComposer.js

This file was deleted.

4 changes: 2 additions & 2 deletions lib/fiwareDeviceSimulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ var multilinePositionInterpolator = require(ROOT_PATH + '/lib/interpolators/mul
var textRotationInterpolator = require(ROOT_PATH + '/lib/interpolators/textRotationInterpolator');
var attributeFunctionInterpolator = require(ROOT_PATH + '/lib/interpolators/attributeFunctionInterpolator');
var fdsErrors = require(ROOT_PATH + '/lib/errors/fdsErrors');
var fiwareDeviceSimulatorComposer = require(ROOT_PATH + '/lib/composers/fiwareDeviceSimulatorComposer');
var fiwareDeviceSimulatorTranspiler = require(ROOT_PATH + '/lib/transpilers/fiwareDeviceSimulatorTranspiler');
var fiwareDeviceSimulatorValidator = require(ROOT_PATH + '/lib/validators/fiwareDeviceSimulatorValidator');

/**
Expand Down Expand Up @@ -1200,7 +1200,7 @@ function start(config, theFromDate, theToDate, interval, margin, theDelay) {
realFromDate = null;
isEnded = false;
cancelAllJobs();
fiwareDeviceSimulatorComposer.compose(config, function(err, newConfig) {
fiwareDeviceSimulatorTranspiler.compose(config, function(err, newConfig) {
if (err) {
process.nextTick(function notifySimulationConfigurationError() {
emitError(err);
Expand Down
Loading

0 comments on commit c4865f3

Please sign in to comment.