La plupart des messages reçus du serveur URBI sont le résultat d'une commande
précédement envoyée.
Le mécanisme des tags URBI permet de lier un message à sa réponse: à chaque
commande est associée un tag, et ce tag est répété dans le message de réponse.
Les class UClient gèrent la réception de ces messages
dans le thread indépendant créé par le constructeur, les analyses et rempli
une structure UMessage.
Les retours de fonction avec un tag associé peuvent être enregistrés avec la
méthode registerCallback: chaque fois qu'un message avec
ce tag est envoyé par le serveur, la fonction callback est appelée avec une
structure UMessage comme paramètre. Les deux formes de base de
registerCallback sont:
typedef UCallbackAction (*UCallback) (const UMessage &msg); typedef UCallbackAction (*UCustomCallback) (void * callbackData, const UMessage &msg); UCallbackID setCallback (UCallback cb, const char *tag) UCallbackID setCallback (UCustomCallback cb, void *callbackData, const char *tag)
le premier paramètre est toujours un pointeur vers la fonction à appeler. callbackData est un pointeur qui sera retourné à la fonction callback chaque fois quelle est appelée. La fonction callback doit retourner URBI_CONTINUE, ou URBI_REMOVE. Dans le cas URBI_REMOVE, la fonction n'est pas enregistrée.
Quelques exemples:
UCallbackAction onImage(const UMessage &msg) {
//Test si quelqu'un a utilisé votre tag ou si un message d'erreur a été reçu
if (msg.type != MESSAGE_DATA || ((UImage)msg).imageFormat == IMAGE_UNKNOWN)
return URBI_CONTINUE;
UImage img = (UImage)msg;
msg.client.printf("Image of size (%d,%d) received from server at %d\n",img.width, img.height, msg.timestamp);
unsigned char *image = new unsigned char[img.width*img.height*3];
int sz = img.width*img.height*3;
if (img.imageFormat == IMAGE_JPEG)
convertJPEGtoRGB((const byte *) img.data, img.size, (byte *) image, sz); //provided by liburbi
if (img.imageFormat == IMAGE_YCbCr)
convertYCrCbtoRGB((const byte *) img.data, img.size, (byte *) image); //provided by liburbi
myDisplayRGBImage(image, img.width, img.height);
delete image;
return URBI_CONTINUE;
}
UCallbackAction onSound(const UMessage &msg) {
if (msg.type != MESSAGE_DATA || USound(msg).soundFormat == SOUND_UNKNOWN)
return URBI_CONTINUE;
//convert the sound to a wav 16KHz 16bit.
USound snd;
snd.soundFormat = SOUND_WAV;
snd.rate = 16000;
snd.sampleSize = 16;
snd.sampleFormat = SAMPLE_SIGNED;
snd.channels = 0; //take the value from source
snd.data = 0;
snd.size = 0;
convert((USound)msg, snd); //Cette fonction est fournie par liburbi
myPlayWAV(snd.data, snd.size);
return URBI_CONTINUE;
}
UCallbackAction onJoint(const UMessage &msg) {
if (msg.type != MESSAGE_DATA || ((UValue)msg).type != DATA_DOUBLE)
return URBI_CONTINUE;
msg.client.printf("The joint value si %lf\n", UValue(msg).val);
return URBI_CONTINUE;
}
int main(int argc, const char * argv[]) {
UClient * cl = new UClient(argv[1]);
if (cl->error()) urbi::exit(1); //portability call explaned below
cl->setCallback(&onImage, "img");
cl->setCallback(&onSound, "snd");
cl->setCallback(&onJoint, "joint");
cl->send("img: camera.val;");
cl->send("loop snd: micro.val,");
cl->send("joint: headPan.val;");
urbi::execute(); //portability call explaned below
}
La structure UMessage est capable de stocker les informations contenues dans nimporte quel type de message URBI grâce à un attribut "type", et à une structure UValue (union de structures de types différents). Ces deux structures sont définies comme suit:
class UMessage
{
public:
/// Connection dont le message est originaire
UAbstractClient &client;
/// timestamp coté serveur.
int timestamp;
/// Tag associé.
std::string tag;
UMessageType type;
urbi::UValue *value;
std::string message;
/// Message brut sans les donné binaires
std::string rawMessage;
};
class UValue
{
public:
UDataType type;
ufloat val; // value if of type DATA_DOUBLE
union
{
std::string *stringValue; // value si de type DATA_STRING
UBinary *binary; // value si de type DATA_BINARY
UList *list; // value si de type DATA_LIST
UObjectStruct *object; // value si de type DATA_OBJ
};
}
Le champs UMessageType représentant le type peut avoir la valeur MESSAGE_SYSTEM, MESSAGE_ERROR ou MESSAGE_DATA. Si le type est MESSAGE_DATA, alors le message contient une UValue.
L'UValue contient elle meme un UDataType qui peut prendre les valeurs: DATA_DOUBLE, DATA_STRING, DATA_BINARY, DATA_LIST, DATA_OBJECT, DATA_VOID. En fonction de ce champs, les bonnes valeurs dans l'union seront assignés.
Si l'UValue est de type binaire, alors elle contient une structure UBinary défini ci après. Le champs UBinaryType dans la structure UBinary donne des informations additionnelles sur le type de la donnée (BINARY_NONE, BINARY_UNKNOWN, BINARY_IMAGE, BINARY_SOUND), et la structure appropriée (de son ou d'image) est remplie.
class UBinary
{
public:
UBinaryType type;
union
{
struct
{
void *data; /// donnée binaire
int size;
} common;
UImage image;
USound sound;
};
}
class USound {
public:
char *data; // pointeur vers la données de son
int size; // taille totale en octets
int channels; // nombre de channels audio
int rate; // fréquence en Hertz
int sampleSize; // taille du sample en bit
USoundFormat soundFormat; // format du son
// (SOUND_RAW, SOUND_WAV, SOUND_MP3...)
USoundSampleFormat sampleFormat; // format du sample
};
class UImage {
public:
char *data; // pointeur vers l'image
int size; // taille de l'image en octets
int width, height; // taille de l'image
UImageFormat imageFormat; // IMAGE_RGB, IMAGE_YCbCr, IMAGE_JPEG...
};
Des versions template de registerCallback sont aussi
définies. Elles permettent de définir des callbacks sur des fonctions membres,
avec 0 à 4 paramètres de tout types (incluant pointeurs et références). La
seule contrainte sur la signature de fonction est qu'elle doit renvoyer un
UCallbackAction, et prendre un const UMessage& comme dernier paramètre.
Quelques exemples:
class Test {
public:
UCallbackAction onJoint(int value, const UMessage &msg);
}:
UCallbackAction Test::onJoint(int value, const UMessage &msg) {
msg.client.printf("got a message at %d with tag %s, our int is %d\n",msg.timestamp, msg.tag, value);
return URBI_REMOVE; //unregister ourself
}
int main(int argc, const char * argv[]) {
Test *a = new Test();
UClient * cl= new UClient(argv[1]);
if (cl->error()) urbi::exit(1);
cl->setCallback(*a, &Test::onJoint, 12, "tag");
cl->send("tag: headPan.val;");
urbi::execute();
}