TP 7 - Communications

Protocoles de communication sous MadKit

proposé par Jacques Ferber - Dernières modifs avril 2009

 

Vous trouverez ici l'API de la classe AbstractAgent, celle de la classe Agent.

1. Les ACLMessages

MadKit comprend l'ensemble des messages ACL dont l'API est donnée ici.

On utilisera la classe de message appelée ACLMessage. Il faudra en particulier penser à mettre:
import madkit.messages.ACLMessage;

au début de vos fichiers (mais Eclipse peut le faire pour vous... ).

Note: on peut créer un message soit en lui donnant son ID, par exemple en faisant

            new ACLMessage(ACLMessage.REQUEST,"le contenu du message") 

ce qui assure que l'on a bien mis la bonne valeur dans le champ du performatif, soit en mettant directement la chaîne de caractère du performatif:

            new ACLMessage("REQUEST","le contenu du message") 

On peut récupérer le performatif par la commande String getPerformatif() et le contenu du message par getContent(). Comme pour les autres messages, getSender() retourne l'émetteur du message.

2. FIPA request protocole

Nous avons donné, dans le cours, la définition du 'FIPA request protocol'. Vous trouverez sa définition dans les slides qui se trouvent ici (ces slides seront développées dans le cours ensuite, pour l'instant il s'agit surtout d'expérimenter la notion de protocole avec la classe ACLMessage). Nous allons essayer de le mettre en oeuvre sous MadKit.

Il faut d'abord créer un nouveau projet à l'aide du Designer.

Je vous propose de créer des agents de type "standard Java agent with GUI", qui permettent d'avoir facilement des interfaces graphiques si cela est nécessaire.

Créer un agent de classe Initiator qui aura la particularité de prendre le rôle 'initiator' dans le groupe RequestProtocol . De même créer un agent Participant qui prendra le rôle 'participant' dans le même groupe. On supposera que l'agent initiator demande une tâche quelconque au participant (votre imagination est requise... Vous pouvez prendre n'importe quoi comme tâche, par exemple demander un café, cela n'a pas d'importance). et l'agent participant pourra ou non accepter, et s'il accepte, il pourra ou non l'effectuer.

On suppose qu'il existe deux rôles différents: 'initiator' (qui correspond à l'agent qui lance la demande) et 'participant' qui correspond au rôle de l'agent qui reçoit la demande.

2.1. Technique "à la main"

1) Premier temps, faites en sorte que lorsqu'on appuie sur un bouton "Request" de l'initiator, celui ci envoie un message de type 'request' au participant.

2) Créer un type d'agent Participant avec 4 boutons pour répondre à l'un des cas possibles (refuser, accepter, échouer, réussir).

Le résultat est celui de la figure suivante. On voit que chaque agent prend bien le rôle correspondant.

Votre programme doit pouvoir fonctionner avec plusieurs initiateurs (qui ne font pas leur demande en même temps pour l'instant). En particulier, le participant doit répondre à l'initiateur qui lui a fait la requête (et non pas répondre à tout le monde).

2.2. Par programme

a) On veut maintenant que ce soit les agents, et surtout le participant qui agisse par lui-même. Mais supposons que le participant puisse recevoir plusieurs demandes différentes, éventuellement issues de différents initiateurs.

Pour cela, il faut pouvoir gérer des tâches en attente. Créer une classe Task dans le receveur qui contient l'émetteur du message (celui qui a demandé la tâche), le contenu de la tâche, le temps (en unité de temps) que la tâche est supposée mettre pour s'exécuter. Créer un mécanisme d'attente (n'oubliez pas de mettre des "pause" dans votre programme) qui boucle et qui gère les tâches en attente. Mettez un peu d'aléatoire pour que certaines tâches échouent. En, fait, dans ce programme, on simule l'exécution des tâches, alors que dans une réalisation "réelle", les tâches feraient effectivement quelque chose. Par exemple, si l'on demande à un agent de "faire du café", il ne le fera pas ici, et le temps mis pour faire le café sera simulé. Mais dans un système de domotique réalisé par des agents, un agent serait associé à la cafetière et ferait effectivement le café.

C'est en réalisant ce type de programme que vous pouvez comprendre le fait que ces protocoles ne sont pas simplement des échanges de messages, mais qu'ils suppossent une bonne gestion des états des agents et la manière d'y répondre.

2.3. Gestion des états des communications par l'initiateur

Supposons maintenant que les initiateurs n'arrêtent pas de faire des requêtes à tout un ensemble de participants. Dans ce cas, un initiateur ne sait pas quand il recevra la réponse des participants, et donc quand ils vont accepter et surtout quand la tâche sera terminée. Ils vont donc devoir gérer l'état du protocole.

Créer une classe communication qui contient l'état du protocole (comme on l'a vu en cours, le request-protocole contient les états attente-acceptation et attente-résultat) ainsi qu'un identifiant correspondant à l'ensemble de la communication. Cet identifiant est unique pendant tout le déroulement du protocol, et cela permet à l'initiateur de savoir, quand il reçoit une réponse, à quelle communication cette réponse est associée.

Lorsque l'initiateur initie un protocole, il crée une instance de la classe avec les informations nécessaires (identifiant, premier état d'attente), et il place cette communication dans une liste de communications en attente. Lorsqu'il reçoit un message, il retrouve la communication correspondante par son identifiant, et il fait ce que le protocole lui dit de faire.

Testez le système avec quelques initiateurs et plusieurs participants, et faites en sorte que les réponses arrivent dans un ordre quelconque, bien différent des demandes. Vous verrez alors l'intérêt d'avoir bien géré les communications.