Cellaserv : une architecture de communication

Introduction

L'un des grands problèmes dans la conception de robots est la communication entre les divers programmes, capteurs et actionneurs. La difficulté réside dans le fait que les capteurs et actionneurs ne sont pas tous sur une même carte parfois même pas dans le robot lui-même.

Afin de faciliter la communication entre toutes les entitées qui composent les robots, nous avons suivis la philosophie UNIX :

Chaque programme ne fait qu'une seule chose, mais la fait bien.

Notre solution s'appelle cellaserv. Nous allons décrire son fonctionnement dans cet article.

Aperçu du fonctionnement

Chaque programme lancé dans l'environement du robot est un "service" qui propose un certain nombre d'actions et peut demander à d'autres services des informations ou leur faire exécuter des actions. La communication entre les services est assurées par un programme principal appelé cellaserv.

Grossièrement une session cellaserv se déroule de la manière suivante:
  • cellaserv est démarré.
  • Dès qu'un service est lancé, il s'enregistre auprès de cellaserv en lui indiquant un nom (exemple : carte moteur). cellaserv retient le nom de ce service et garde une socket ouverte pour communiquer avec lui.
  • Si un service à besoin d'executer une action fournie par autre service, il fait une requète à cellaserv en lui indiquant qui doit faire quoi (exemple: carte moteur, tourne de 90).
  • cellaserv transmet alors l'ordre au bon service, et s'assure que la communication s'est bien passée.
  • Le service effectuant l'action peut répondre avec des informations, dans ce cas là il envoie sa réponse à cellaserv qui s'occupera de la transmettre au service ayant effectué la requête.
Prenons un exemple plus concret :
  • cellaserv est lancé au démarrage du robot.
  • Le programme hokuyo est lancé, il ouvre une socket sur cellaserv et enregistre le service hokuyo. Il propose d'envoyer les coordonnées des robots sur la table grâce à la requête robots.
  • De même le programme sonar s'enregistre à son tour, celui ci envoie une notifications pour signaler qu'un objet est proche.
  • Le service évitement est lancé. Il va continuellement faire des requêtes à hokuyo pour calculer les trajectoires du robots. Malgré les calculs de trajectoire censé éviter les robots adverses. Le service sonar nous informe par le biais d'une notification near qu'un objet est proche. Le service évitement écoute cette notification et impose donc l'arrêt du robot en envoyant une notification stop à tous les services.

Voici un schéma récapitulant la situation :

+------+    publish 'near'
|sonar |+--------+
|------|         |
|near  |         |
+------+         |
                 |                                      reply       +------+
                 |             +---------+            +------------+|hokuyo|
                 +------------>|cellaserv|<-----------+             |------|
                               +---------+                          |robots|
                                   +                                +------+
                                   |
                                   |
                                   |
               subscribe 'near'  +-+   request 'hokuyo.robots'
                                 |
                                 |
                                 v
                            +---------+
                            |évitement|
                            |---------|
                            |stop     |
                            +---------+

Aspect technique

Cellaserv utilise TCP/IP pour communiquer entre les divers services. Toutes les cartes doivent être connectées au même réseau, que ce soit en localhost, ethernet ou wifi.

Les protocole est basé sur des messages protobuf. Les données contenues dans ces messages sont généralement des objets JSON, mais on peut aussi envoyer une suite d'octets bruts.

Cellaserv est codé en go et s'exécute sur la carte principale du robot (voir mini210s). Il est également possible de le lancer sur son propre PC pour faire du debug.

Afin de faciliter la conception des services, nous avons développé des bibliothèques qui s'interface avec cellaserv.

Les langages/plateformes suivantes sont actuellement supportées :
  • cellaserv.proxy.CellaservProxy (python3)
En cours de développement :
  • go-cellaserv (Go)
  • mclas (C++ sur le microcontolleur mbed)

cellaservctl est un outil qui permet d'envoyer des messages depuis la ligne de commande, écouter les notifications ou bien lire les logs enregistrés par cellaserv.

Vocabulaire

Note

Dans ces exemples la présence de cellaserv est cachée, mais c'est bien avec lui que communiquent tous les clients, cellaserv transmet ensuite les messages aux destinataires de ceux-ci.

Les services cellaserv ont deux moyens de communiquer entre eux, soit un service s'adresse directement à un autre :

Le service X request Y, puis Y reply à X.

ou alors en publiant une notification :

Y subscribe à l'évènement 'foo', puis X publish l'évènement 'foo', Y reçoit la notification (publish) 'foo'.
Ainsi le protocole cellaserv définit plusieurs types de commandes :
  • register
  • request / reply
  • subscribe / publish

Ces commandes sont décrites dans les sections suivantes.

Register

Chaque service doit déclarer un nom auprès de cellaserv pour que des clients puisse lui envoyer des actions par la commande request.

Un client n'a pas besoin de s'enregistrer pour envoyer des commandes ou souscrire (subscribe) ou recevoir des notifications (publish).

Request / reply

Un client envoie une request à un service, qui va lui répondre avec la commande reply, contenant si besoin la réponse à la requête. Si le client ne reply pas, ou pas assez rapidement, cellaserv notifie le client en envoyant une erreur Timeout.

Subscribe / publish

Chaque service peut s'abonner à un évènement via la commande subscribe, un client peut alors envoyer une notification pour un évenement en utilisant la commande publish, tous les clients abonnés a cet évènement vont recevoir le message. L'intérêt est de pouvoir envoyer un message à des services sans même connaitre leur nom.

Exemple : le script d'IA peut envoyer la notification start, tous les services abonnés à cette notification vont donc exéctuer la fonction start.

Installation de cellaserv

cellaserv est déjà installé et lancé au démarrage sur le robot principal (utilisant la mini210s).

Une instance de cellaserv tourne en permanence sur evolutek.org (port par défaut : 4200).

Vous pouvez aussi l'installer sur votre machine, pour cela il existe deux méthodes :

Avec Archlinux

La procédure est décrite dans un article de la documentation evolutek : doc.evolutek.org

Compilation

Il est également possible de compiler cellaserv à partir du dépot git, vous devez alors avoir un environnement de développement go fonctionel.

$ go get bitbucket.org/evolutek/cellaserv2

Le binaire cellaserv2 se trouvera alors dans le dossier "$GOPATH/bin/".

Utiliser cellaserv et des services

Note

Par défaut python-cellaserv2 et cellaservctl essayent de se connecter à evolutek.org:4200.

Toutes les applications cellaserv lisent la valeur des variables d'environnement suivantes :

CS_HOST
Permet de définir l'IP ou est lancé cellaserv
CS_PORT
Port du serveur cellaserv
CS_DEBUG
Niveau de verbosité 0, 1, 2 ou 3.

Pour faire des tests, il suffit de lancer un serveur cellaserv en local et de lancer le service avec CS_HOST=127.0.0.1 :

$ cellaserv2

Puis dans un autre shell :

$ CS_HOST=127.1 cellaservctl publish log.foo hello=world
$ CS_HOST=127.1 cellaservctl log foo

Vous pouvez utiliser le fichier /etc/conf.d/cellaserv pour stocker les options de façon plus permanente. Son format est :

[cellaserv]
port = 4200
debug = 2

[client]
host = evolutek.org
port = 4200
debug = 2