Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

fr:specs:sensors [2016/08/26 21:06] (Version actuelle)
Ligne 1: Ligne 1:
 +====== 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.
 +
 +<WRAP center round todo 60%>
 +Spécification en cours de rédaction
 +</​WRAP>​
 +
 +===== 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...).
 +
 +<​graphviz dot center>
 +digraph {
 +  rankdir=BT;
 +  Sensor -> Product [label=product];​
 +  Sensor -> Product [label=host];​
 +  Analysis -> Sensor;
 +  Analysis -> Product [label=product];​
 +  Analysis -> Product [label=host];​
 +  AnalysisItem -> Analysis;
 +}
 +</​graphviz>​
 +=== 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...
 +  * ''​product''​ => ''​Product''​~:​ Produit du capteur (si approvisionné)
 +  * ''​embedded''​~:​ Si le capteur est embarqué
 +  * ''​host''​ => ''​Product''​~:​ 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 :
 +  * ''​product''​ => ''​Product''​
 +  * ''​host''​ => ''​Product''​
 +  * ''​sensor''​ => ''​Sensor''​
 +  * ''​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)
 +
 +<code ruby 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
 +</​code>​
 +
 +<code ruby 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'​)
 +...
 +</​code>​
 +
 +<WRAP center round important 60%>
 +Le fichier de conf YAML est une idée. À voir pour la pertinence.
 +</​WRAP>​
 +
 +
 +<code yaml 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"​
 +    ...     
 +</​code>​
 +
 +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 :
 +<code 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
 +  ...
 +</​code>​
 +
 +En cas d'​erreur :
 +
 +<code yaml>
 +---
 +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.
 +</​code>​
 +
 +=== Côté modèle ===
 +Au niveau modèle, il faut prévoir une méthode pour effectuer la récupération des valeurs~:
 +<code ruby>
 +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
 +</​code>​
 +
 +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.