Tutorat système & Jeu du mouton
Thomas Vantroys, Nathalie Devesa & Xavier Redon
1 Description générale du projet
1.1 Objectif
L'objectif est de créer des costumes (des tee-shirts en l'occurrence) et un logiciel
permettant de jouer à un jeu collectif. Le jeu s'intitule 'le jeu du mouton" et il
consiste à parier sur la réaction des autres joueurs.
1.2 Règle du jeu
Avant le lancement d'une partie, chaque joueur choisit le rôle du mouton blanc ou le
rôle du mouton noir. Dans le rôle du mouton blanc, le joueur doit s'efforcer de suivre
le même comportement que les autres joueurs quelque soit leur rôle. Dans le rôle du
mouton noir, le joueur doit arriver à se distinguer des autres joueurs. Dans cette
version du jeu, les groupes sont constitués en fonction de leur position. Les cinq
positions possibles sont : buste droit, buste penché vers l'avant, buste penché vers
l'arrière, corps penché vers la droite, corps penché vers la gauche.
Un étape commence par un avertissement de la part du contrôleur, les joueurs ont alors
quelques secondes pour prendre position. Les positions sont relevées et les groupes formés.
Un mouton blanc se trouvant dans un groupe de cardinalité non maximale est éliminé. Un
mouton noir se trouvant dans un groupe de cardinalité non minimale est éliminé. Une
autre étape est lancée avec les joueurs non éliminés. Le jeu se termine quand il ne
reste plus qu'un ou deux joueurs. Ces joueurs sont déclarés gagnants.
1.3 Architecture générale
Les costumes remontent les informations de leur capteur de position à un serveur de jeu qui
décide des joueurs éliminés à chaque phase du jeu. Le costume est réalisé à base d'Arduino
Lilypad.
La remontée des données se fait par communication sans fil XBee entre le costume
et un processus client tournant sur le PC associé au costume puis par TCP/IP entre le
processus client et un serveur de jeu central. Le serveur central est choisi parmi l'un
des processus client.
L'architecture de communication est établie en utilisant un exécutable d'administration
sur chaque PC qui demande au processus client d'établir une connexion TCP vers le serveur
de jeu. La communication entre l'exécutable d'administration et le processus client est
réalisée par une file de messages (IPC).
1.4 Conception du costume
Le costume est réalisé avec un module Lilypad intégrant un micro-contrôleur atmega328p,
un module de communication série sans fil XBee et divers capteurs et actionneurs ;
un accéléromètre, une led RVB, un vibreur et un buzzer.
La programmation du micro-contrôleur se fait en utilisant l'IDE arduino. L'algorithme
du micro-contrôleur consiste en la lecture des ordres arrivant sur le port série et en la
réalisation des actions ou mesures demandées. Les valeurs mesurées sont renvoyées via le
même port série.
Prenez soin de configurer vos deux modules XBee pour qu'ils puissent communiquer
entre eux sur une fréquence leur étant propre.
1.5 Conception de l'application client/serveur
L'application implante deux fonctionnalités. Une fonctionnalité de gestion de costume qui est
activée si le joueur participe à un jeu et une fonctionnalité de serveur de jeu qui est activée
si d'autres gestionnaires de costumes se connectent à lui.
La gestion du costume est à implanter dans un processus léger dédié. Ce processus se contente
de lire les instructions du serveur de jeu arrivant par la connexion TCP et à les répercuter
au costume via le port série du PC.
La fonction de serveur de jeu consiste à accepter les connexions des autres gestionnaires
de costumes et a superviser un jeu sur demande de l'administrateur. Un processus léger doit
être utilisé pour conduire le déroulement du jeu.
Enfin, un dernier processus léger est chargé de traiter les requêtes de l'exécutable
d'administration.
1.6 Connexion au serveur de jeu
Quand un gestionnaire de costume C se connecte par TCP au serveur de jeu J,
le protocole décrit ci-dessous doit être respecté.
-
C se connecte sur le port d'écoute TCP de J ;
- C indique son pseudo par la commande pseudo suivie du dit pseudo ;
- J indique si le pseudo est libre par OK ou NOTFREE ;
- C indique son mode de jeu par la commande mode suivie du mot
blanc ou noir ;
- J doit répondre par OK si la syntaxe est correcte et par ERROR sinon ;
- J peut demander à C de déclencher un actionneur par la commande SET,
C doit répondre par OK, si ce n'est pas le cas la connexion est rompue ;
- J peut demander à C de récupérer la valeur d'un capteur par la commande GET,
C doit répondre par OK value où value est la valeur retournée par le capteur ;
- si C se déconnecte, J arrête le processus léger correspondant et retire le joueur du jeu ;
- si J se déconnecte, C arrête le processus léger de gestion de costume.
La commande SET est suivie d'un mot clef indiquant l'actionneur concerné et de paramètres
permettant de le configurer. Voici les syntaxes à utiliser pour ce projet :
-
SET LED color : color est un octet écrit en hexadécimal dont les
deux bits de poids faibles représentent le niveau de bleu, les deux bits suivant le niveau de vert et
les deux bits suivant le niveau de rouge (les deux bits de poids forts sont fixés à zéro) ;
- SET BUZZ frequency : frequency est un octet écrit en hexadécimal représentant
la fréquence à jouer sur le buzzer (0 pour le la 220Hz et 6 pour sol) ;
- SET VIBE duration : duration est un octet écrit en décimal
représentant la durée de la vibration en secondes limitée à 15 secondes.
La commande GET n'a qu'une syntaxe : GET ACCEL. Aucun paramètre n'est requis mais
le gestionnaire de costume doit retourner un octet écrit en décimal représentant la position du joueur.
Les positions sont notées de 0 à 4 dans l'ordre où elles sont décrites dans la section 1.2.
1.7 Les requêtes administrateur
L'administrateur lance le client d'administration à chaque fois qu'il veut effectuer
une requête (la requête est fournie en argument). L'administrateur peut envoyer plusieurs
requêtes simultanément, c'est à dire exécuter plusieurs instances du client d'administration
simultanément. Les requêtes à disposition de l'administrateur sont les suivantes :
-
aide :
- obtenir la liste des requêtes possibles ;
- score :
- indique si un jeu est en cours ou si un jeu est terminé, liste les
joueurs avec leur état (en lice, éliminé ou à défaut connecté) ;
- mode {blanc|noir} :
- préciser le mode du joueur ;
- connecter <pseudo>:[<port>@]<nom machine> :
-
se connecter à un serveur distant, lancer le processus de gestion de costume
si la connexion est correctement établie ;
- deconnecter :
- déconnecter le gestionnaire de costume du serveur de jeu,
arrêter le processus léger ;
- demarrer :
- lancer un jeu avec les joueurs connectés ;
- arreter :
- arrêter le jeu en cours ;
- suivre :
- suivre les événements du jeu, c'est à dire que l'administrateur
attend et affiche les événements suivants envoyés par le serveur de jeu ; démarrage
du jeu, signalement de pré-étape, signalement de fin d'étape, liste des positions des
joueurs, liste des joueurs éliminés, liste des joueurs toujours en lice, fin du jeu ;
- stopper :
- arrêter l'application.
Tout dialogue entre le serveur de jeu et le client d'administration est réalisé par
des communications IPC (envoi et réception de messages). Pour permettre l'exécution
simultanée de plusieurs clients d'administration, il faut créer une file de messages
de réponse par client et envoyer le descripteur de cette file dans les requêtes. Pour
implanter la commande suivre, il est recommandé de définir, au niveau du serveur
de jeu, un tableau des clients d'administration en attente d'événements. Ainsi à chaque
nouveau événement à leur transmettre, il suffit de parcourir ce tableau et de les
contacter sur leur file de messages de réponse.
2 Organisation du travail
2.1 Généralités
Il est fortement conseillé de suivre les étapes proposées ci-après pour réaliser le travail.
2.2 Organisation modulaire
Le projet est constitué du programme Arduino, de 3 bibliothèques et des programmes
serveur de jeu et client d'administration. Chacune de ces entités est développée
dans un répertoire propre. On gère donc les six répertoires suivants :
-
Costume -
- pour les sources du programme chargé sur la plate-forme
Arduino Lilypad ;
- Communication -
- pour les sources de la bibliothèque contenant les fonctions
de gestion réseau et série libcom ;
- Threads -
- pour les sources de la bibliothèque contenant les fonctions
de gestion des threads libthrd ;
- IPC -
- pour les sources de la bibliothèque contenant les fonctions de
gestion des communications inter-processus libipc ;
- Jeu -
- pour l'application de gestion des costumes et du jeu ;
- Administration -
- pour le client d'administration.
Cette arborescence et quelques squelettes de fichiers sont disponibles sous forme
d'un fichier au format tar et compressé à l'URL
http://www.plil.net/~rex/Enseignement/Systeme/Tutorat.IMA4sc.Mouton/mouton.tgz.
Transférez ce fichier dans votre compte Polytech'Lille et décompressez-le avec
la commande tar xvzf mouton.tgz. Vous pouvez constater que très peu de code
est fourni concernant l'application de gestion des costumes et du jeu :
-
quelques fonctions d'affichage dans la bibliothèque Communication pour impulser
l'affichage de déverminage ;
- le code des fonctions d'ouverture et de fermeture du port série.
Cependant, le répertoire créé contient déjà un fichier Makefile global et des Makefile
annexes dans chaque sous-répertoire. Vous avez un exemple de Makefile permettant de générer
un exécutable et un exemple de Makefile permettant de construire une bibliothèque. Appuyez
vous sur ces deux exemples pour compléter les autres Makefile. Le projet doit pouvoir être
généré par la simple commande make exécutée dans le répertoire principal du projet. Il doit
être aussi possible de regénérer complètement le projet par la commande make clean all lancée
du même répertoire.
Vous constaterez aussi la présence d'un répertoire Tests contenant des scripts PHP qui vont
vous permettre de tester la conformité de votre code aux spécifications. Plus de détails à ce propos
vous sont donnés dans la suite de ce document. Enfin un dernier répertoire XBee contient un
utilitaire qui peut vous aider pour configurer vos modules XBee. Le code de cet utilitaire
est suffisament clair pour que vous puissez en comprendre le fonctionnement en le lisant.
On prévoira de pouvoir compiler les différents sources avec un drapeau DEBUG (option
-DDEBUG de gcc), permettant un affichage conditionnel d'informations de déverminage
des programmes.
2.3 Sockets et serveur TCP
Il s'agit dans cette étape de réaliser un serveur TCP basique à l'aide de
l'interface de programmation des sockets.
Ecrivez dans le module libcom.c (répertoire Communication), les deux
fonctions suivantes :
-
int initialisationServeur(short int); Cette fonction prend en
paramètre le port sur lequel il faut écouter et retourne la socket de
lecture. Il vous est demandé d'activer l'option de réutilisation
d'adresse sur la socket d'écoute ainsi que de désactiver les tampons
TCP.
- int boucleServeur(int, void (*)(int)); Cette fonction effectue
l'écoute sur la socket passée en premier argument et lors d'une
connexion, exécute la fonction passée en second argument. Cette
fonction passée en argument doit être une fonction qui prend une socket
en unique paramètre. Lors d'une connexion de client, la fonction
boucleServeur lance donc cette fonction avec la socket de dialogue
en paramètre.
Testez cette bibliothèque en écrivant un programme jeu.c (répertoire
Jeu) qui l'utilise et dont la fonction de traitement des connexions
(celle appelée par boucleServeur) effectue juste une écriture de message
dans la socket et clôt la connexion. Pour écrire, et plus tard lire, sur la
connexion utilisez les fonctions classiques comme fgets ou fprintf.
Pour y arriver vous devez transformer le descripteur de socket en une structure
de fichier par la primitive fdopen.
Ce programme peut prendre des arguments : -p <port> ou --port <port>
pour spécifier un numéro de port différent de celui par défaut (port
4000). Pour traiter les arguments, utilisez la fonction
getopt_long (voir la page de manuel correspondante). Si les arguments
sont incorrects, on doit afficher un message qui précise la syntaxe.
Pour plus de clarté, l'analyse des arguments et l'affichage de la syntaxe
seront écrits dans des fonctions séparées.
Modifiez la fonction de traitement des connexions afin qu'elle ne ferme la
connexion qu'après le traitement des commandes pseudo et mode.
Testez votre serveur avec plusieurs commandes nc simultanées. Conclusion ?
Maintenant que vous avez écrit un embryon de serveur TCP, passez au client TCP.
Ajoutez les fonctions nomVersAdresse et connexionServeur dans
libcom. Réalisez un exécutable temporaire utilisant ces deux fonctions
pour se connecter sur le serveur jeu en envoyant les commandes pseudo
et mode.
2.4 Un serveur de jeu à base de processus légers
Pour que votre serveur de jeu puisse traiter plusieurs connexions simultanément,
vous allez lancer un processus léger (thread) par connexion. Pour cela, implémentez
la fonction publique de libthrd (répertoire Threads) :
int lanceThread(void (*)(int),int);
Cette fonction doit avoir comme action de lancer un thread dans le mode
détaché. Ce thread doit exécuter la fonction passée en paramètre. La dite
fonction prenant elle même comme paramètre le second paramètre de
lanceThread. Il vous est conseillé d'utiliser une fonction intermédiaire
récupérant un pointeur vers une structure comprenant les deux paramètres de
lanceThread.
Testez votre fonction lanceThread en créant une nouvelle fonction dans
jeu.c qui appelle votre fonction de traitement des connexions
via lanceThread. Utilisez la nouvelle fonction comme paramètre de
boucleServeur. Pour plus de clarté, ce serait une bonne idée de déplacer
les fonctions de traitement de connexions du fichier jeu.c vers
le fichier gestionConnexions.c.
Vérifiez que votre serveur est maintenant capable de gérer plusieurs
connexions simultanément (avec votre exécutable client TCP ou avec l'utilitaire
nc).
La fonction lanceThread est utilisée dans 4 situations différentes. Tout
d'abord comme expliqué plus haut pour gérer les connexions TCP ; dans ce cas
l'entier passé à la fonction paramètre est un descripteur de socket de dialogue.
La fonction lanceThread est aussi utilisée pour démarrer le processus léger
de gestion de la file de message d'administration, le processus léger de gestion
du costume et le processus léger de gestion de jeu.
2.5 Structure de données du serveur de jeu
La structure de données du serveur de jeu doit principalement permettre de gérer
les joueurs qui se connectent.
Définissez cette structure de données dans le fichier entête jeu.h et
écrivez sa fonction d'initialisation.
La structure des clients d'administration est plus à sa place dans le fichier entête
gestionAdmin.h.
2.6 Analyse des opérations sur la structure de données
Analysez les opérations nécessaires sur les structures de données afin de
déterminer celles qui nécessitent l'utilisation de sémaphores.
Implantez dans votre bibliothèque libthrd les deux fonctions publiques :
void P(int) ;
void V(int) ;
Ces fonctions cachent totalement le fait que vous utilisez des verrous
d'exclusion mutelle pour threads POSIX. En particulier, les verrous sont
représentés par une constante.
En implantant dans le module algorithmeMouton.c les fonctions nécessaires
à la gestion du jeu et dans le module commandesAdmin.c les fonctions
nécessaires à la gestion du client d'administration vous prendrez soin
d'y ajouter les poses et levées de verrous nécessaires.
2.7 Le client d'administration
Il vous faut implanter le client d'administration et le processus léger chargé
de traiter les requêtes de l'administrateur dans le serveur.
Les commandes sont passées en argument au client d'administration (donc pas
d'interface textuelle, mais une analyse des arguments au moyen de
getopt_long). Ce processus communique avec le serveur au moyen
de deux files de messages : une pour les commandes et une pour les réponses.
La file des commandes est créée par le serveur à l'initialisation. Un thread,
lancé par le serveur est chargé de scruter en permanence cette file. La file
des réponses est créée par le client d'administration.
La bibliothèque libipc contiendra toutes les fonctions permettant de
cacher le fait que la communication est implantée par IPC (selon le même
principe que celui utilisé pour les sémaphores).
Le processus léger de gestion des requêtes est lancé par la fonction lanceThread
de la bibliothèque libthrd. Cette fois le second paramètre n'est pas utile,
vous pouvez passer une valeur quelconque.
Lorsque que le serveur se termine, tous les clients d'administration
en mode de scrutation ou de reniflage doivent se terminer aussi.
2.8 Le serveur de jeu
Vous pouvez maintenant vous attaquer à l'écriture du code concernant le
serveur de jeu. Il vous est conseillé d'utiliser les fichiers suivants :
-
jeu.h : fichier des structures de données ;
- jeu.c : programme principal et analyse des arguments ;
- gestionAdmin.c : fonction de lecture de la file de message et
d'analyse des requêtes du client d'administration ;
- commandesAdmin.c : une fonction de traitement pour chaque type
de requête du client d'administration ;
- gestionConnexions.c : la fonction de gestion des demandes de
connexion des gestionnaire de costumes ;
- algorithmeMouton.c : implantation de l'algorithme de gestion
du jeu du mouton (gestion du costume et du jeu).
Détaillons le comportement du serveur de jeu :
-
lors de la connexion d'un gestionnaire de costume, le serveur de jeu
le signale en lui demandant d'allumer la LED du costume, initialement
éteinte, et de la passer à la couleur bleue ;
- en début d'étape, le serveur de jeu signale les gestionnaires de costumes
connectés, en demandant de passer leur LED à la couleur verte ;
- après un court instant, le serveur signale aux joueurs de se mettre en
position en leur demandant d'émettre un mi sur les buzzers et en
demandant aux vibreurs une activation courte ;
- quelques secondes après, les positions des joueurs sont relevées et les
gestionnaires de costumes sont signalés pour émettre un sol
sur les buzzers et lancer une autre vibration courte ;
- l'état des joueurs est mis à jour, les costumes des éliminés sont signalés
pour passer les LED en rouge et pour générer une vibration longue, les
costumes des gagnants sont signalés pour passer les LED en blanc et pour
émettre un la ;
- le serveur de jeu relance une étape si nécessaire.
Comme le protocole sur les connexions TCP est au format ASCII, le fonctionnement
du serveur de jeu peut être testé en lançant plusieurs nc, en lançant
manuellement la commande pseudo puis en répondant, toujours manuellement,
aux requêtes du serveur de jeu.
Pour réaliser la déconnexion d'un joueur, une solution consiste à ajouter un tube
(pipe) en variable globale. Le processus léger de gestion de costume se met alors
en écoute sur la connexion TCP vers le serveur de jeu et sur le tube (primitive
select). Si l'activité est détectée sur le tube, le processus léger se termine
sinon la commande du serveur de jeu est lue. L'arrêt d'un processus léger de gestion de
costume peut ainsi s'obtenir par simple écriture dans son tube.
Faites de même pour permettre l'arrêt du jeu en cours. La primitive select
est alors utilisée pour écouter sur un second tube tout en lançant le minuteur
utilisé en fin d'étape.
2.9 Tester votre application
Le répertoire Tests présent dans l'archive fournie contient deux programmes vous
permettant de tester votre application. Pour tester la gestion du costume et votre
programme Lilypad, lancez le script serveur.php. Connectez ensuite votre joueur
sur ce serveur (il écoute sur le port 4444). Vous verrez alors que le serveur lance
des commandes sur votre costume pour tester la bonne réceptions des commandes. Quand
vous aurez implanté votre algorithme de jeu, vous pourrez le tester avec le script
clients.php. Il suffit de le lancer après avoir lancé votre application. Le
script simule des clients se connectant sur votre serveur. Si vous démarrez le jeu,
le script affichera toutes les commandes reçues par les clients avec leurs réponses.
This document was translated from LATEX by
HEVEA.