Chapter 3. Receiving

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
}

UMessage

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;
};

UValue

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.

UBinary

class UBinary
{
  public:
  UBinaryType  type;
  union
  {
    struct
    {
       void *data; /// binary data
       int  size;
    } common;
    UImage image;
    USound sound;
  };
}

USound

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
};

UImage

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();
}