POD -- 98/99
Les Robots


1  Graphisme

La gestion de l'interface graphique du projet est construite sur un modèle client/serveur : un serveur graphique exécute les requêtes graphiques que lui transmettent les clients graphiques.
Un premier serveur graphique graph_server est défini il crée l'environnemet graphique qui traite les requêtes de façon synchrones : simple boucle de réception et de traitement des requêtes.
Ce serveur synchrone est étendu par un serveur asynchrone, enhanced_graph_server qui comporte deux boucles : une boucle de réception et une boucle d'émission. La boucle de réception stocke les requêtes dans une file d'attente; la boucle d'émission fait appel aux méthode du serveur synchrone pour traiter la requête et tête de liste. Chacune de ces boucle est un processus indépendant (c'est un petit modéle producteur/consommateur)



1.1  Les requêtes graphiques

Les requêtes graphiques sont toutes héritières de la classe virtuelle
class virtual graph_query = object method virtual draw : unit end
Chacune des requêtes agit sur un environnement (une fenêtre) graphique suppossé(e) présent(e) en implantant la méthode draw

1exLe module Graphic_server fourni les requêtes

class gq_clear : object method draw : unit end| 
colore la totalité de la fenêtre graphique avec la couleur de fond courrante.

class gq_line : Graphics.color -> int * int -> int * int -> 
 object method draw : unit end
La méthode draw d'un objet new gq_line c (x1,y1) (x2,y2) trace une ligne de couleur c de (x1,y1) à (x2,y2)

class gq_disk : Graphics.color -> int * int -> int -> 
 object method draw : unit end
La méthode draw d'un objet new gq_disk c (x,y) r trace un disque de couleur c, de centre (x,y) et de rayon r

class gq_kill : unit -> object method draw : unit end
La méthode draw d'un objet new gq_kill () lève l'exception Failure("gq_kill#draw : not an usable method") Cet objet sera, en fait utilisé pour mettre fin à une session graphique (voir : champs kill_signal de la classe graph_server)

1exIl vous est loisible de définir de nouvelle requêtes afin d'enrichir les figures affichables.



1.2  Le serveur synchrone

Le serveur synchrone est un objet de la classe
class graph_server :
  int ->
  int ->
  object
    val mutable flag_alive : bool
    val kill_signal : graph_query
    val sem : Mutex.t
    val sync_channel : graph_query Event.channel
    method alive : bool
    method exit : unit
    method get_channel : graph_query Event.channel
    method private treat_query : graph_query -> unit
  end
Lors de la création d'un objet de cette classe, sont crées :
  1. un canal de communication (sync_channel)
  2. une requête particulière (kill_signal)
  3. une fenêtre graphique aux dimension données par les deux paramètres de la classe
  4. un processus léger exécutant la boucle réception/traitement
L'intégrité de l'exécution des requêtes est assurée par le sémaphore sem géré par la méthode privée treat_query



1.3  Le client synchrone

Un client synchrone est un objet de la classe
class graph_client :
  #graph_server -> 
  object method send : graph_query -> unit end
qui se passe de commentaire



1.4  le serveur assynchrone

Un serveur assynchrone est un objet de la classe
class enhanced_graph_server :
  int ->
  int ->
  object
    val async_channel : graph_query Event.channel
    val async_sem : Mutex.t
    val async_signal : Condition.t
    val mutable flag_alive : bool
    val kill_signal : graph_query
    val mutable query_fifo : graph_query list
    val sem : Mutex.t
    val sync_channel : graph_query Event.channel
    method alive : bool
    method exit : unit
    method get_async_channel : graph_query Event.channel
    method get_channel : graph_query Event.channel
    method private treat_query : graph_query -> unit
  end
qui hérite de graph_server. Lors de la création d'un objet, sont crées (outre ce qui relève de la classe mère) :
  1. un canal de réception des requêtes (async_channel)
  2. un sémaphore (async_sem) assurant l'exclusion mutuelle entre le traitement en réception et le traitement en réémission
  3. un signal (async_signa) servant au réveil de la boucle de traitement
  4. une file d'attente (query_fifo) pour les requêtes
  5. un processus léger exécutant la boucle d'attente et de stockage des requêtes
  6. un second processus léger chargé de la boucle de retransmission des requêtes
La boucle de réception exécute la séquence La boucle de retransmission exécute la séquence

1.5  Le client assynchrone

Un client assynchrone est un objet de la classe
class enhanced_graph_client :
  #enhanced_graph_server ->
  object
    method async_send : graph_query -> unit
    method send : graph_query -> unit
  end
qui se passe également de commentaire.



2  Des robots et des mondes

Le monde et ses robots vivent, a priori, dans un rapport d'interaction complèxe qu'il faut simplifier. Nous ferons le choix suivant : les robots sont essentiellement passifs et le monde est moteur.

1exNous vous proposons d'organiser la définiton des mondes et des robots de la façon suivante :
  1. un couple de classes (virtuelles) mondeV/robotV fournissant les méthodes de bases attendues des mondes et des robots
  2. deux classes (virtuelles) mondeV_display et robotV_display fournissant chacune une méthode d'affichage
  3. une classe (virtuelle) monde héritant à la fois de mondeV et mondeV_display et une classe robot héritant à la fois de robotV et robotV_display

2.1  Les bases

Les classes mondeV et robotV seront toutes deux paramétriques ce qui permettra de résoudre le problème sous-classe/sous-type vu en TD à propos des aventures de Ghandi au MacDo.

Le monde
Le monde est un ensemble de cases accessibles par leur coordonnées (un couple d'entier) il contient un ensemble de robots situés chacun sur une case différente appartenant au monde.

1exVous déclarez donc la classe
class virtual ['a] mondeV hi li =
 object
 
  constraint 'a = <get_pos:int*int; set_pos:int*int -> unit; .. >
  .
  .
  .
 end
hi et li sont les dimensions du monde (hauteur et largeur) Le paramètre 'a deviendra #robot

1exLa classe mondeV comportera trois champs (variable d'instance) :

val h : int contenant la hauteur du monde;

val l : int contenant la largeur du monde;

val mutable roblist : 'a list qui contiendra la liste des robots.

1exElle fournira cinq méthode de classe :

method add : 'a -> unit permettant d'ajouter un robot à la liste;

method free_places : int * int -> (int * int) list donnant la liste des places libres autour d'une case donnée par l'argument de la méthode;

method get_dim : int * int renvoie les dimensions du monde;

method get_roblist : 'a list renvoie la liste des robots présents;

method is_free : int * int -> bool indiquant si la case donnée par l'argument est libre ou non;

1exEnfin, elle déclarera deux méthodes virtuelles :

method virtual is_legal : int * int -> bool indiquant si oui ou non un couple d'entier désigne une case du monde

method virtual normalize : int * int -> int * int ramenant un couple d'entiers à des coordonnées licites du monde (l'implémentation dépendra de la topologie du monde et du bon vouloir du programmeur)

Les robots
Un robot est essentiellement caractérisé par sa position et un vecteur vitesse indiquant son déplacement. D'une position (x,y) avec la vitesse (vx,vy) on arrive à la case (x+vx, y+vy)

1exVous déclarerez la classe virtuelle
class virtual ['a] robotV (xi,yi) (vxi, vyi) = 
  object

   constraint 'a = <get_roblist: 'a #robotV list ; ..>
   .
   .
   .
  end
(xi,yi) et (vxi, vyi) donnent, respectivement, la position et la vitesse initiales du robot. La paramètre 'a deviendra un monde.

1exLa classe robotV contient les quatre champs :

val mutable vx : int vitesse en x;

val mutable vy : int vitesse en y;

val mutable x : int position en x;

val mutable y : int position en y.

1exElle fournit les quatre méthodes usuelles

method get_pos : int * int

method get_speed : int * int

method set_pos : int * int -> unit

method set_speed : int * int -> unit

1exElle fournit enfin la méthode virtuelle

method virtual next_position : 'a -> int * int qui, étant donné un monde (le paramètre 'a) calcule une prochaine position (de son implémentation dépendra le comportement des divers robots : immobile, à mouvement aléatoire, à mouvement déterministe, etc ...)



2.2  Mondes et robots affichables

Les deux (virtuelles) classes que nous proposons ici sont essentiellement chargées de fournir une méthode d'affichage. Ce sont encore des classes paramétrées pour contourner le problème évoqué plus haut.

Le monde
Vous déclarez la classe
class virtual ['a] robotV_display :
  object
    method virtual display : unit
  end
qui se passe de commentaire.

Les robots
Vous déclarez la classe
class virtual ['a] robotV_display :
  object
    method virtual display : unit
  end
qui se passe tout autant de commentaire.

Commentaire
Pour obtenir un mode d'affichage particulier (texte ou graphique), on procèdera par héritage en implémentant la méthode display Les autres méthode resteront, à ce stade virtuelles.



2.3  Les classes finales

Les classes finales sont celles qui permettent des créer des mondes et des robots aux caractéristiques (ie : méthodes) complètement définies.

Le(s) monde(s)
Un monde final s'obtient en héritant à la fois d'un monde de base (mondeV) et d'un monde affichable (mondeV_display) et en implémentant les méthodes restées virtuelles. On rajoutera également une méthode

fiat_lux : unit -> unit chargée de donner vie aux robots. On pourra soit choisir une boucle parcourant la liste des robots et les activant (par appel aux méthodes idoines) tour à tour, soit créer un processus léger pour chaque robot : il faudra alors songer aux problème de synchronisation et utilisant, par exemple, un mécanisme de sémaphores.

Les robots
Un robot final s'obtient par héritage multiple de robotV et robotV_display en définissant sa stratégie de mouvement (next_position) et son mode d'affichage (display)



2.4  Résumé des épisodes précédents




Page initiale Maison Page précédente POD
This document was translated from LATEX by HEVEA.