• Aucun résultat trouvé

. . .

sendMessage (”IN MSE : pos = ( 5 , 2 0 ) ; ”) ;

. . .

}

Figure 2– Exemple de message envoy´e par un agent

Cr´eation de binding.

Chaque agent poss`ede aussi une m´ethode bindMessageafin de cr´eer un binding entre une

ex-pression r´eguli`ere et une de ses m´ethodes. L’agent B de notre exemple souhaite ˆetre inform´e des

changements de coordonn´ees dans la souris. Le code correspondant est visible sur la figure 3. Il

cr´ee donc un binding pour recevoir tous les messages respectant la syntaxe convenue pour les

coor-donn´ees de la souris. La notation des expressions r´eguli`eres utilis´ee est similaire `a la syntaxe utilis´ee

par la plupart des syst`emes. Les parenth`eses sont utilis´ees afin de d´efinir les morceaux de chaˆıne

donn´ees en param`etre au callback1. La syntaxe du callback est fixe, et comprend trois param`etres :

– appcorrespond au bus sur lequel est cr´e´e le binding.

– argcest le nombre de param`etres r´ecup´er´ees par l’expression r´eguli`ere.

– argvest un tableau contenant les param`etres r´ecup´er´es par l’expression r´eguli`ere.

Dans Ivy, les messages peuvent ˆetre envoy´es par une application, et les callbacks peuvent ˆetre cr´e´es

sur une autre machine. Ceci peut sembler ˆetre un avantage dans notre cas et ainsi faciliter la cr´eation

d’applications multimodales distribu´ees. Cependant ce syst`eme cr´ee plusieurs sockets par binding.

Il est donc conc¸u pour n’utiliser que peu de bindings et servir de canal de communication entre

plusieurs programmes. Or dans notre cas il peut y avoir beaucoup de bindings : chaque agent ayant

besoin des coordonn´ees d’un p´eriph´erique doit cr´eer un binding, chaque agent utilisant des donn´ees

d’un autre agent aussi, etc. Une application bas´ee sur notre architecture utilise rapidement plusieurs

dizaines de bindings, ce qui donne rapidement pr`es d’une centaine de sockets. La consommation

de ressources li´ees `a la communication devient tr`es importante et p´enalise notre syst`eme. De plus

nous avons besoin d’un temps de r´eponse tr`es rapide pour les p´eriph´eriques haptiques comme nous

allons le voir plus loin. C’est pour cette raison que nous avons r´e-impl´ement´e la partie d’Ivy qui

nous int´eresse, sans utiliser de sockets pour optimiser la vitesse. Au passage nous avons pu v´erifier

1

Si la chaˆıne contient une parenth`ese il faut l’´echapper avec deux antislash : un pour le compilateur C++, et un pour

l’interpr´eteur d’expressions r´eguli`eres

AgentB : : AgentB ( )

{

. . .

bindMessage (” ˆ IN MSE : pos =\ \( ( .∗) , ( .∗)\ \) ; ”, \

BUS CALLBACK OF ( AgentB , monCallback ) ) ;

. . .

}

void AgentB : : monCallback ( MicoleBus ∗app , i n t argc , c o n s t char ∗∗argv )

{

// argc == 2 ;

// argv [ 0 ] == ”5”

// argv [ 1 ] == ”20”

}

Figure 3– Exemple de binding cr´e´e par un agent

l’abstraction des applications bas´ees sur l’architecture, car en changeant le bus les applications n’ont

pas eu besoin d’ˆetre modifi´ees.

2.1.3. Les agents thread´es

Deux cat´egories d’agents ont ´et´e conc¸us : les agents thread´es et les agents non-thread´es. Les agents

non-thread´es sont les agents de base qui ont un comportement passif : ils n’agissent que lorsque leurs

callbacks sont appel´es. `A l’inverse les agents thread´es poss`edent leur propre boucle d’ex´ecution. Ces

agents peuvent initier des comportements en envoyant des messages sur le bus `a partir de leur boucle

d’ex´ecution. Typiquement ces agents sont utiles pour les agents qui interrogent les p´eriph´eriques afin

d’obtenir leurs coordonn´ees. Le code de la thread est ´ecrit dans la m´ethoderunque le d´eveloppeur

doit surcharger. La figure 4 montre celle de l’agent qui envoie les coordonn´ees de la souris.

La thread boucle tant que l’agent est marqu´e comme actif. Dans cette boucle l’agent r´ecup`ere

les coordonn´ees de la souris, construit un message avec ces coordonn´ees en respectant la syntaxe

pr´ed´efinie, et envoie le message sur le bus.

2.1.4. Le r´eseau

Le fait d’avoir d´evelopp´e notre propre syst`eme de bus bas´e sur les m´ecanismes d’Ivy mais sans

socket nous a priv´e de la possibilit´e de connecter plusieurs applications voire plusieurs machines

au bus. Pour compenser cette perte nous avons cr´e´e des agents permettant `a plusieurs bus de se

connecter et communiquer entre eux. Ainsi en cr´eant un agentNetworkAgenton pr´ecise l’adresse

du bus qui joue le r ˆole de serveur. Si aucun serveur n’est pr´esent, le bus courant devient le serveur et

d’autres bus peuvent se connecter dessus. Chaque bus cr´e´e est donc un conteneur d’agents. Ensuite

pour envoyer un message aux autres bus il suffit de le pr´efixer par la chaˆıne«Net: ».

void MouseSenderAgent : : run ( )

{

while( g e t S t a t e ( ) == ACTIVE )

{

Vec2f p = mouseMoveListener−>g e t ( ) ;

. . .

sendMessage (”IN MSE : pos =( ” + p . x + ” , ” + p . y + ” ) ; ”) ;

. . .

}

}

Figure 4– Exemple d’agent thread´e

`

A titre d’illustration compl´ementaire au m´ecanisme du bus et d’explication du fonctionnement

du r´eseau, la figure 5 montre les m´ecanismes principaux du fonctionnement de l’interface r´eseau.

Au d´ebut de la cr´eation de cet agent, on initialise la socket. La m´ethodeinitSocket tente de se

connecter `a l’adressedomainesur le portport. Si la connexion ´echoue c’est qu’il n’y a aucun serveur

`a cette adresse ou qu’il n’est pas joignable. Un serveur est donc cr´e´e en local. Ensuite on cr´ee un

binding sur les messages commenc¸ant par « Net: », le reste ´etant captur´e afin d’ˆetre pass´e en

param`etre au callback. Ce callback est tr`es simple : il appelle une m´ethode qui envoie la chaˆıne

captur´ee sur la socket initialis´ee ci-dessus. Enfin la boucle de la thread de l’agent est lanc´ee. Cette

boucle se contente de lire de mani`ere bloquante sur la socket. Quand un message est rec¸u, il est

envoy´e sur le bus.

2.2. Le framework multimodal

L’int´erˆet de cette architecture consiste non seulement en sa conception, mais aussi en la collection

d’agents facilitant le d´eveloppement d’applications multimodales. En effet sans ce genre

d’archi-tecture il faut utiliser autant d’API et de pilotes que de p´eriph´eriques `a utiliser. Cette archid’archi-tecture

impl´emente des agents utilisant chacun le driver ou l’API appropri´e pour communiquer avec le

p´eriph´erique. De plus nous avons int´egr´e des techniques d’interaction telles que celles que nous

avons d´evelopp´ees dans les chapitres II et III. Nous avons aussi inclus des techniques de guidage

pour des p´eriph´eriques tels que le PHANToM. Enfin la m´ethode utilis´ee pour g´erer les p´eriph´eriques

d’entr´ee et de sortie d´ecrite ci-dessous peut ˆetre utilis´ee pour impl´ementer d’autres agents g´erant

d’autres p´eriph´eriques.

Nous avons d´efini une convention de conception pour les agents d’entr´ee et les agents de sortie.

Cette convention permet d’uniformiser `a la fois les agents n´ecessaires pour g´erer un p´eriph´erique

d’entr´ee ou de sortie, et la syntaxe des messages permettant de communiquer avec eux. Cette

con-vention est r´esum´e sur le sch´ema de la figure 6.

NetworkAgent : : NetworkAgent (char ∗ domaine , i n t p o r t ) : . . .

{

i n i t S o c k e t ( domaine , p o r t ) ;

bindMessage (” ˆ Net : ( .∗) $”, \

BUS CALLBACK OF ( NetworkAgent , n e t C a l l b a c k ) ) ;

s t a r t ( ) ;

}

void NetworkAgent : : n e t C a l l b a c k ( MicoleBus ∗app ,

i n t argc , c o n s t char ∗∗argv )

{

sendOnNetwork ( s t r i n g ( argv [ 0 ] ) ) ;

}

void NetworkAgent : : run ( )

{