Most of the messages received from the URBI server are the results of a
previously sent command. The mechanism of URBI tags enables to link a message
to its reply: with each command is associated a tag, and this tag is repeated
in the reply message. The UClient class handles the
reception of those messages in the independant thread created by the
constructor, parses them and fills a UMessage structure. Callback functions
with an associated tag can be registered with the method
registerCallback: each time a message with this tag is
sent by the server, the callback function will be called with a UMessage
structure as a parameter. The two basic forms of
registerCallback are:
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)
The first parameter is always a pointer to the function to call. callbackData is a pointer that will be given back to the callback function each time it is called. The callback function must return URBI_CONTINUE, or URBI_REMOVE, in which case the function will be unregistered.
A few examples:
UCallbackAction onImage(const UMessage &msg) {
//Test sif omeone has used your tag or an error message has been received
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); //this function is provided by 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
}
The UMessage structure is capable of storing the informations contained in any kind of URBI message by using a "type" field and an UValue (union of type-dependant structures). These two structures are defined as follows:
class UMessage
{
public:
/// Connection from which originated the message.
UAbstractClient &client;
/// Server-side timestamp.
int timestamp;
/// Associated tag.
std::string tag;
UMessageType type;
urbi::UValue *value;
std::string message;
/// Raw message without the binary data.
std::string rawMessage;
};
class UValue
{
public:
UDataType type;
ufloat val; // value if of type DATA_DOUBLE
union
{
std::string *stringValue; // value if of type DATA_STRING
UBinary *binary; // value if of type DATA_BINARY
UList *list; // value if of type DATA_LIST
UObjectStruct *object; // value if of type DATA_OBJ
};
}
The type field UMessageType can be MESSAGE_SYSTEM, MESSAGE_ERROR or MESSAGE_DATA. If the type is MESSAGE_DATA, the message contains an UValue. The UValue itself contains an UDataType which can take the values: DATA_DOUBLE, DATA_STRING, DATA_BINARY, DATA_LIST, DATA_OBJECT, DATA_VOID. Depending of this field, the corresponding value in the union will be set. If the UValue is of the binary type, it contains an UBinary structure defined hereafter. The UBinaryType in the UBinary structure will give additional informations on the type of data (BINARY_NONE, BINARY_UNKNOWN, BINARY_IMAGE, BINARY_SOUND), and the appropriate sound or image structure will be filled.
class UBinary
{
public:
UBinaryType type;
union
{
struct
{
void *data; /// binary data
int size;
} common;
UImage image;
USound sound;
};
}
class USound {
public:
char *data; // pointer to sound data
int size; // total size in byte
int channels; // number of audio channels
int rate; // rate in Hertz
int sampleSize; // sample size in bit
USoundFormat soundFormat; // format of the sound data
// (SOUND_RAW, SOUND_WAV, SOUND_MP3...)
USoundSampleFormat sampleFormat; // sample format
};
class UImage {
public:
char *data; // pointer to image data
int size; // image size in byte
int width, height; // size of the image
UImageFormat imageFormat; // IMAGE_RGB, IMAGE_YCbCr, IMAGE_JPEG...
};
Template versions of registerCallback are also
defined. They allow to set callbacks on member functions, with from 0 to 4
custom parameters of any type (including pointers and references). The only
constraint on the function signature is that it must return a UCallbackAction,
and take a const UMessage& as its last parameter. A few examples:
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();
}