01/03/2018

Back-end IoT avec Ruby on Rails

Rails
Iot

Le but de ce tutoriel est de vous guider dans la création d'un back-end IoT avec Ruby on Rails

Souvent, le moyen le plus simple pour relier un producteur de données (capteur IoT) à un consommateur (appli mobile) est d'utiliser un serveur back-end placé entre le producteur et le consommateur. Le but de ce tutoriel est de vous guider dans la création d'un back-end, et, chemin faisant (pan pan), de vous montrer comme il est simple et rapide de le faire avec Ruby on Rails.

Imaginons que vous ayez une carte Arduino dotée de deux capteurs qui collectent température et humidité. Vous souhaitez visualisez ces mesures sous la forme de graphiques (thermomètre, jauge) dans une application mobile. Le rôle du serveur back-end va être de stocker les données qui lui seront transmises par une requête REST (HTTP POST) et de les restituer à la demande sous la forme d'une liste JSON via une requête HTTP GET.

FLUX = Capteurs > Back-end > Consommateurs

Installation de Ruby on Rails

Comme le framework Ruby on Rails (RoR) est basé sur le langage Ruby, il va falloir l'installer et dans la bonne version. Si vous êtes sous Linux ou MacOS, Ruby est surement déjà installé. Vérifions quelle version est installée :

$ ruby -v ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]

Ici on est bon car pour installer la dernière version de RoR (la 5.2), il faut au moins la version 2.2.2 de Ruby. Si ce n'est pas votre cas et que vous êtes sous Linux, je vous invite à installer rvm qui offre un grand choix de versions et une grande souplesse d'utilisation. Si vous êtes sous Windows, pas de panique, il existe aussi un kit d'installation http://rubyinstaller.org/downloads/

Par défaut, Rails va stocker ses données dans Sqlite, qui est souvent installé par défaut par les distributions Linux actuelle, mais vérifions quand même :

$ sqlite3 --version 3.11.0 2016-02-15 17:29:24

Ruby dispose de son propre gestionnaire de paquets RubyGems, à l'instar de Node.js avec npm. L'installation de Rails se fait grâce à la commande suivante :

$ gem install rails

Après un peu d'attente, vous devriez disposer de la toute dernière version Rails.

$ rails -v Rails 5.2.0

Nous sommes fin prêts. Let's go Ruby !

Mise en place du Back-end

RoR facilite la tâche des développeurs Web depuis bientôt 14 ans en fournissant un sur-ensemble au langage Ruby particulièrement bien pensé et dédié au domaine de la création d'applications Web. Cette sophistication se fait sentir dès la première commande qui vous allez passer pour demander la création de l'application.

$ rails new Backend

Tous les composants (Gems) nécessaires à son bon fonctionnement sont alors installés et configurés selon un principe cher à RoR; "Convention over Configuration". Ce qui signifie que ces composants sont configurés avec un paramétrage de base, issus d'un consensus, qui vous évite de devoir déjà faire des choix et permet de démarrer rapidement le développement son application. Pour une utilisation plus avancée, il est toujours possible d'affiner le paramétrage de base par le biais de l'édition des fichiers de configuration.

Comme nous avons besoin de stocker température et humidité, nous allons créer un modèle de données "Mesure" qui aura deux attributs: temperature, humidity

$ cd Backend $ rails generate scaffold Mesure temperature:integer humidity:integer $ rake db:migrate

En plus de créer le modèle de données 'Mesure' qui nous permettra de manipuler les données de ce type sans avoir à nous soucier de ce que cela implique comme transactions avec le moteur de base de données, Rails, dans sa grand bonté, a aussi créé tout ce qu'il faut comme code et formulaires pour pouvoir en profiter immédiatement dans le navigateur à cette adresse http://localhost:3000/mesures :

$ rails server => Booting Puma => Rails 5.2.0 application starting in development => Run `rails server -h` for more startup options Puma starting in single mode... * Version 3.11.4 (ruby 2.4.1-p111), codename: Love Song * Min threads: 5, max threads: 5 * Environment: development * Listening on tcp://0.0.0.0:3000

Vous allez pouvoir ajouter, lister, modifier et supprimer à loisirs les mesures que vous aurez entré manuellement, mais là n'est pas le but. Ces mesures doivent être envoyées par vos capteurs au Back-end, à intervalle régulier, au moyen d'une requête POST comme celle-ci :

$ curl --header "Content-Type: application/json" --request POST --data '{"mesure":{"temperature":27,"humidity":60}}' http:/localhost:3000/mesures.json

Pour que votre application mobile récupère toutes les données stockées dans le Back-end, il suffit de demander, via la requête HTTP GET suivante :

http://localhost:3000/mesures.json

Si au lieu d'avoir un message de succès, comme celui-là :

{"id":2,"temperature":27,"humidity":60,"created_at":"2018-06-13T12:15:12.158Z","updated_at":"2018-06-13T12:15:12.158Z","url":"http://localhost:3000/mesures/2.json"}

vous voyez surgir un erreur InvalidAuthenticityToken, vous devrez ajouter "protect_from_forgery prepend: true" au fichier app/controllers/mesures_controller.rb, juste après la première ligne. Cette alerte est provoquée par une sécurité qui est activée par défaut. Cela rentrera dans l'ordre une fois l'application hébergée sur un serveur distant (heroku?) et l'appel GET fait via HTTPS.

Inverser l'ordre des mesures

Par défault, le fichier JSON généré renvoie toutes les mesures dans l'ordre de création (de la plus ancienne à la plus récente). Comme nous allons être intéressé que par la dernière valeur obtenue par les capteurs, il va falloir modifier le comportement par défaut et définir comment obtenir que la dernière mesure, à savoir, trier les mesures de la plus récente à la plus ancienne (DESC), ce qui revient à inverser l'ordre par défaut (ASC) et limiter cette liste à un seul enregistrement. Pour ce faire nous allons utiliser deux scopes: un pour renverser l’ordre de tri, et un autre pour limiter la liste :

app/models/mesure.rb class Mesure < ApplicationRecord scope :desc, -> { order("mesures.id DESC") } scope :last_one, -> { limit(1) } end

Renvoyer que la dernière mesure

Maintenant que les scopes sont définis, pour obtenir la dernière mesure il suffit de demander comme suit :

app/controllers/mesures_controller.rb # GET /mesures # GET /mesures.json def index @mesures = Mesure.desc.last_one end

Rendre les données obligatoires

Afin d'être certain que les données de température et d'humidité sont bien présentes, nous allons ajouter des tests de validation pour chacune d'elles

app/models/mesure.rb class Mesure < ApplicationRecord scope :desc, -> { order("mesures.id DESC") } scope :last_one, -> { limit(1) } validates :temperature, presence: true validates :humidity, presence: true end

Alléger le fichier json

Modifiez le fichier pour qu'il ne reste que cette ligne:

app/views/mesures/_mesure.json.jbuilder json.extract! mesure, :id, :temperature, :humidity, :created_at

Si vous ne souhaitez pas avoir d'interface pour l'utilisateur mais uniquement les services d'une API,vous pouvez utiliser l'option --api lors de la création de l'application Rails.

Pour aller plus loin :

l'Authentification avec le gem Devise

Faire une petite beauté avec Bootstrap

Afficher les données sous forme graphique