Skip to content

Latest commit

 

History

History
410 lines (325 loc) · 11.8 KB

README.md

File metadata and controls

410 lines (325 loc) · 11.8 KB

API-REC

API:et är inspirerat/baserat på specifikationen för REST-API:et i RealEstateCore (även kallat REC).

POST /api/spaces

GET /api/spaces

POST /api/buildings

GET /api/buildings

POST /api/sensors

GET /api/sensors

POST /api/observations

GET /api/observations

root[id] - id för root-objekt

root[type] - typ för root-objekt

hasObservationTime[starting] - starttidpunkt för tidsspann med observationer

hasObservationTime[ending] - sluttidpunkt för tidsspann med observationer

page aktuell sida att hämta

size antal objekt (buildings, sensors, observations o.dyl.) i varje sida

Sensorvärden

api-rec tar emot meddelanden och lagrar data i ett tidsserie format.

Cloudevents

iot-events konfigureras att POST ett cloudevent till api-rec endpoint /api/cloudevents. Event som ska POST:as är message.accepted och function.updated.

subscribers:
  - id: api-rec-devices
    name: MessageAccepted
    type: message.accepted
    endpoint: http://api-rec:8080/api/cloudevents
    source: github.com/diwise/iot-agent
    eventType: message.accepted
    tenants:
      - default
    entities:
  - id: api-rec-functions
    name: FunctionUpdated
    type: function.updated
    endpoint: http://api-rec:8080/api/cloudevents
    source: github.com/diwise/iot-core
    eventType: function.updated
    tenants:
      - default
    entities:

Flödet blir då: sensor -> iot-agent -> iot-events -> cloudevents -> api-rec

api-rec kommer tolka händelserna och skapa observations från dem.

REST

-> api-rec

En observation kan också POST:as direkt till /api/observations.

{
  "format": "rec3.3",
  "deviceId": "https://recref.com/device/64b65a99-a53c-47f5-b959-1c7a641d82d8",
  "observations": [
    {
      "observationTime": "2019-05-27T20:07:44Z",
      "value": 16.1,
      "quantityKind": "https://w3id.org/rec/core/Temperature",
      "sensorId" : "https://recref.com/sensor/e0d5120b-90f1-48d6-a47f-f8ccd7727b04"
    }
  ]
}

QuantityKind

Units

Ett urval av typer som finns i spec för REC:

  • Concentration
  • Energy
  • Force
  • Illuminance
  • Power
  • Pressure
  • RelativeHumidity
  • Resistance
  • Temperature
  • Volume

Några extra har skapats

  • diwise:AirQuality
  • diwise:DigitalInput
  • diwise:Level
  • diwise:Lifebuoy
  • diwise:Presence
  • diwise:Timer

och fler eller andra kommer skapas vid behov.

https://github.com/RealEstateCore/rec/blob/main/API/Edge/edge_message.schema.json

Skillnden mellan deviceId och sensorId är att ett device kan ha en eller flera sensorer i samma "låda".

Skapa struktur

API för att stukturera fastigheter, byggnader, våningar, rum, m.m. Vi kan behöva fler/andra modeller från REC.

För närvarande finns endpoints för spaces, buildings och sensors.

POST /spaces

{
  "@context": "https://dev.realestatecore.io/contexts/Space.jsonld",
  "@id": "00f67d60-d4d4-4bd5-af32-cf6c9b9310ec",
  "@type": "dtmi:org:w3id:rec:Space;1"
}

POST /buildings

{
  "@context": "https://dev.realestatecore.io/contexts/Building.jsonld",
  "@id": "79b30db6-c5d3-4cd1-a438-6d8954b330ad",
  "@type": "dtmi:org:w3id:rec:Building;1",
  "isPartOf" : {
        "@id": "00f67d60-d4d4-4bd5-af32-cf6c9b9310ec",
        "@type": "dtmi:org:w3id:rec:Space;1"
  }
}

POST /sensors

{
  "@context": "https://dev.realestatecore.io/contexts/Sensor.jsonld",
  "@id": "76bb4d31-1167-49e0-8766-768eb47c47e2",
  "@type": "dtmi:org:brickschema:schema:Brick:Sensor;1",
  "isPartOf" : {
        "@id": "79b30db6-c5d3-4cd1-a438-6d8954b330ad",
        "@type": "dtmi:org:w3id:rec:Building;1"
  }
}

isPartOf skapar relation mellan entiteter. Alla modeller har fler properties för metadata som inte finns med i spiken.

Hämta data

Exempel med /sensors.

page=0 och size=10 är default om inget annat anges. Med dessa parametrar kan man hämta delar av dataset:et. hydra:totalItems kommer att ha totalt antal objekt i det fullständiga svaret.

root[type] och root[id] finns inte i spec, men faller in under Advanced queries och är tänkt svara på frågor som "ge mig alla sensorer i byggnad X".

hydra:view visas enbart om det finns en uppdelning av dataset:et.

Obs Används root[type] och root[id] så kommer inte page=0 och/eller size=10 att påverka något, utan då hämtas hela resultatet i samma fråga.

{
  "@context": "http://www.w3.org/ns/hydra/context.jsonld",
  "@id": "/api/sensors",
  "@type": "hydra:Collection",
  "hydra:totalItems": 32,
  "hydra:member": [
    {
      "@context": "https://dev.realestatecore.io/contexts/Sensor.jsonld",
      "@id": "76bb4d31-1167-49e0-8766-768eb47c47e2",
      "@type": "dtmi:org:brickschema:schema:Brick:Sensor;1",
    },
    ...
  ],
  "hydra:view": {
      "@id": "/api/sensors?page=3",
      "@type": "hydra:PartialCollectionView",
      "first": "/api/sensors?page=0&size=10",
      "previous": "/api/sensors?page=2&size=10",
      "last": "/api/sensors?page=3&size=10"
  }
}

Spaces, Buildings & Sensors

GET /sensors?root[type]=building&root[id]=79b30db6-c5d3-4cd1-a438-6d8954b330ad

Hämtar alla sensorer som finns i byggnaden med id 79b30db6-c5d3-4cd1-a438-6d8954b330ad. type måste anges då olika typer (spaces, buildings o.dyl.) kan ha samma ID.

{
  "@context": "http://www.w3.org/ns/hydra/context.jsonld",
  "@id": "/api/sensors",
  "@type": "hydra:Collection",
  "hydra:totalItems": 32,
  "hydra:member": [
    {
      "@context": "https://dev.realestatecore.io/contexts/Sensor.jsonld",
      "@id": "76bb4d31-1167-49e0-8766-768eb47c47e2",
      "@type": "dtmi:org:brickschema:schema:Brick:Sensor;1",
    },
    ...
  ]
}

Observations

Se Time interval queries för information.

För /observations finns ?hasObservationTime[starting] och hasObservationTime[ending] för att få ut data för ett visst tidsintervall. Datum måste vara formaterade enl. RFC3339

page=0 och size=10 funkar för observations på samma sätt som för t.ex. /sensors.

GET /observations?sensorId=76bb4d31-1167-49e0-8766-768eb47c47e2&hasObservationTime[starting]=2019-05-27T20:07:44Z&hasObservationTime[ending]=2019-06-27T20:07:44Z

{
    "@context": "http://www.w3.org/ns/hydra/context.jsonld",
    "@id": "/api/observations",
    "@type": "hydra:Collection",
    "hydra:totalItems": 78,
    "hydra:member": [
        {
            "observationTime": "2020-04-27T10:18:12Z",
            "value": 12380400000000,
            "quantityKind": "Energy",
            "sensorId": "vp1-em01"
        },
       ...
    ],
    "hydra:view": {
        "@id": "/observations?sensorId=76bb4d31-1167-49e0-8766-768eb47c47e2&hasObservationTime[starting]=2019-05-27T20:07:44Z&hasObservationTime[ending]=2019-06-27T20:07:44Z",
        "@type": "hydra:PartialCollectionView",
        "first": "/observations?sensorId=76bb4d31-1167-49e0-8766-768eb47c47e2&hasObservationTime[starting]=2019-05-27T20:07:44Z&hasObservationTime[ending]=2019-06-27T20:07:44Z&page=0&size=10",
        "previous": "/observations?sensorId=76bb4d31-1167-49e0-8766-768eb47c47e2&hasObservationTime[starting]=2019-05-27T20:07:44Z&hasObservationTime[ending]=2019-06-27T20:07:44Z&page=2&size=10",
        "last": "/observations?sensorId=76bb4d31-1167-49e0-8766-768eb47c47e2&hasObservationTime[starting]=2019-05-27T20:07:44Z&hasObservationTime[ending]=2019-06-27T20:07:44Z&page=3&size=10"
    }
}

Det finns logik som hindrar att samma värde lagras flera gånger inom en tidsperiod (nu 1 minut), dvs om sensor X skickar värdet 42 n gånger inom samma tidsperiod kommer enbart värdet lagras första gången, de andra gångerna kastas värdet. Om sensorn däremot skickar 42, 43, 42 inom samma tidsperiod kommer alla tre värden att lagras.

Databas

En graf skapas med två tabeller tills det behövs en riktig grafdatabashanterare.

DDL

CREATE TABLE IF NOT EXISTS entity (
  node_id        BIGSERIAL,
  entity_id      TEXT NOT NULL,
  entity_type    TEXT NOT NULL,
  entity_context TEXT NOT NULL,
  PRIMARY KEY (node_id)
);

CREATE UNIQUE INDEX IF NOT EXISTS entity_entity_type_entity_id_unique_indx ON entity (entity_type, entity_id);

CREATE TABLE IF NOT EXISTS  relation (
  parent        BIGINT NOT NULL,
  child         BIGINT NOT NULL,
  PRIMARY KEY (parent, child)
);

CREATE INDEX IF NOT EXISTS relation_child_parent_indx ON relation(child, parent);

CREATE TABLE IF NOT EXISTS observations (
  observation_id 		BIGSERIAL PRIMARY KEY,
  device_id			TEXT NOT NULL,
  sensor_id 			TEXT NOT NULL,
  observation_time	TIMESTAMPTZ NOT NULL,
  value 				NUMERIC NULL,
  value_string		TEXT NULL,
  value_boolean		BOOLEAN NULL,
  quantity_kind		TEXT NOT NULL
);

ALTER TABLE observations DROP CONSTRAINT IF EXISTS observations_device_id_sensor_id_observation_time_value_val_key;

CREATE UNIQUE INDEX IF NOT EXISTS observations_device_id_sensor_id_observation_time_quantity_kind_indx ON observations (device_id, sensor_id, observation_time, quantity_kind);

SQL

SQL för att svara på frågor som t.ex. "ge mig alla sensorer i byggnad X"

WITH RECURSIVE traverse(node_id, entity_type, entity_id) AS (
    SELECT
        node_id,
        entity_type,
        entity_id
    FROM
        entity
    WHERE
        entity.entity_id = $1 AND
        entity.entity_type = $2
    UNION ALL
    SELECT
        entity.node_id,
        entity.entity_type,
        entity.entity_id
    FROM traverse JOIN
    relation ON traverse.node_id = relation.parent JOIN
    entity ON relation.child = entity.node_id
)
SELECT
    traverse.entity_id
FROM traverse
WHERE traverse.entity_type = $3
GROUP BY traverse.entity_id
ORDER BY traverse.entity_id ASC

PK för en entitet är id + type. $1 och $2 pekar ut root-entiteten och $3 den typ som ska hittas bland kopplade entiteter.

Funderingar

Hämta data

Möjligen måste type vara dtmi:org:w3id:rec:Building;1 och inte bara building. Men det finns en CSV med översättningar för endpoints.

Databas

Bättre säkerhet för magiska strängar om de läggs in i en ENUM?

CREATE TYPE entity_type AS ENUM (
  'dtmi:org:w3id:rec:Space;1',
  'dtmi:org:w3id:rec:Building;1',
  'dtmi:org:brickschema:schema:Brick:Sensor;1',
  'dtmi:org:w3id:rec:ObservationEvent;1'
);

CREATE TYPE entity_context AS ENUM (
  'https://dev.realestatecore.io/contexts/Space.jsonld',
  'https://dev.realestatecore.io/contexts/Building.jsonld',
  'https://dev.realestatecore.io/contexts/Sensor.jsonld',
  'https://dev.realestatecore.io/contexts/ObservationEvent.jsonld'
);

CREATE TYPE quantity_kind AS ENUM (
  'diwise:AirQuality'
  'diwise:DigitalInput',
  'diwise:Presence',
  'Acceleration',
  'Angle',
  'AngularAcceleration',
  'AngularVelocity',
  'Area',
  'Capacitance',
  'Concentration',
  'Conductivity',
  'DataRate',
  'DataSize',
  'Density',
  'Distance',
  'Efficiency',
  'ElectricCharge',
  'ElectricCurrent',
  'Energy',
  'Force',
  'Frequency',
  'Illuminance',
  'Inductance',
  'Irradiance',
  'Length',
  'Luminance',
  'LuminousFlux',
  'LuminousIntensity',
  'MagneticFlux',
  'MagneticFluxDensity',
  'Mass',
  'MassFlowRate',
  'Power',
  'PowerFactor',
  'Pressure',
  'RelativeHumidity',
  'Resistance',
  'SoundPressureLevel',
  'Temperature',
  'Thrust',
  'Time',
  'Torque',
  'Velocity',
  'Voltage',
  'Volume',
  'VolumeFlowRate'
);