Skip to content

Commit

Permalink
Add an example of interacting with the gateway
Browse files Browse the repository at this point in the history
Configure a Southbound provider and demonstrate that it can be queried or set

Signed-off-by: Tim Ward <timothyjward@apache.org>
  • Loading branch information
timothyjward committed Aug 30, 2023
1 parent df062c8 commit c15994b
Show file tree
Hide file tree
Showing 2 changed files with 237 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/examples/Configuring.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,4 @@ Once the configuration is updated and saved the gateway will automatically insta

curl http://localhost:8082/sensinact

The full set of endpoints available is listed in the [documentation for the northbound REST interface](../northbound/RestDataAccess.md#available-endpoints)
The full set of endpoints available is listed in the [documentation for the northbound REST interface](../northbound/RestDataAccess.md#available-endpoints). For a guide to using the REST interface you may also wish to look at the [interacting with sensiNact example](Interacting.md).
236 changes: 236 additions & 0 deletions docs/examples/Interacting.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
# Interacting with Eclipse sensiNact

In this example we will see how to interact with Eclipse sensiNact using the REST interface. This will help you to understand how to navigate the gateway, access the data from sensors, and update values stored in the digital twin.

To start with you will need a runnable gateway configured with REST access. If you don't have one of these then you may wish to consider starting with the earlier examples.

## Listing and Introspecting the gateway

Listing the providers present in the gateway is very simple. Assuming that you're using the configuration from [the previous example](Configuring.md) then issuing a GET request to `http://localhost:8082/sensinact/providers` will return something like:

```js
{
"type": "PROVIDERS_LIST",
"uri": "/",
"statusCode": 200,
"providers": ["sensiNact"]
}
```

This shows the result was successful, is of type `PROVIDERS_LIST` and the `providers` property contains a single provider `sensiNact`.

The `sensiNact` provider is a built in provider which we can further introspect at `http://localhost:8082/sensinact/providers/sensiNact`

```js
{
"type": "DESCRIBE_PROVIDER",
"uri": "/sensiNact",
"statusCode": 200,
"response": {
"name": "sensiNact",
"services": ["system","admin"]
}
}
```
This shows us details about the `sensiNact` provider in the form of a `DESCRIBE_PROVIDER` response. We could get similar information by listing the services for the `sensiNact` provider at `http://localhost:8082/sensinact/providers/sensiNact/services`

```js
{
"type": "SERVICES_LIST",
"uri": "/sensiNact",
"statusCode": 200,
"services": ["system","admin"]}
```

We can further query the resources available for a given service at `http://localhost:8082/sensinact/providers/sensiNact/services/system` or `http://localhost:8082/sensinact/providers/sensiNact/services/system/resources`

```js
{
"type": "DESCRIBE_SERVICE",
"uri": "/sensiNact/system",
"statusCode": 200,
"response": {
"name": "system",
"resources": [
{"name": "version", "rws": "RO", "type":"SENSOR"},
{"name": "started", "rws": "RO", "type": "SENSOR"}
]
}
}
```
```js
{
"type": "RESOURCES_LIST",
"uri": "/sensiNact/system",
"statusCode": 200,
"resources": ["started","version"]
}
```

### Querying resources

When you describe a resource such as `http://localhost:8082/sensinact/providers/sensiNact/services/system/resources` you get a list of all of the possible sensiNact verbs that can be used, and the parameters that they accept:

```js
{
"type": "DESCRIBE_RESOURCE",
"uri": "/sensiNact/system/started",
"statusCode": 200,
"response": {
"name": "started",
"type": "SENSOR",
"attributes": [],
"accessMethods":[
{
"name": "GET",
"parameters": [
{"name": "attributeName", "type": "string", "fixed": false, "constraints": []}
]
},
{
"name": "SUBSCRIBE",
"parameters": [
{"name": "topics", "type": "array", "fixed": false, "constraints": []},
{"name": "isDataListener", "type": "boolean", "fixed": false, "constraints":[]},
{"name": "isMetadataListener", "type": "boolean", "fixed": false, "constraints": []},
{"name": "isLifecycleListener", "type": "boolean", "fixed": false, "constraints": []},
{"name": "isActionListener", "type": "boolean", "fixed": false, "constraints": []}
]
},
{
"name": "UNSUBSCRIBE",
"parameters": [
{"name": "subscriptionId", "type":"string", "fixed":false, "constraints":[]}
]
},
{
"name": "SET",
"parameters": [
{"name": "value", "type": "java.time.Instant", "fixed": false, "constraints": []}
]
}
]
}
}
```

We can then "GET" the value of the resource from `http://localhost:8082/sensinact/providers/sensiNact/services/system/resources/started/GET`

```js
{
"type": "GET_RESPONSE",
"uri": "/sensiNact/system/started",
"statusCode": 200,
"response": {
"name": "started",
"timestamp": 1693387668421,
"type": "java.time.Instant",
"value": "2023-08-30T09:27:48.421487Z"
}
}
```


## Adding a southbound provider

In order to get more meaningful data from our sensiNact we can deploy a virtual sensor by updating the `configuration/configuration.json` to include the `virtual-temperature-sensor-feature`.

```js
```js
"sensinact.launcher": {
"features": [
"core-feature",
"jakarta-servlet-whiteboard-feature",
"jakarta-rest-whiteboard-feature",
"northbound-rest-feature",
"virtual-temperature-sensor-feature"
],
"repository": "repository",
"featureDir": "features"
},
"sensinact.virtual.temperature": {
"name": "temp1",
"interval": 5000,
"latitude": 1.0,
"longitude": 2.0
},
...
```

We can query the value of the `sensor/temperature` resource as before at `http://localhost:8082/sensinact/providers/temp1/services/sensor/resources/temperature/GET`

```js
{
"type": "GET_RESPONSE",
"uri": "/temp1/sensor/temperature",
"statusCode": 200,
"response": {
"name": "temperature",
"timestamp": 1693394285012,
"type": "double",
"value": 29.60935577560438
}
}
```

### Subscribing to updates

The virtual temperature sensor periodically updates its temperature reading. This could be read by repeatedly polling the value, however this is inefficient, and risks missing changes if the update is more frequent than the polling interval.

The SUBSCRIBE verb allows clients to register so that they can be notified when an update occurs, for the REST interface this is delivered as a Server Sent Event (SSE) stream, but other interfaces (such as Web Socket) may use other push mechanisms.

Making a GET request to `http://localhost:8082/sensinact/providers/temp1/services/sensor/resources/temperature/SUBSCRIBE` will result in a series of `data` events being delivered:

```js
event: data
data: {
"provider": "temp1",
"service": "sensor",
"resource": "temperature",
"timestamp": 1693402975313,
"oldValue": 18.758596432570382,
"newValue": 17.56708764568309
}
event: data
data: {
"provider": "temp1",
"service": "sensor",
"resource": "temperature",
"timestamp": 1693402980313,
"oldValue": 17.56708764568309,
"newValue": 8.2679466364907
}
...
```

In the REST interface unsubscription occurs automatically when the client closes the SSE connection. For other northbound providers the initial subscription response will include a subscription identifier. This identifier can be passed back in an unsubscription request to terminate the delivery of notifications.

### Updating values

If a resource is `MODIFIABLE` that means that it can have its value updated by an end user using the `SET` verb. Unlike the other requests so far a `SET` operation in the REST API is a `PUT`, not a `GET`. The body of the PUT request is an array of parameters with a `name`, `type` and `value`. We can test this using `curl`

```
curl -d "[{\"name\": \"value\", \"type\": \"string\", \"value\": \"Lassie\"}]" -H "Content-Type: application/json" \
http://localhost:8082/sensinact/providers/temp1/services/admin/resources/friendlyName/SET
```
A successful response will look like the following:
```js
{
"type": "SET_RESPONSE",
"uri": "/temp1/admin/friendlyName",
"statusCode": 200,
"response": {
"name": "friendlyName",
"timestamp": 1693407687936,
"type": "java.lang.String",
"value":"Lassie"
}
}
```

After a `SET` subsequent `GET` requests will return the previously set value.

0 comments on commit c15994b

Please sign in to comment.