Capteurs

Les capteurs sont objets qui ont la capacité de relevés des mesures physiques. Ils transmettent les valeurs régulièrement ou sur changement notable. Ils peuvent être indépendants ou embarqués. Cette spécification traite principalement du premier cas.

Spécification en cours de rédaction

Principe

Pour récupérer les informations des capteurs, il existe plusieurs méthodes :

  • Le capteur pousse directement les données en web-service dans Ekylibre via l'API normalisée.
  • Un utilisateur enregistre un fichier de données issu de capteurs
  • Ekylibre interroge le capteur directement pour récupérer les valeurs à interval régulier ou en mode manuel
  • Ekylibre interroge le serveur de centralisation des données du capteur pour récupérer les valeurs à interval régulier ou en mode manuel

Cela étant dit, les objectifs sont :

  • Permettre de développer rapidement des contrôleurs de capteurs pour récupérer les valeurs.
  • Récupérer les valeurs centralisées de capteurs, ou en accès direct via un service web REST

Pour cela, il faudra définir chaque capteur utilisé sur l'exploitation et son mode de récupération de données. En fonction des capteurs, une méthode de récupération des données (retrieve) devra être définie. Les contrôleurs de capteurs permettront à terme de configurer ceux qui sont configurables.

Dans l'immédiat, la méthode sera appelée toutes les heures.

Implémentation

Modèle de données

Le modèle doit permettre de recenser les capteurs et stocker les valeurs qu'ils mesurent. De plus, ce valeurs doivent être mise en lien avec le sujet de la mesure (équipement hôte, parcelle…).

Sensors

Une table sensors permet de stocker les capteurs :

  • vendor_euid~: EUID du fabricant. Ex.~: previmeteo, weenat
  • model_euid~: (Ex.~: weather_plus3502, hygromax200…)
  • name~: (Ex.~: capteur température n°3…)
  • active~: Si le capteur est actif ou non
  • retrieval_mode~: Mode de récupération des valeurs manual, automatic
  • access_parameters~: Paramètres demandé par le contrôleur (stocké en JSON). Exemple~: id capteur externe, id client externe, clef API
  • productProduct~: Produit du capteur (si approvisionné)
  • embedded~: Si le capteur est embarqué
  • hostProduct~: Hôte du capteur embarqué
  • stamps

EUID signifie External Unique ID.

Analyses

La table analyses permet de stocker les relevés et en cas de problème, l'erreur remontée :

  • productProduct
  • hostProduct
  • sensorSensor
  • mode : mode temporel instantané ou sur un période (instant, period)
  • stopped_at (pour period)
  • return_status (ok, error)
  • return_explanation (text)
  • geolocation
  • nature
  • number
  • (reference_number)
  • sampled_at
  • analysed_at
  • stamps

AnalysisItem

La table analysis_items permet de stocker les valeurs du relevé :

  • analysis
  • indicator_name (température, pluviométrie cumulée…)
  • *value

Une base de fabricants et de matériels sera nécessaire.

ActiveSensor

Le module ActiveSensor doit permettre de définir rapidement la connexion à un capteur. Une méthode par mode de récupération de données (une par fournisseur pour l'instant)

lib/previmeteo/generic_controller.rb
class Previmeteo::GenericController < ActiveSensor::Controller
  has_parameter :id
  has_parameter :api
  has_parameter :id_station
  has_parameter :url, default: "http://my.previmeteo.com/api"
 
  # Méthode appelée par ActiveSensor pour récupérer les valeurs
  # La méthode dispose d'un jeu de méthode permettant de récupérer
  # le fabricant (''vendor''), le modèle (''model'') et les 
  # informations du capteur en général.
  # La méthode doit retourner un hash avec les différentes valeurs mesurées,
  # la géolocalisation et les informations temporelles.
  def retrieve(options = {})
    # Requête JSON et retraitement des valeurs
  end
 
end
lib/previmeteo.rb
...
ActiveSensor.register(
  :previmeteo, 
  :weather_2000, 
  label: 'Weather 2000/2001', # I18n à voir
  description: 'Weather station ....', # I18n
  indicators: [:temperature, :cumulated_rainfall, :wind_speed, :hygrometry],
  image: '/path/to/beautiful/image.png', # 500px × 500px
  controller: 'Previmeteo::GenericController'
)
 
ActiveSensor.register(
  :previmeteo, 
  :weather_4000, 
  label: {
    fra: 'Weather 4000/4001',
    jpn: '気象センサ4000/4001'
  },
  description: 'Weather station 4000 series ....', # I18n
  indicators: [:temperature, :cumulated_rainfall, :wind_speed, :hygrometry, :sunshine, :atmospheric_pressure],
  image: '/path/to/beautiful/image.png', # 500px × 500px
  controller: 'Previmeteo::GenericController'
)
 
# Une approche YAML peut être envisagée, dans ce cas-là
# le chemin des images peut-être envisagé en relatif s'il
# ne commence pas par un '/'
ActiveSensor.register_many('config/sensors.yml')
...

Le fichier de conf YAML est une idée. À voir pour la pertinence.

config/sensors.yml
previmeteo:
  weather_2000:
    label: "Weather 2000/2001"
    description: "Weather station ..."
    indicators: 
      - temperature
      - cumulated_rainfall
      - wind_speed
      - hygrometry
    image: "/path/to/beautiful/image.png"
    controller: "Previmeteo::GenericController"
    
  weather_4000:
    label:
      fra: "Weather 4000/4001"
      jpn: "気象センサ4000/4001"
    description: "Weather station 4000 series..."
    indicators: 
      - temperature
      - cumulated_rainfall
      - wind_speed
      - hygrometry
      - sunshine
      - atmospheric_pressure
    image: "/path/to/beautiful/image.png"
    controller: "Previmeteo::GenericController"
    ...     

Il y a une méthode par mode de récupération de données (soit une par fournisseur pour l'instant).

Côté lib

Le module ActiveSensor permet de gérer la base de données de capteurs via plusieurs méthodes~:

  • vendors : pour lister les fabricants
  • equipments_of : pour retourner la liste des équipements d'un fabricant
  • register(vendor, model, options={}) : pour enregistrer un matériel
  • erase(vendor, model) : pour le supprimer
  • find(vendor, model) : pour le trouver (retourne une instance ActiveSensor::Equipment)
  • connect(vendor, model, access_parameter={}) : pour récupérer une instance ActiveSensor::Connection

La classe ActiveSensor::Equipment permet de stocker la description d'un équipement et d'accéder à ces informations (avec support i18n). Elle sert de proxy pour récupérer les paramètres de la méthode.

La classe ActiveSensor::Connection permet de gérer la connexion au capteur. Un new initialise la connexion, un close la ferme. Aujourd'hui le close n'est pas nécessaire mais à implémenter pour les futurs capteurs qui le nécessiteront. Cette classe implémente la méthode retrieve qui permet d'appeler le contrôleur.

Résultat d'un relevé de capteur sous représentation YAML :

---
status: ok
sampled_at: 2015-.....
sampling_temporal_mode: instant   # optional
nature: meteorological_analysis   # optional
geolocation: Charta::Point        # optional
values:
  indicator_1: a
  indicator_2: b
  ...

En cas d'erreur :

---
status: error
message: Vivamus mattis ullamcorper nibh, non sollicitudin
  nisl luctus nec. Praesent at rutrum diam. Aliquam nulla ante, 
  lobortis vel cursus id, varius eu erat. Sed venenatis scelerisque 
  sem, et pretium neque posuere quis. Morbi pretium tincidunt ipsum, 
  vel fringilla sem. Nunc ac posuere leo. Vivamus non auctor nunc, 
  vel commodo elit.

Côté modèle

Au niveau modèle, il faut prévoir une méthode pour effectuer la récupération des valeurs~:

class Sensor < Ekylibre::Record::Base
  ...
  # Parcourt et execute les tâches de récupération
  # pour chacun des capteurs
  def self.retrieve_all(mode)
    where(mode: mode).find_each do |sensor|
      connection = ActiveSensor.get(vendor, model, sensor.access_parameters)
      results = connection.retrieve(started_at: ..., stopped_at: ...)
      # Traiter et sauvegarder les résultats, gérer les erreurs
    end
  end
  ...
end

OSS sidekiq ne gérant pas les tâches programmées, voir pour Crono : https://github.com/plashchynski/crono En fait sidekiq-cron doit faire l'affaire à voir avec Apartment.