Héritage simple

Dans l'étude précédent, nous avons évoqués les principes généraux de l’héritage sans tenir compte d’un langage quelconque. Ce chapitre sera donc consacré à l’étude de l’héritage simple codé avec le langage C++. L'interactivité croissante des applications web, consécutive à l'amélioration des performances des navigateurs, a rapidement rendu nécessaire le développement de techniques de communications bidirectionnelles entre l'application web et les processus serveur. Le protocole WebSocket vise à développer un canal de communication bidirectionnel et full-duplex sur un socket TCP pour les navigateurs et les serveurs web.

Liste des travaux pratiques

Les websockets et le service écho sur pcDuino

Les cartes pcDuino disposent de la librairie Qt au même titre que les PC classiques. Nous allons en profiter pour mettre en oeuvre des services qui pourront être exploités au travers d'un Intranet, mais aussi éventuellement depuis Internet. Pour que cela soit simple à pratiquer, nous mettrons en place un service Websocket avec le protocole adapté. Je rappelle que la notion de service sous-entend la possibilité de communiquer avec plusieurs clients en même temps.

Inconvénient du protocole HTTP et Intérêt du protocole WebSocket.

Le protocole HTTP est un protocole ancien et très fiable. Toutefois, il possède un gros défaut. Son fonctionnement est le suivant : le client se connecte, il soumet une requête, le serveur lui donne sa réponse et dès que la réponse est reçue par le client, la connexion est tout de suite interrompue. Dans ce système là, à aucun moment le serveur peut envoyer une information de lui-même, par exemple pour notifier une alerte à un client particulier. Ainsi, avec ce protocole HTTP, il n'est pas possible à un client de dialoguer avec un autre client.

Avec le protocole WebSocket, il en va tout autrement. Ce protocole utilise les technologies Web à l'image du protocole HTTP mais grâce à cette notion de socket, il permet en plus d'établir une connexion permanente et bidirectionnelle. Ainsi, quand ils le veulent, le client et le serveur peuvent envoyer librement des données à l’autre par ce protocole. Il partage le port HTTP 80 Web avec le contenu existant, traversant facilement les différents pare-feu, routeurs, proxy, etc… La grande nouveauté, c'est que le serveur peut lui-même, quand il le désire, envoyer des données vers le client et n’est pas obligé d’attendre que ce dernier ne soumette une requête. L'exemple par excellence est l'utilisation du service DropBox qui utilise ces compétences là.

Intérêt WebSocket

Le protocole des WebSockets change la manière dont un serveur web réagit aux requêtes d'un client : au lieu de fermer la connexion, il envoie un message avec le code HTTP 101 et laisse la connexion ouverte. Les deux parties s'attendent ensuite à recevoir des données et à écrire sur le flux. Contrairement à HTTP, le protocole supporte une communication bilatérale complète de telle manière que le client et le serveur peuvent s'échanger des messages en même temps. Grâce à cette technique, il est maintenant possible à un client de communiquer en temps réel avec un autre client par le biais du service WebSocket, et ceci éventuellement depuis Internet.

Diagramme des cas d'utilisation

Afin de bien maîtriser ces technologies, nous commencerons par un service très simple, nommé écho, qui renvoie tout simplement le même texte soumis par le client. Toutefois, nous en profitons pour envoyer en même temps que le message reçu, l'adresse IP du poste client. Nous utiliserons le navigateur pour soumettre nos messages à l'aide d'un site dédié à la communication à l'aide du protocole websocket. Plus tard, nous fabriquerons une application fenêtrée qui remplacera ce site particulier. Vous avez un exemple d'utilisation ci-dessous :

Diagramme de déploiement

Une fois que le service echo est lancée dans la carte pcDuino, plusieurs clients peuvent l'utiliser à l'aide d'un simple navigateur comme cela vous a été montré précédemment.

Diagramme des classes

La mise en oeuvre du service websocket se fait au travers d'une classe déjà toute faite et qui se nomme QWebSocketServer. Le service permettra de communiquer avec plusieurs clients en même temps. Une fois que le service est monté, nous pouvons communiquer avec un des clients au travers du protocole websocket à l'aide également d'une classe spécialisée qui se nomme QWebSocket. Chaque objet de cette classe représente un client en particulier.

Le principe général d'un système qui communique au travers du réseau, quelque soit le protocole utilisé, est de mettre en place une gestion événementielle de la même façon que lors de la construction d'une IHM. Effectivement, dès qu'un message arrive, il faut tout de suite activer un slot qui prendra en compte cette requête et réagira en conséquence. Pour chaque client connecté, vous devez proposer une gestion événementielle attitrée qui permettra par la suite au service de communiquer avec chacun d'entre eux.Afin de capitaliser toute cette organisation, nous allons mettre en oeuvre la classe Service, qui est une classe assez générale, qui permet de lancer un service websocket en choisissant le nom et le port, qui enregistre l'ensemble des clients déjà connectés avec leurs identifiants de connexion et qui prend en compte toute l'ossature événementielle quelque soit la gestion que nous en ferons par la suite. Cette classe sera utilisable dans tous les projets qui utiliseront ce type de service. Pour cela, il sera nécessaire de créer une nouvelle classe qui hérite de cette classe Service, et il faudra redéfinir la méthode receptionMessage() qui récupère le message soumis par un des clients le dernier client qui envoie le message est gardé en mémoire à l'aide de l'attribut client afin de proposer un traitement spécifique à ce que vous souhaitez faire. Justement, pour réaliser le service echo, nous créons une classe Echo qui hérite de Service et qui permet de renvoyer le message soumis par le dernier client, avec l'adresse IP de son poste.
Mise en oeuvre du codage

Dans le fichier de projet, il est nécessaire d'intégrer le module websockets afin que cette technologie soit bien pris en compte. Le réseau n'est pas intégré par défaut. Il suffit juste de le spécifier. Un dernier point, lorsque nous lançons le service, ce dernier reste constamment en fonctionnement. Une solution consiste à aller sur la cible et de tuer kill le programme correspondant. Une autre, qui a été mise en oeuvre ici, consiste à envoyer, à l'aide du navigateur, un message avec le mot stop. Automatiquement le programme s'arrête de lui-même.

echo.pro
QT       += core websockets
QT       -= gui
CONFIG   += console c++11
TEMPLATE = app

SOURCES += main.cpp service.cpp echo.cpp
HEADERS += service.h echo.h

TARGET = echo
target.path = /home/ubuntu/Public
INSTALLS += target
service.h
#ifndef SERVICE_H
#define SERVICE_H

#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <vector>
using namespace std;

class Service : public QObject
{
  Q_OBJECT
public:
  explicit Service(QObject *parent, const char *nom, int port);
protected:
  vector<QWebSocket *> connectes;
  QWebSocket *client;
private:
  QWebSocketServer service;
signals:
  void arreter();
public slots:
  void nouvelleConnexion();
  virtual void receptionMessage(const QString &texte);
  void deconnexion();
};

#endif // SERVICE_H
service.cpp
#include "service.h"
#include <algorithm>

Service::Service(QObject *parent, const char *nom, int port) : QObject(parent), service(nom, QWebSocketServer::NonSecureMode, this)
{
  if (service.listen(QHostAddress::Any, port))
    connect(&service, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
  else arreter();
}

void Service::nouvelleConnexion()
{
  QWebSocket *client = service.nextPendingConnection();
  connectes.push_back(client);
  client->sendTextMessage("Bienvenue !");
  connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  connect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}

void Service::receptionMessage(const QString &texte)
{
  if (texte=="stop") { arreter(); return; }
  client = (QWebSocket *) sender();
}

void Service::deconnexion()
{
  QWebSocket *client = (QWebSocket *) sender();
  client->deleteLater();
  connectes.erase(find(connectes.begin(), connectes.end(), client));
  disconnect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  disconnect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
echo.h
#ifndef ECHO_H
#define ECHO_H

#include "service.h"

class Echo : public Service
{
public:
  Echo() : Service(nullptr, "chat", 8080) {}
  void receptionMessage(const QString &message) override;
};

#endif // ECHO_H
echo.cpp
#include "echo.h"

void Echo::receptionMessage(const QString &message)
{
   Service::receptionMessage(message);
   QString adresse = client->peerAddress().toString();
   client->sendTextMessage(QString("[%1] > %2").arg(adresse).arg(message));
}
main.cpp
#include <QCoreApplication>
#include "echo.h"

int main(int nombre, char *options[])
{
    QCoreApplication appli(nombre, options);
    Echo websocket;
    QObject::connect(&websocket, SIGNAL(arreter()), &appli, SLOT(quit()));
    return appli.exec();
}

Gérer l'afficheur depuis l'Intranet

Maintenant que nous connaissons bien la mise en place d'un service WebSocket avec son protocole associé, et puisque nous sommes capables de le placer directement à l'intérieur d'un système embarqué comme le pcDuino, nous allons en profiter pour gérer l'affichage de message depuis l'Intranet sur l'afficheur AM-03127-LED connecté à la carte à l'aide de la prise USB.

Diagramme des cas d'utilisation

Diagramme de déploiement

Au niveau de la structure matérielle, nous retrouvons la même architecture que lors du projet précédent, toutefois, cette fois-ci nous connectons l'afficheur sur la prise USB de la carte pcDuino. Par ailleurs, nous devons rajouter les fichiers afficheur.h et afficheur.cpp que nous avons déjà implémentés lors d'un projet précédent. Enfin, le service echo est remplacé par le service MessageWeb dont l'implémentation est fournie par les deux fichiers messageweb.h et messageweb.cpp.

Diagramme des classes

Le diagramme de classes conserve l'ossature principale que j'ai déjà évoqué lors du projet précédent. Nous trouvons en plus la classe MessageWeb qui remplace la classe Echo. Son objectif est de récupérer des messages venant du réseau, soumis par les différents clients, et de les transmettre à l'afficheur AM-03127-LED par l'intermédiaire de la classe Afficheur sur laquelle nous avons déjà réalisé le code. Il existe toutefois une différence par rapport à une étude antérieure. Puisque nous utilisons la librairie Qt, cette dernière dispose d'une classe qui implémente l'interface série et par là même le port USB. Il s'agit de la classe QSerialPort qui remplace donc la classe initiale serialib.

Mise en oeuvre du codage

Vous avez ci-dessous l'ensemble du codage nécessaire au bon fonctionnement du système. Dans le fichier de projet, afin de permettre la prise en compte de l'interface série, il est indispensable d'intégrer le module serialport en même temps que le module websockets.

AfficheurWeb.pro
QT       += core websockets serialport
QT       -= gui
CONFIG   += console c++11
TEMPLATE = app

TARGET = AfficheurWeb
target.path = /home/ubuntu/Public
INSTALLS += target

SOURCES += main.cpp service.cpp afficheur.cpp messageweb.cpp
HEADERS += service.h afficheur.h messageweb.h
service.h
#ifndef SERVICE_H
#define SERVICE_H

#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <vector>
using namespace std;

class Service : public QObject
{
  Q_OBJECT
public:
  explicit Service(QObject *parent, const char *nom, int port);
protected:
  vector<QWebSocket *> connectes;
  QWebSocket *client;
private:
  QWebSocketServer service;
signals:
  void arreter();
public slots:
  void nouvelleConnexion();
  virtual void receptionMessage(const QString &texte);
  void deconnexion();
};

#endif // SERVICE_H
service.cpp
#include "service.h"
#include <algorithm>

Service::Service(QObject *parent, const char *nom, int port) : QObject(parent), service(nom, QWebSocketServer::NonSecureMode, this)
{
  if (service.listen(QHostAddress::Any, port))
    connect(&service, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
  else arreter();
}

void Service::nouvelleConnexion()
{
  QWebSocket *client = service.nextPendingConnection();
  connectes.push_back(client);
  client->sendTextMessage("Bienvenue !");
  connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  connect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}

void Service::receptionMessage(const QString &texte)
{
  if (texte=="stop") { arreter(); return; }
  client = (QWebSocket *) sender();
}

void Service::deconnexion()
{
  QWebSocket *client = (QWebSocket *) sender();
  client->deleteLater();
  connectes.erase(find(connectes.begin(), connecetes.end(), client));
  disconnect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  disconnect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
afficheur.h
#ifndef AFFICHEUR_H
#define AFFICHEUR_H

#include <QSerialPort>
#include <string>
using namespace std;

class Afficheur
{
  QSerialPort usb;
  bool connexion;
public:
  Afficheur();
  ~Afficheur()  { affichePage(" "); usb.close(); }
  bool etat()   { return connexion; }

  void affichePage(const string &message);
private:
  string checksum(string texte);
};

#endif // AFFICHEUR_H
afficheur.cpp
#include "afficheur.h"
#include <sstream>
#include <unistd.h>

Afficheur::Afficheur()
{
  usb.setBaudRate(QSerialPort::Baud9600);
  usb.setPortName("/dev/ttyUSB0");
  connexion = usb.open(QIODevice::WriteOnly);
}

void Afficheur::affichePage(const string &message)
{
  string commande = "<L1><PA><FE><MA><WC><FE><CE>";
  commande += message;
  ostringstream trame;
  trame << "<ID01>" << commande << checksum(commande) << "<E>";
  usb.write(trame.str().c_str());
  usleep(50000);
}

string Afficheur::checksum(string texte)
{
  char calcul = 0;
  for (char octet : texte) calcul^=octet;
  char bas = calcul & 0b00001111;
  char haut = (calcul & 0b11110000) >> 4;
  bas = bas < 10 ? bas+=0x30 :bas+=0x41-10;
  haut = haut < 10 ? haut+=0x30 : haut+=0x41-10;
  return {haut, bas};
}
messageweb.h
#ifndef MESSAGEWEB_H
#define MESSAGEWEB_H

#include "service.h"
#include "afficheur.h"

class MessageWeb : public Service
{
  Afficheur lcd;
public:
  MessageWeb() : Service(nullptr, "afficheur", 8090) {}
  void receptionMessage(const QString &message) override;
};

#endif // MESSAGEWEB_H
messageweb.cpp
#include "messageweb.h"

void MessageWeb::receptionMessage(const QString &message)
{
Service::receptionMessage(message);
 if (lcd.etat()) { lcd.affichePage(message.toStdString()); client->sendTextMessage("message transmis"); } else client->sendTextMessage("Afficheur non connecté"); }
main.cpp
#include <QCoreApplication>
#include "messageweb.h"

int main(int nombre, char *options[])
{
    QCoreApplication appli(nombre, options);
    MessageWeb websocket;
    QObject::connect(&websocket, SIGNAL(arreter()), &appli, SLOT(quit()));
    return appli.exec();
}

Service de température

Ce service va permettre à chaque client connecté de connaître la température ambiante de la pièce. Le rafraichissement se fera toutes les cinq secondes. La particularité de ce projet, c'est que cette fois-ci le client ne soumet aucun message. Il se contente de se connecter. Une fois que la connexion est établie, il reçoit périodiquement la valeur de la température. Vous remarquez que c'est systématiquement le service qui envoie un message à chacun de ses clients. Nous voyons bien ainsi l'intérêt de ce type de service, c'est que n'importe quel système informatique peut communiquer une information quand il le désire, ce n'est pas toujours le client qui initie le dialogue.

Diagramme des cas d'utilisation

Diagramme de déploiement

Diagramme des classes

La classe Temperature est relativement modeste. Par rapport aux projets précédents vous n'avez plus à redéfinir la méthode receptionMessage(). Puisque Temperature hérite de Service, la classe connaît automatiquement tous les clients connectés. Il est ainsi facile de pouvoir communiquer avec chacun d'entre eux. Par ailleurs, puisque Service hérite de QObject, nous avons la possibilité de lancer le timer intégré et du coup de pouvoir réaliser des traitements spécifiques périodiquement toutes les cinq secondes grâce à la redéfinition de la méthode protégée timerEvent(). Enfin, la classe Temperature dispose de l'attribut capteur qui nous renseigne sur la température ambiante.

Mise en oeuvre du codage
 
TemperatureWeb.pro
QT       += core websockets
QT       -= gui
CONFIG   += console c++11
TEMPLATE = app

TARGET = TemperatureWeb
target.path = /home/ubuntu/Public
INSTALLS += target

SOURCES += main.cpp service.cpp temperature.cpp adc.cpp
HEADERS += service.h temperature.h adc.h
service.h
#ifndef SERVICE_H
#define SERVICE_H

#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <vector>
using namespace std;

class Service : public QObject
{
  Q_OBJECT
public:
  explicit Service(QObject *parent, const char *nom, int port);
protected:
  vector<QWebSocket *> connectes;
  QWebSocket *client;
private:
  QWebSocketServer service;
signals:
  void arreter();
public slots:
  void nouvelleConnexion();
  virtual void receptionMessage(const QString &texte);
  void deconnexion();
};

#endif // SERVICE_H
service.cpp
#include "service.h"
#include <algorithm>

Service::Service(QObject *parent, const char *nom, int port) : QObject(parent), service(nom, QWebSocketServer::NonSecureMode, this)
{
  if (service.listen(QHostAddress::Any, port))
    connect(&service, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
  else arreter();
}

void Service::nouvelleConnexion()
{
  QWebSocket *client = service.nextPendingConnection();
  connectes.push_back(client);
  client->sendTextMessage("Bienvenue !");
  connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  connect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}

void Service::receptionMessage(const QString &texte)
{
  if (texte=="stop") { arreter(); return; }
  client = (QWebSocket *) sender();
}

void Service::deconnexion()
{
  QWebSocket *client = (QWebSocket *) sender();
  client->deleteLater();
  connectes.erase(find(connectes.begin(), connectes.end(), client));
  disconnect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  disconnect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
adc.h
#ifndef ADC_H
#define ADC_H

#include <fstream>
using namespace std;

class ADC
{
  ifstream broche;
  static const double convertisseur; // 0.806 mV par unité
  const double proportion;
public:
  ADC(unsigned numero, double proportion);
  ~ADC();
  double lire();
};

#endif // ADC_H
adc.cpp
#include "adc.h"
#include <stdlib.h>

const double ADC::convertisseur = 0.806;  // 0.806 mV par unité

ADC::ADC(unsigned numero, double proportion) : proportion(convertisseur*proportion)
{
  string localisation = "/proc/adc";
  localisation += '0' + numero;
  broche.open(localisation.c_str());
}

ADC::~ADC()
{
  broche.close();
}

double ADC::lire()
{
  string texte;
  broche.seekg(0);
  broche >> texte;
  string numerique = texte.substr(5);
  return atoi(numerique.c_str()) * proportion;
}
temperature.h
#ifndef TEMPERATURE_H
#define TEMPERATURE_H

#include "service.h"
#include "adc.h"

class Temperature : public Service
{
  ADC capteur;
public:
  Temperature();
protected:
  void timerEvent(QTimerEvent *);
};

#endif // TEMPERATURE_H
temperature.cpp
#include "temperature.h"

Temperature::Temperature() : Service(nullptr, "temperature", 8085), capteur(2, 0.1)
{
  startTimer(5000);
}

void Temperature::timerEvent(QTimerEvent *)
{
  for (auto client : connectes)
    client->sendTextMessage(QString("%1°").arg(capteur.lire(), 0, 'f', 1));
}
main.cpp
#include <QCoreApplication>
#include "temperature.h"

int main(int nombre, char *options[])
{
    QCoreApplication appli(nombre, options);
    Temperature websocket;
    QObject::connect(&websocket, SIGNAL(arreter()), &appli, SLOT(quit()));
    return appli.exec();
}

Gestion de la fréquence de clignotement

Dans ce dernier projet, je vous propose d'agir à distance sur le module d'entrées-sorties tout où rien GPIO. Le service en action lance une activité relativement simple qui permet de faire clignoter une led avec une fréquence de clignotement variable. Chaque client à distance peut se connecter à ce service et soumettre une nouvelle fréquence de clignotement qui sera automatiquement pris en compte en temps réel. Afin de bien résoudre ce type de fonctionnalité notion de supervision, il est indispensable de mettre en oeuvre la technique des threads sur laquelle nous avons déjà travaillé dans l'étude précédente.

Diagramme des cas d'utilisation

Diagramme de déploiement

Diagramme des classes

Mise en oeuvre du codage

La grande particularité de ce projet, c'est de lancer d'autres tâches annexes qui correspondent aux différents processus de fonctionnement du service lui-même. La classe qui gère le service souhaité, ici Clignotement, doit dès le départ dans le constructeur lancer les différentes tâches qui s'exécuteront durant toute l'activité du service. Dès que ce dernier doit s'interrompre, avant sa fin, vous devez clôturer les différentes tâches en cours de fonctionnement. Cette clôture doit donc être réalisée à la fin du service, donc dans le destructeur symétrie par rapport à la phase de construction.

clignotement.pro
QT       += core websockets
QT       -= gui
CONFIG   += console c++11
TEMPLATE = app
LIBS += -pthread

TARGET = clignotement target.path = /home/ubuntu/Public INSTALLS += target SOURCES += main.cpp service.cpp clignotement.cpp gpio.cpp HEADERS += service.h clignotement.h gpio.h
service.h
#ifndef SERVICE_H
#define SERVICE_H

#include <QObject>
#include <QWebSocketServer>
#include <QWebSocket>
#include <vector>
using namespace std;

class Service : public QObject
{
  Q_OBJECT
public:
  explicit Service(QObject *parent, const char *nom, int port);
protected:
  vector<QWebSocket *> connectes;
  QWebSocket *client;
private:
  QWebSocketServer service;
signals:
  void arreter();
public slots:
  virtual void nouvelleConnexion();
  virtual void receptionMessage(const QString &texte);
  void deconnexion();
};

#endif // SERVICE_H
service.cpp
#include "service.h"
#include <algorithm>

Service::Service(QObject *parent, const char *nom, int port) : QObject(parent), service(nom, QWebSocketServer::NonSecureMode, this)
{
  if (service.listen(QHostAddress::Any, port))
    connect(&service, SIGNAL(newConnection()), this, SLOT(nouvelleConnexion()));
  else arreter();
}

void Service::nouvelleConnexion()
{
  QWebSocket *client = service.nextPendingConnection();
  connectes.push_back(client);
  client->sendTextMessage("Bienvenue !");
  connect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  connect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}

void Service::receptionMessage(const QString &texte)
{
  if (texte=="stop") { arreter(); return; }
  client = (QWebSocket *) sender();
}

void Service::deconnexion()
{
  QWebSocket *client = (QWebSocket *) sender();
  client->deleteLater();
  connectes.erase(find(connectes.begin(), connectes.end(), client));
  disconnect(client, SIGNAL(textMessageReceived(QString)), this, SLOT(receptionMessage(QString)));
  disconnect(client, SIGNAL(disconnected()), this, SLOT(deconnexion()));
}
gpio.h
#ifndef GPIO_H
#define GPIO_H

#include <fstream>
using namespace std;

enum MODE {Entree, Sortie};

class GPIO
{
  fstream broche;
  const MODE mode;
public:
  GPIO(MODE m, unsigned numero);
  ~GPIO();
  bool etat();
  void activer(bool action);
};

#endif // GPIO_H
gpio.cpp
#include "gpio.h"

GPIO::GPIO(MODE m, unsigned numero) : mode(m)
{
  string localisation = "/sys/devices/virtual/misc/gpio/mode/gpio";
  localisation += '0' + numero;
  ofstream modeGPIO(localisation.c_str());
  modeGPIO << (mode==Sortie ? '1' : '0');
  modeGPIO.close();
  localisation = "/sys/devices/virtual/misc/gpio/pin/gpio";
  localisation += '0' + numero;
  broche.open(localisation.c_str());
}

GPIO::~GPIO()
{
  if (mode==Sortie) broche << '0';
  broche.close();
}

void GPIO::activer(bool action)
{
  if (mode==Sortie) {
    broche << (action ? '1' : '0');
    broche.flush();
  }
}

bool GPIO::etat()
{
  broche.seekg(0);
  return broche.peek()=='1';
}
clignotement.h
#ifndef CLIGNOTEMENT_H
#define CLIGNOTEMENT_H

#include <thread>
#include "service.h"
#include "gpio.h"

class Clignotement : public Service
{
    std::thread *tache;
    bool fin = false;
    int periode = 500000;
public:
    Clignotement();
    ~Clignotement();
    void nouvelleConnexion() override;
    void receptionMessage(const QString &message) override;
};

#endif // CLIGNOTEMENT_H
clignotement.cpp
#include "clignotement.h"
#include <unistd.h>

Clignotement::Clignotement() : Service(nullptr, "clignoter", 9090)
{
    tache = new std::thread([&](){
        GPIO led(Sortie, 3);
        bool sortie = false;
        while(!fin) {
            sortie = !sortie;
            led.activer(sortie);
            usleep(periode);
        }
    });
}

Clignotement::~Clignotement()
{
    fin = true;
    tache->join();
    delete tache;
}

void Clignotement::nouvelleConnexion()
{
  Service::nouvelleConnexion();
  connectes.back()->sendTextMessage("Précisez votre nouvelle période en millisecondes");
}

void Clignotement::receptionMessage(const QString &message)
{
    Service::receptionMessage(message);
    periode = message.toInt() * 1000;
}
main.cpp
#include <QCoreApplication>
#include "clignotement.h"

int main(int nombre, char *options[])
{
    QCoreApplication appli(nombre, options);
    Clignotement websocket;
    QObject::connect(&websocket, SIGNAL(arreter()), &appli, SLOT(quit()));
    return appli.exec();
}
Dans la classe Clignotement, nous avons réalisé la tâche correspondant au pilotage de la led par l'intermédiaire d'un objet anonyme, créé durand la phase de construction et détruit dans la phase de destruction. Il est possible d'avoir à la place un attribut qui correspond exactement à la même tâche. La description de la tâche doit alors se faire durant la liste d'initialisation du constructeur ce qui est peut être un peu moins lisible mais plus performant. Voici ci-dessous les modifications du code associé à ce principe :
clignotement.h
#ifndef CLIGNOTEMENT_H
#define CLIGNOTEMENT_H

#include <thread>
#include "service.h"
#include "gpio.h"

class Clignotement : public Service
{
    std::thread tache;
    bool fin = false;
    int periode = 500000;
public:
    Clignotement();
    ~Clignotement();
    void nouvelleConnexion() override;
    void receptionMessage(const QString &message) override;
};

#endif // CLIGNOTEMENT_H
clignotement.cpp
#include "clignotement.h"
#include <unistd.h>

Clignotement::Clignotement() : Service(nullptr, "clignoter", 9090), tache([&]() // liste d'initialisation
{
     GPIO led(Sortie, 3);
     bool sortie = false;
     while(!fin) {
         sortie = !sortie;
         led.activer(sortie);
         usleep(periode);
     }
}) { } // constructeur vide

Clignotement::~Clignotement()
{
    fin = true;
    tache.join();
}

void Clignotement::nouvelleConnexion()
{
  Service::nouvelleConnexion();
  connectes.back()->sendTextMessage("Précisez votre nouvelle période en millisecondes");
}

void Clignotement::receptionMessage(const QString &message)
{
    Service::receptionMessage(message);
    periode = message.toInt() * 1000;
}