6. APIs and examples of their usage

6.1. APIs of FogFlow Discovery

6.1.1. Look up nearby brokers

For any external application or IoT devices, the only interface they need from FogFlow Discovery is to find out a nearby Broker based on its own location. After that, they only need to interact with the assigned nearby Broker.

POST /ngsi9/discoverContextAvailability

Param Description
latitude latitude of your location
longitude latitude of your location
limit number of expected brokers

Please check the following examples.

Note

For the Javascript code example, you need the library ngsiclient.js. Please refer to the code repository at application/device/powerpanel

curl -iX POST \
  'http://localhost:8071/ngsi9/discoverContextAvailability' \
  -H 'Content-Type: application/json' \
  -d '
    {
       "entities":[
          {
             "type":"IoTBroker",
             "isPattern":true
          }
       ],
       "restriction":{
          "scopes":[
             {
                "type":"nearby",
                "value":{
                   "latitude":35.692221,
                   "longitude":139.709059,
                   "limit":1
                }
             }
          ]
       }
    } '
const NGSI = require('./ngsi/ngsiclient.js');

var discoveryURL = "http://localhost:8071/ngsi9";
var myLocation = {
        "latitude": 35.692221,
        "longitude": 139.709059
    };

// find out the nearby IoT Broker according to my location
var discovery = new NGSI.NGSI9Client(discoveryURL)
discovery.findNearbyIoTBroker(myLocation, 1).then( function(brokers) {
    console.log('-------nearbybroker----------');
    console.log(brokers);
    console.log('------------end-----------');
}).catch(function(error) {
    console.log(error);
});

6.2. APIs of FogFlow Broker

https://img.shields.io/swagger/valid/2.0/https/raw.githubusercontent.com/OAI/OpenAPI-Specification/master/examples/v2.0/json/petstore-expanded.json.svg

6.2.1. Create/update context

Note

It is the same API to create or update a context entity. For a context update, if there is no existing entity, a new entity will be created.

POST /ngsi10/updateContext

Param Description
latitude latitude of your location
longitude latitude of your location
limit number of expected brokers

Example:

curl -iX POST \
  'http://localhost:8070/ngsi10/updateContext' \
  -H 'Content-Type: application/json' \
  -d '
    {
        "contextElements": [
            {
                "entityId": {
                    "id": "Device.temp001",
                    "type": "Temperature",
                    "isPattern": false
                },
                "attributes": [
                {
                  "name": "temp",
                  "type": "integer",
                  "contextValue": 10
                }
                ],
                "domainMetadata": [
                {
                    "name": "location",
                    "type": "point",
                    "value": {
                        "latitude": 49.406393,
                        "longitude": 8.684208
                    }
                },{
                    "name": "city",
                    "type": "string",
                    "value": "Heidelberg"
                }
                ]
            }
        ],
        "updateAction": "UPDATE"
    }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"

var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var profile = {
        "type": "PowerPanel",
        "id": "01"};

var ctxObj = {};
ctxObj.entityId = {
    id: 'Device.' + profile.type + '.' + profile.id,
    type: profile.type,
    isPattern: false
};

ctxObj.attributes = {};

var degree = Math.floor((Math.random() * 100) + 1);
ctxObj.attributes.usage = {
    type: 'integer',
    value: degree
};
ctxObj.attributes.shop = {
    type: 'string',
    value: profile.id
};
ctxObj.attributes.iconURL = {
    type: 'string',
    value: profile.iconURL
};

ctxObj.metadata = {};

ctxObj.metadata.location = {
    type: 'point',
    value: profile.location
};

ngsi10client.updateContext(ctxObj).then( function(data) {
    console.log(data);
}).catch(function(error) {
    console.log('failed to update context');
});

6.2.2. Query Context via GET

6.2.2.1. Fetch a context entity by ID

GET /ngsi10/entity/#eid

Param Description
eid entity ID

Example:

curl http://localhost:8070/ngsi10/entity/Device.temp001

6.2.2.2. Fetch a specific attribute of a specific context entity

GET /ngsi10/entity/#eid/#attr

Param Description
eid entity ID
attr specify the attribute name to be fetched

Example:

curl http://localhost:8070/ngsi10/entity/Device.temp001/temp

6.2.2.3. Check all context entities on a single Broker

GET /ngsi10/entity

Example:

curl http://localhost:8070/ngsi10/entity

6.2.3. Query context via POST

POST /ngsi10/queryContext

Param Description
entityId specify the entity filter, which can define a specific entity ID, ID pattern, or type
restriction a list of scopes and each scope defines a filter based on domain metadata

6.2.3.1. query context by the pattern of entity ID

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{"entities":[{"id":"Device.*","isPattern":true}]}'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{id:'Device.*', isPattern: true}];

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.3.2. query context by entity type

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{"entities":[{"type":"Temperature","isPattern":true}]}'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{type:'Temperature', isPattern: true}];

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.3.3. query context by geo-scope (circle)

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "restriction": {
            "scopes": [{
                "scopeType": "circle",
                "scopeValue": {
                   "centerLatitude": 49.406393,
                   "centerLongitude": 8.684208,
                   "radius": 10.0
                }
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{type:'.*', isPattern: true}];
queryReq.restriction = {scopes: [{
                    "scopeType": "circle",
                    "scopeValue": {
                       "centerLatitude": 49.406393,
                       "centerLongitude": 8.684208,
                       "radius": 10.0
                    }
                }]};

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.3.4. query context by geo-scope (polygon)

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{
   "entities":[
      {
         "id":".*",
         "isPattern":true
      }
   ],
   "restriction":{
      "scopes":[
         {
            "scopeType":"polygon",
            "scopeValue":{
               "vertices":[
                  {
                     "latitude":34.4069096565206,
                     "longitude":135.84594726562503
                  },
                  {
                     "latitude":37.18657859524883,
                     "longitude":135.84594726562503
                  },
                  {
                     "latitude":37.18657859524883,
                     "longitude":141.51489257812503
                  },
                  {
                     "latitude":34.4069096565206,
                     "longitude":141.51489257812503
                  },
                  {
                     "latitude":34.4069096565206,
                     "longitude":135.84594726562503
                  }
               ]
            }
        }]
    }
}'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{type:'.*', isPattern: true}];
queryReq.restriction = {
       "scopes":[
          {
             "scopeType":"polygon",
             "scopeValue":{
                "vertices":[
                   {
                      "latitude":34.4069096565206,
                      "longitude":135.84594726562503
                   },
                   {
                      "latitude":37.18657859524883,
                      "longitude":135.84594726562503
                   },
                   {
                      "latitude":37.18657859524883,
                      "longitude":141.51489257812503
                   },
                   {
                      "latitude":34.4069096565206,
                      "longitude":141.51489257812503
                   },
                   {
                      "latitude":34.4069096565206,
                      "longitude":135.84594726562503
                   }
                ]
             }
          }
       ]
    }

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.3.5. query context with the filter of domain metadata values

Note

the conditional statement can be defined only with the domain matadata of your context entities For the time being, it is not supported to filter out entities based on specific attribute values.

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "restriction": {
            "scopes": [{
                "scopeType": "stringQuery",
                "scopeValue":"city=Heidelberg"
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{type:'.*', isPattern: true}];
queryReq.restriction = {scopes: [{
                    "scopeType": "stringQuery",
                    "scopeValue":"city=Heidelberg"
                }]};

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.3.6. query context with multiple filters

curl -X POST 'http://localhost:8070/ngsi10/queryContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "restriction": {
            "scopes": [{
                "scopeType": "circle",
                "scopeValue": {
                   "centerLatitude": 49.406393,
                   "centerLongitude": 8.684208,
                   "radius": 10.0
                }
            }, {
                "scopeType": "stringQuery",
                "scopeValue":"city=Heidelberg"
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var queryReq = {}
queryReq.entities = [{type:'.*', isPattern: true}];
queryReq.restriction = {scopes: [{
                    "scopeType": "circle",
                    "scopeValue": {
                       "centerLatitude": 49.406393,
                       "centerLongitude": 8.684208,
                       "radius": 10.0
                    }
                }, {
                    "scopeType": "stringQuery",
                    "scopeValue":"city=Heidelberg"
                }]};

ngsi10client.queryContext(queryReq).then( function(deviceList) {
    console.log(deviceList);
}).catch(function(error) {
    console.log(error);
    console.log('failed to query context');
});

6.2.4. Delete context

6.2.4.1. Delete a specific context entity by ID

DELETE /ngsi10/entity/#eid

Param Description
eid entity ID

Example:

curl -iX DELETE http://localhost:8070/ngsi10/entity/Device.temp001

6.2.5. Subscribe context

POST /ngsi10/subscribeContext

Param Description
entityId specify the entity filter, which can define a specific entity ID, ID pattern, or type
restriction a list of scopes and each scope defines a filter based on domain metadata
reference the destination to receive notifications

6.2.5.1. subscribe context by the pattern of entity ID

curl -X POST 'http://localhost:8070/ngsi10/subscribeContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities":[{"id":"Device.*","isPattern":true}],
        "reference": "http://localhost:8066"
    }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);
var mySubscriptionId;

var subscribeReq = {}
subscribeReq.entities = [{id:'Device.*', isPattern: true}];

ngsi10client.subscribeContext(subscribeReq).then( function(subscriptionId) {
    console.log("subscription id = " + subscriptionId);
        mySubscriptionId = subscriptionId;
}).catch(function(error) {
    console.log('failed to subscribe context');
});

6.2.5.2. subscribe context by entity type

curl -X POST 'http://localhost:8070/ngsi10/subscribeContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities":[{"type":"Temperature","isPattern":true}]
        "reference": "http://localhost:8066"
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var subscribeReq = {}
subscribeReq.entities = [{type:'Temperature', isPattern: true}];

ngsi10client.subscribeContext(subscribeReq).then( function(subscriptionId) {
    console.log("subscription id = " + subscriptionId);
        mySubscriptionId = subscriptionId;
}).catch(function(error) {
    console.log('failed to subscribe context');
});

6.2.5.3. subscribe context by geo-scope

curl -X POST 'http://localhost:8070/ngsi10/subscribeContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "reference": "http://localhost:8066",
        "restriction": {
            "scopes": [{
                "scopeType": "circle",
                "scopeValue": {
                   "centerLatitude": 49.406393,
                   "centerLongitude": 8.684208,
                   "radius": 10.0
                }
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var subscribeReq = {}
subscribeReq.entities = [{type:'.*', isPattern: true}];
subscribeReq.restriction = {scopes: [{
                    "scopeType": "circle",
                    "scopeValue": {
                       "centerLatitude": 49.406393,
                       "centerLongitude": 8.684208,
                       "radius": 10.0
                    }
                }]};

ngsi10client.subscribeContext(subscribeReq).then( function(subscriptionId) {
    console.log("subscription id = " + subscriptionId);
        mySubscriptionId = subscriptionId;
}).catch(function(error) {
    console.log('failed to subscribe context');
});

6.2.5.4. subscribe context with the filter of domain metadata values

Note

the conditional statement can be defined only with the domain matadata of your context entities For the time being, it is not supported to filter out entities based on specific attribute values.

curl -X POST 'http://localhost:8070/ngsi10/subscribeContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "reference": "http://localhost:8066",
        "restriction": {
            "scopes": [{
                "scopeType": "stringQuery",
                "scopeValue":"city=Heidelberg"
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var subscribeReq = {}
subscribeReq.entities = [{type:'.*', isPattern: true}];
subscribeReq.restriction = {scopes: [{
                    "scopeType": "stringQuery",
                    "scopeValue":"city=Heidelberg"
                }]};

ngsi10client.subscribeContext(subscribeReq).then( function(subscriptionId) {
    console.log("subscription id = " + subscriptionId);
        mySubscriptionId = subscriptionId;
}).catch(function(error) {
    console.log('failed to subscribe context');
});

6.2.5.5. subscribe context with multiple filters

curl -X POST 'http://localhost:8070/ngsi10/subscribeContext' \
  -H 'Content-Type: application/json' \
  -d '{
        "entities": [{
            "id": ".*",
            "isPattern": true
        }],
        "reference": "http://localhost:8066",
        "restriction": {
            "scopes": [{
                "scopeType": "circle",
                "scopeValue": {
                   "centerLatitude": 49.406393,
                   "centerLongitude": 8.684208,
                   "radius": 10.0
                }
            }, {
                "scopeType": "stringQuery",
                "scopeValue":"city=Heidelberg"
            }]
        }
      }'
const NGSI = require('./ngsi/ngsiclient.js');
var brokerURL = "http://localhost:8070/ngsi10"
var ngsi10client = new NGSI.NGSI10Client(brokerURL);

var subscribeReq = {}
subscribeReq.entities = [{type:'.*', isPattern: true}];
subscribeReq.restriction = {scopes: [{
                    "scopeType": "circle",
                    "scopeValue": {
                       "centerLatitude": 49.406393,
                       "centerLongitude": 8.684208,
                       "radius": 10.0
                    }
                }, {
                    "scopeType": "stringQuery",
                    "scopeValue":"city=Heidelberg"
                }]};

// use the IP and Port number your receiver is listening
subscribeReq.reference =  'http://' + agentIP + ':' + agentPort;


ngsi10client.subscribeContext(subscribeReq).then( function(subscriptionId) {
    console.log("subscription id = " + subscriptionId);
        mySubscriptionId = subscriptionId;
}).catch(function(error) {
    console.log('failed to subscribe context');
});

6.2.5.6. Cancel a subscription by subscription ID

DELETE /ngsi10/subscription/#sid

Param Description
sid the subscription ID created when the subscription is issued

curl -iX DELETE http://localhost:8070/ngsi10/subscription/#sid

6.3. APIs of FogFlow Service Orchestrator

The overall development process of an IoT Service in FogFlow is shown in the following figure. For the development of a fog function, the steps 4 and 5 are combined, which means a default requirement is issued by the FogFlow editor when a fog function is submmited.

_images/development_process.png

6.3.1. Implement an operator

Before you can define the designed service topology, all operators used in your service topology must be provided by you or the other provider in the FogFlow system.

Note

currently two templates are provided: one for nodejs based Implement and the other for python-based implementation

6.3.2. Publish the operator

You can publish the image of your operator to the public docker registry or your own private docker registery. If you do not want to use any docker registry, you have to make sure that the docker image of your operator is built on all edge nodes. Currently, when the FogFlow worker receives a command to launch a task instance, it will first search the required docker image from the local storage. If it does not find it, it will start to fetch the required docker image for the docker registry (the public one or your own private one, which is up to the configuration of your FogFlow worker).

If you like to publish your image, you can use the following docker command.

docker push  [the name of your image]

Note

this step is done with only docker commands

6.3.3. Define and register your operator

You can also register an operator docker image by sending a constructed NGSI update message to the IoT Broker deployed in the cloud.

Here is a Javascript-based code example to register an operator docker image. Within this code example, we use the Javascript-based library to interact with FogFlow IoT Broker. You can find out the library from the github code repository (designer/public/lib/ngsi). You must include ngsiclient.js into your web page.

var image = {
    name: "counter",
    tag: "latest",
    hwType: "X86",
    osType: "Linux",
    operatorName: "counter",
    prefetched: false
};

//register a new docker image
var newImageObject = {};

newImageObject.entityId = {
    id : image.name + ':' + image.tag,
    type: 'DockerImage',
    isPattern: false
};

newImageObject.attributes = {};
newImageObject.attributes.image = {type: 'string', value: image.name};
newImageObject.attributes.tag = {type: 'string', value: image.tag};
newImageObject.attributes.hwType = {type: 'string', value: image.hwType};
newImageObject.attributes.osType = {type: 'string', value: image.osType};
newImageObject.attributes.operator = {type: 'string', value: image.operatorName};
newImageObject.attributes.prefetched = {type: 'boolean', value: image.prefetched};

newImageObject.metadata = {};
newImageObject.metadata.operator = {
    type: 'string',
    value: image.operatorName
};

// assume the config.brokerURL is the IP of cloud IoT Broker
var client = new NGSI10Client(config.brokerURL);
client.updateContext(newImageObject).then( function(data) {
    console.log(data);
}).catch( function(error) {
    console.log('failed to register the new device object');
});

6.3.4. Define and register your service topology

Usually,you can define and register your service topology via the FogFlow topology editor. However, you can also define and register with your own code.

To register a service topology, your code needs to send a constructed NGSI update message to the IoT Broker deployed in the cloud.

Here is a Javascript-based code example to register an operator docker image. Within this code example, we use the Javascript-based library to interact with FogFlow IoT Broker. You can find out the library from the github code repository (designer/public/lib/ngsi). You must include ngsiclient.js into your web page.

// the json object that represent the structure of your service topology
// when using the FogFlow topology editor, this is generated by the editor
var topology = {
   "description":"detect anomaly events from time series data points",
   "name":"anomaly-detection",
   "priority": {
        "exclusive": false,
        "level": 100
   },
   "trigger": "on-demand",
   "tasks":[
      {
         "name":"AnomalyDetector",
         "operator":"anomaly",
         "groupBy":"shop",
         "input_streams":[
            {
                  "type": "PowerPanel",
                "shuffling": "unicast",
                  "scoped": true
            },
            {
                  "type": "Rule",
                "shuffling": "broadcast",
                  "scoped": false
            }
         ],
         "output_streams":[
            {
               "type":"Anomaly"
            }
         ]
      },
      {
         "name":"Counter",
         "operator":"counter",
         "groupBy":"*",
         "input_streams":[
            {
               "type":"Anomaly",
               "shuffling": "unicast",
               "scoped": true
            }
         ],
         "output_streams":[
            {
               "type":"Stat"
            }
         ]
      }
   ]
}

//submit it to FogFlow via NGSI Update
var topologyCtxObj = {};

topologyCtxObj.entityId = {
    id : 'Topology.' + topology.name,
    type: topology.name,
    isPattern: false
};

topologyCtxObj.attributes = {};
topologyCtxObj.attributes.status = {type: 'string', value: 'enabled'};
topologyCtxObj.attributes.template = {type: 'object', value: topology};

// assume the config.brokerURL is the IP of cloud IoT Broker
var client = new NGSI10Client(config.brokerURL);

// send NGSI10 update
client.updateContext(topologyCtxObj).then( function(data) {
    console.log(data);
}).catch( function(error) {
    console.log('failed to submit the topology');
});

6.3.5. Create a requirement entity to trigger the service topology

Here is the Javascript-based code example to trigger a service topology by sending a customized requirement entity to FogFlow.

var rid = 'Requirement.' + uuid();

var requirementCtxObj = {};
requirementCtxObj.entityId = {
    id : rid,
    type: 'Requirement',
    isPattern: false
};

var restriction = { scopes:[{scopeType: geoscope.type, scopeValue: geoscope.value}]};

requirementCtxObj.attributes = {};
requirementCtxObj.attributes.output = {type: 'string', value: 'Stat'};
requirementCtxObj.attributes.scheduler = {type: 'string', value: 'closest_first'};
requirementCtxObj.attributes.restriction = {type: 'object', value: restriction};

requirementCtxObj.metadata = {};
requirementCtxObj.metadata.topology = {type: 'string', value: curTopology.entityId.id};

console.log(requirementCtxObj);

// assume the config.brokerURL is the IP of cloud IoT Broker
var client = new NGSI10Client(config.brokerURL);
client.updateContext(requirementCtxObj).then( function(data) {
    console.log(data);
}).catch( function(error) {
    console.log('failed to send a requirement');
});

6.3.6. Remove a requirement entity to terminate the service topology

Here is the Javascript-based code example to terminate a service topology by deleting the requirement entity.

var rid = [the id of your created requirement entity];

//
var client = new NGSI10Client(config.brokerURL);
client.deleteContext(rid).then( function(data) {
    console.log(data);
}).catch( function(error) {
    console.log('failed to send a requirement');
});