As part of the FIWARE work, WireCloud has integrated support for accessing Orion Context Broker (reference implementation of the FIWARE's Pub/Sub Context Broker GE) instances from widgets/operators. In this tutorial we're going to learn how to use this support.
First of all, widgets and operators wishing to use the JavaScript bindings
provided by WireCloud for accessing the FIWARE NGSI Open RESTful API in order to
seamlessly interoperate with the Orion Context Broker must add the NGSI feature
as a requirement into their description files (config.xml
files).
The following is an example of a widget description using the XML flavour of the Mashable Application Component Description Language:
<?xml version='1.0' encoding='UTF-8'?>
<widget xmlns="http://wirecloud.conwet.fi.upm.es/ns/macdescription/1" vendor="CoNWeT" name="observation-reporter" version="1.0">
<details>
<title>Observation Reporter</title>
<authors>aarranz</authors>
<email>aarranz@conwet.com</email>
<image>images/catalogue.png</image>
<smartphoneimage>images/smartphone.png</smartphoneimage>
<description>Creates a new observation</description>
<doc>http://www.envirofi.eu/</doc>
</details>
<requirements>
<feature name="NGSI"/>
</requirements>
<wiring/>
<contents src="index.html" contenttype="text/html" charset="utf-8" useplatformstyle="true"/>
<rendering height="20" width="5"/>
</widget>
The RDF/xml flavour of the same widget description is:
<?xml version="1.0" encoding="utf-8"?>
<rdf:RDF
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:wire="http://wirecloud.conwet.fi.upm.es/ns/widget#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:usdl="http://www.linked-usdl.org/ns/usdl-core#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:ns1="http://purl.org/goodrelations/v1#"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:vcard="http://www.w3.org/2006/vcard/ns#"
>
<wire:Widget rdf:about="http://wirecloud.conwet.fi.upm.es/ns/widget#CoNWeT/observation-reporter/1.0">
<vcard:addr>
<vcard:Work rdf:nodeID="Nb17ce611aa2645e488515f86eb855e53">
<vcard:email>aarranz@conwet.com</vcard:email>
</vcard:Work>
</vcard:addr>
<usdl:utilizedResource>
<usdl:Resource rdf:about="index.html">
<wire:codeCacheable>True</wire:codeCacheable>
</usdl:Resource>
</usdl:utilizedResource>
<wire:hasPlatformWiring>
<wire:PlatformWiring rdf:nodeID="Neecb97db81ed40859b8c04e935a9a9cc"/>
</wire:hasPlatformWiring>
<wire:displayName>Observation Reporter</wire:displayName>
<wire:hasiPhoneImageUri rdf:resource="images/smartphone.png"/>
<usdl:versionInfo>1.0</usdl:versionInfo>
<usdl:hasProvider>
<ns1:BusinessEntity rdf:nodeID="N9a6bf56577c741ac806997a80281afff">
<foaf:name>CoNWeT</foaf:name>
</ns1:BusinessEntity>
</usdl:hasProvider>
<wire:hasImageUri rdf:resource="images/catalogue.png"/>
<wire:hasPlatformRendering>
<wire:PlatformRendering rdf:nodeID="N713e5ea11dce4750a592c754c748def7">
<wire:renderingHeight>20</wire:renderingHeight>
<wire:renderingWidth>5</wire:renderingWidth>
</wire:PlatformRendering>
</wire:hasPlatformRendering>
<wire:hasRequirement>
<wire:Feature rdf:nodeID="N3cb336bd9b6243ecbf345c80442498f9">
<rdfs:label>NGSI</rdfs:label>
</wire:Feature>
</wire:hasRequirement>
<dcterms:title>observation-reporter</dcterms:title>
<dcterms:description>Creates a new observation</dcterms:description>
<dcterms:creator>
<foaf:Person rdf:nodeID="Ndb72cb5a7f3844b29b72f304baaa14a7">
<foaf:name>aarranz</foaf:name>
</foaf:Person>
</dcterms:creator>
</wire:Widget>
</rdf:RDF>
Before being able to make use of the NGSI API, you should create a Connection with the NGSI Context Broker you're going to use. This can be accomplished with the following code:
var ngsi_connection = new NGSI.Connection(ngsi_server[, options]);
See the NGSI javascript API documentation for the full list of supported options
but, as summary, the ngsi_proxy_url
option is required for being able to create
subscriptions handled by widgets/operators. Also, if you are connecting to a
Orion Context Broker using the IdM authentication, you will need to pass the
required authentication credentials. This can be accomplished in two ways:
request_headers
option and passing directly the required
Authentication headeruse_user_fiware_token
option to make the the NGSI API user
the FIWARE's OAuth2 token of the current user (obtained by WireCloud from the
IdM). Any request made by a connection using this option will fail if the
current users doesn't have a valid token (take into account that anonymous
users and users authenticated using other authentication backends fall into
this category). If you're worried about security, take into account that the
OAuth2 token is injected in the request by the WireCloud's proxyThis is an example of NGSI connection creation using the resources available at FIWARE Lab:
var ngsi_connection = new NGSI.Connection('http://orion.lab.fiware.org:1026', {
use_user_fiware_token: true,
ngsi_proxy_url: 'https://ngsiproxy.lab.fiware.org'
});
Once created the connection, you will be able to use the NGSI API bindings (in
the example, through the ngsi_connection
variable).
Queries are the most basic operations that can be executed in a Orion Context Broker. This operation can be accessed thought the query method of the connection object:
var entityIdList = [
{type: 'Van', id: '.*', isPattern: true}
];
var attributeList = ['current_position'];
var options = {
flat: true,
onSuccess: function (data) {
// data contains the obtained info
// from the context broker
}
};
ngsi_connection.query(entityIdList, attributeList, options);
The first parameter is the list of entities you're interested on. In our case,
we're interested in all Van
entities. This is accomplished using the isPattern
option that allows us to use a regular expression that matches with any id
.
The second one is the list of attributes you're interested on. In our case,
we're interested only in the current_position
attribute. However, you can
pass null or an empty list to indicate that you are interested in all the
attributes of the selected entities.
Finally, all the methods supports a last parameter called options that should be used to pass the callbacks and the extra options. Any method of NGSI.Connection support at least the following callbacks:
onSuccess
is called when the request finishes successfully. The parameters passed to this callback depends on the invoked method. In the case of the query operation, the first parameter will contain the data returned after querying the context brokeronFailure
is called when the request finish with errorsonComplete
is called when the request finish regardless of whether the request is successful or notThe query
method also supports other extra options. The flat
options is used for simplifying the data structure used for representing the returned data. This simplification relies in making the following assumptions about the returned entry set:
id
there is only one value for the entity's type parameterid
or type
name
For example, this is the value of the data parameter passed to the onSuccess
callback when using the flat
option:
{
"van1": {
"id": "van1",
"type": "Van",
"current_position": "43.47557, -3.8048315"
},
"van2": {
"id": "van2",
"type": "Van",
"current_position": "43.47258, -3.8026643"
},
"van3": {
"id": "van3",
"type": "Van",
"current_position": "43.47866, -3.7991238"
},
"van4": {
"id": "van4",
"type": "Van",
"current_position": "43.471214, -3.7994885"
}
}
Whereas this is the value of the data parameter when flat
is false
(default value):
[
{
"entity": {
"id": "van1",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.47557, -3.8048315",
"metadata": [{name: 'location', type: 'string', value: 'WGS84'}]
}
]
},
{
"entity": {
"id": "van2",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.47258, -3.8026643",
"metadata": [{name: 'location', type: 'string', value: 'WGS84'}]
}
]
},
{
"entity": {
"id": "van3",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.47866, -3.7991238",
"metadata": [{name: 'location', type: 'string', value: 'WGS84'}]
}
]
},
{
"entity": {
"id": "van4",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.471214, -3.7994885",
"metadata": [{name: 'location', type: 'string', value: 'WGS84'}]
}
]
}
]
In addition to the flat
option. The query
method also support other options
related to the pagination support found on Orion 0.14.0 and newer. These options
are the limit
, offset
and details
options. For example, the following code can
be used for retrieving a first page containing (at most) 100 entities:
var options = {
flat: true,
limit: 100,
onSuccess: function (data) {
....
}
};
ngsi_connection.query(entityIdList, attributeList, options);
While this code will request the second page:
var options = {
flat: true,
limit: 100,
offset: 100,
onSuccess: function (data) {
....
}
};
ngsi_connection.query(entityIdList, attributeList, options);
You can get the total number of matches using the details
option:
var options = {
flat: true,
limit: 100,
offset: 200,
details: true,
onSuccess: function (data, details) {
// The total number of matches is stored in details.count
....
}
};
ngsi_connection.query(entityIdList, attributeList, options);
One of the most important operations provided by the context broker is the support for creating subscriptions, in this way our system can obtain "real time" notifications about the status of the entities of our system. Subscriptions are very similar to queries, the main difference between queries and subscriptions is that queries are synchronous operations. Moreover, the Orion Context Broker will send a first notification containing the data that would be returned for the equivalent query operation. This way, you will know that there is no gap between the current values and their notified changes.
Both widgets and operators can create subscriptions through the
createSubscription
method.
The following example explains how to be notified about the changes of the position of the vans:
var entityIdList = [
{type: 'Van', id: '.*', isPattern: true}
];
var attributeList = null;
var duration = 'PT3H';
var throttling = null;
var notifyConditions = [{
type: 'ONCHANGE',
condValues: ['current_position']
}];
var options = {
flat: true,
onNotify: function (data) {
// called when the context broker sends a new notification
},
onSuccess: function (data) {
ngsi_subscriptionId = data.subscriptionId;
}
};
ngsi_connection.createSubscription(entityIdList, attributeList, duration, throttling, notifyConditions, options);
In the previous example, this call to createSubscription
will make the context broker
call the onNotify
function each time the current_position
attribute of the
entities of type Van
is changed. You must take into account that the Orion
Context Broker evaluates patterns at runtime, so using patterns you will be able
to receive notification about new entities provided that the notify conditions
are meet.
This subscription will expire after 3 hours, time from which the context broker will stop sending notifications. Anyway, widgets/operators can renew those subscriptions by using the updateSubscription method, even if they have expired. Subscriptions can be cancelled using the cancelSubscription method making the context broker release any info about the subscription. In any case, WireCloud will cancel any subscription automatically when widgets/operators are unloaded.
As with the query operations, you can make use of the flat
option when creating
subscriptions. The assumptions made by the createSubscription
method will be the
same that the ones used by the query
method, the only thing that changes is that
this affects to the parameter passed to the notification callback instead to the
success callback.
As example, this is the value of the data parameter passed to
the onNotify
callback when using the flat
option:
{
"elements": {
"van2": {
"id": "van2",
"type": "Van",
"current_position": "43.47258, -3.8026643"
},
"van4": {
"id": "van4",
"type": "Van",
"current_position": "43.471214, -3.7994885"
}
},
"subscriptionId": "53708768286043030c116e2c",
"originator": "localhost"
}
Whereas this is the value of the data parameter when flat
is false
(default value):
{
"elements": [
{
"entity": {
"id": "van2",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.47258, -3.8026643"
}
]
},
{
"entity": {
"id": "van4",
"type": "Van"
},
"attributes": [
{
"name": "current_position",
"type": "coordinates",
"contextValue": "43.471214, -3.7994885"
}
]
}
},
"subscriptionId": "53708768286043030c116e2c",
"originator": "localhost"
}
Widgets and operator can update entities using the
updateAttributes
and addAttributes
methods. The updateAttributes
and addAttributes
methods use the
same format for their parameters, the main difference is that addAttribute
method will create new attributes/entities if needed whereas updateAttributes
will fail if the referred entities/attributes didn't exist.
For example, the following code updates the attribute position
of the van1
entity if it
exists, or creates the attribute or the entity if one of them don't exist:
ngsi_connection.addAttributes([{
entity: {id: 'van1', type: 'Van'},
attributes: [
{
type: 'string',
name: 'current_position',
contextValue: coordinates
}
]
}], {
onSuccess: function (accepted_changes, unaccepted_changes) {
// The Orion Context Broker processed the request successfully
if (unaccepted_changes.length === 0) {
// Van created/updated successfully
...
} else {
// Something went wrong
}
}.bind(this),
onFailure: function (error) {
// General failure when creating/updating the van
},
onComplete: function () {
//
}.bind(this)
}
);
The response_data
parameter of the onSuccess
callback is a summary of the
accepted changes as returned by the Orion Context Broker. This info can normally
be ignored, as it will be very similar to the one provided to the
updateAttributes
/addAttribute
methods when the request ends successfully. If
everything goes ok, the unaccepted_changes
parameter will contain an empty
array. If something goes wrong, the unaccepted_changes
parameter will contain
all the info about what changes were rejected. It's very important to take this
into account as the onFailure
callback won't be called for reporting unaccepted
changes as they are treated individually by the Orion Context Broker.
For example, if we execute the following code:
ngsi_connection.updateAttributes([
{
'entity': {type: 'City', id: 'Madrid'},
'attributes': [
{name: 'position', type: 'coords', contextValue: '40.418889, -3.691944'}
]
},
{
'entity': {type: 'Point', id: 'A'},
'attributes': [
{name: 'mobile_phone', type: 'string', contextValue: '0034223456789'}
]
}
],
...
);
Giving the fact both Madrid and A entities exit but only the position attribute
exists, the value of the accepted_changes
will be:
[
{
entity: {id: 'Madrid', type: 'City'},
attributes: [
{name: 'position', type: 'coords'}
]
}
]
Whereas the value of the unaccepted_changes
parameters will be something similar to this:
[
{
entity: {id: 'A', type: 'Point'},
attributes: [
{name: 'mobile_phone', type: 'string'}
],
statusCode: {
code: 472,
reasonPhrase: 'request parameter is invalid/not allowed',
details : 'action: UPDATE - entity: (A, Point) - offending attribute: mobile_phone'
}
}
]
The following code snippet shows an example of how to create a position attribute indicating that it will be used for defining the location of the entity:
ngsi_connection.addAttributes([
{
entity: {type: 'City', id: 'Madrid'},
attributes: [
{
name: 'position',
type: 'coords',
contextValue: '40.418889, -3.691944',
metadata: [
{name: 'location', type: 'string', value: 'WGS84'}
]
}
]
}
],
...
);
After which you can issue geo-located queries. For example:
connection.query([
{type: 'City', id: '.*', isPattern: true}
],
null,
{
restriction: {
scopes: [
{
type: "FIWARE_Location",
value: {
circle: {
centerLatitude: "40.418889",
centerLongitude: "-3.691944",
radius: "14000"
}
}
}
]
},
...
});
Or:
connection.query([
{type: 'Point', id: '.*', isPattern: true}
],
null,
{
restriction: {
scopes: [
{
type : "FIWARE_Location",
value : {
polygon: {
vertices: [
{ latitude: "0", longitude: "0" },
{ latitude: "0", longitude: "6" },
{ latitude: "6", longitude: "0" }
],
inverted: true
}
}
}
]
},
...
});
To check how NGSI works, an example using this feature is provided. If you want to see it running you have to upload the following mashup into WireCloud:
This mashup uses the following components:
Once uploaded, you will be able to create new workspaces using this mashup as template. Take into account that this example is meant to work on FIWARE Lab.
Another source of examples is the OrionStarterKit offering available at the FIWARE Lab's Store containing all the generic widgets and operators related to the Orion Context Broker and several example mashups.
If everything is ok, you will see a map as the following one, with the location of some lampposts in the city of Santander.
Table of Contents | t |
---|---|
Exposé | ESC |
Full screen slides | e |
Presenter View | p |
Source Files | s |
Slide Numbers | n |
Toggle screen blanking | b |
Show/hide slide context | c |
Notes | 2 |
Help | h |