. . .
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 ( )
{
Dans le document
Contributions à la dissémination d'informations haptiques dans un environnement multimodal
(Page 156-159)