Revision: 6913
          http://playerstage.svn.sourceforge.net/playerstage/?rev=6913&view=rev
Author:   thjc
Date:     2008-07-23 07:14:31 +0000 (Wed, 23 Jul 2008)

Log Message:
-----------
Merged 6886 and 6907 from 2-1
blackboard interface/driver and proxy fixes

Modified Paths:
--------------
    code/player/trunk/client_libs/libplayerc/bindings/python/playerc.i
    code/player/trunk/client_libs/libplayerc/dev_blackboard.c
    code/player/trunk/client_libs/libplayerc/playerc.h
    code/player/trunk/libplayercore/interfaces/064_blackboard.def
    code/player/trunk/server/drivers/blackboard/localbb/localbb.cpp

Property Changed:
----------------
    code/player/trunk/


Property changes on: code/player/trunk
___________________________________________________________________
Modified: svn:mergeinfo
   - /code/player/branches/release-2-1-patches:6672-6673,6738,6834
   + /code/player/branches/release-2-1-patches:6672-6673,6738,6834,6886

Modified: code/player/trunk/client_libs/libplayerc/bindings/python/playerc.i
===================================================================
--- code/player/trunk/client_libs/libplayerc/bindings/python/playerc.i  
2008-07-23 06:40:18 UTC (rev 6912)
+++ code/player/trunk/client_libs/libplayerc/bindings/python/playerc.i  
2008-07-23 07:14:31 UTC (rev 6913)
@@ -28,7 +28,7 @@
     memset(buffer, 0, blength );
 
     //result is of type playerc_rfidtag_t
-    unsigned int j;
+    unsigned int j;    
     unsigned int guid_count=$1[i].guid_count;
 
     //copy the bytes into the buffer
@@ -416,432 +416,672 @@
 
 %extend playerc_blackboard
 {
-static void python_on_blackboard_event(playerc_blackboard_t *device, 
player_blackboard_entry_t entry)
+
+#define DICT_GROUPS_INDEX 0
+#define DICT_SUBSCRIPTION_DATA_INDEX 1
+#define LIST_EVENTS_INDEX 2
+#define BOOL_QUEUE_EVENTS_INDEX 3
+
+// Helper function to convert a c blackboard_entry_t into a python dictionary 
object.
+// Returns a new reference to a dictionary object.
+PyObject *__convert_blackboard_entry__(player_blackboard_entry_t *entry)
 {
-       char * str;
-       int i;
-       double d;
-       PyObject *data, *entry_dict, *group_dict, *groups, *list;
-       groups = (PyObject *)PyTuple_GetItem(device->py_private,0);
-       assert(groups);
+  PyObject *entry_dict, *data;
+  char* str;
+  int i;
+  double d;
+  int ok;
 
-       group_dict = (PyObject*) PyDict_GetItemString(groups, entry.group);
-       if (!group_dict)
-       {
-               return;
-       }
+  entry_dict = PyDict_New(); // New reference
+  assert (entry_dict);
+
+  assert(entry);
+  assert(entry->key);
+  assert(entry->key_count > 0);
+  assert(entry->group);
+  assert(entry->group_count > 0);
+
+  ok = PyDict_SetItemString(entry_dict, "key", 
PyString_FromString(entry->key)); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'key'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
+
+  ok = PyDict_SetItemString(entry_dict, "group", 
PyString_FromString(entry->group)); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'group'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
        
-       // find our key in the group, if it exists
-       entry_dict = (PyObject*) PyDict_GetItemString(group_dict, entry.key);
-       if (!entry_dict)
-       {
-               return;
-       }
+  ok = PyDict_SetItemString(entry_dict, "type", PyLong_FromLong(entry->type)); 
// Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'type'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
 
-       PyDict_SetItemString(entry_dict, "type", PyLong_FromLong(entry.type));
-       PyDict_SetItemString(entry_dict, "subtype", 
PyLong_FromLong(entry.subtype));
-       PyDict_SetItemString(entry_dict, "timestamp_sec", 
PyLong_FromLong(entry.timestamp_sec));
-       PyDict_SetItemString(entry_dict, "timestamp_usec", 
PyLong_FromLong(entry.timestamp_usec));
-       list = (PyObject*)PyTuple_GetItem(device->py_private,1);
-       assert(list);
-       data = NULL;
-    switch(entry.subtype)
+  ok = PyDict_SetItemString(entry_dict, "subtype", 
PyLong_FromLong(entry->subtype)); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'subtype'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
+
+  ok = PyDict_SetItemString(entry_dict, "timestamp_sec", 
PyLong_FromLong(entry->timestamp_sec)); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'timestamp_sec'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
+
+  ok = PyDict_SetItemString(entry_dict, "timestamp_usec", 
PyLong_FromLong(entry->timestamp_usec)); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'timestamp_usec'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
+
+  switch(entry->subtype)
+  {
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_NONE:
+      data = Py_None;
+      Py_INCREF(data);
+    break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_STRING:
+      assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_COMPLEX);
+      str = malloc(entry->data_count);
+      if (!str)
+      {
+           Py_XDECREF(entry_dict);
+           return PyErr_NoMemory();
+      }
+      memcpy(str, entry->data, entry->data_count);
+      data = PyString_FromString(str);
+    break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_INT:
+      assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
+      i = 0;
+      memcpy(&i, entry->data, entry->data_count);
+      data = PyLong_FromLong(i);
+    break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_DOUBLE:
+      assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
+      d = 0.0;
+      memcpy(&d, entry->data, entry->data_count);
+      data = PyFloat_FromDouble(d);
+    break;
+    default:
+      data = Py_None;
+      Py_INCREF(data);
+    break;
+  }
+
+  ok = PyDict_SetItemString(entry_dict, "data", data); // Steals reference
+  if (ok != 0)
+  {
+       PyErr_SetString(PyExc_RuntimeError, "Could not set dictionary value for 
'data'");
+       Py_XDECREF(entry_dict);
+       return NULL;
+  }
+  
+  return entry_dict;
+}
+
+// Helper function which sets a value in a nested dictionary. Equivalent of 
dict['group']['key'] = value.
+// Steals the entry reference.
+PyObject *__set_nested_dictionary_entry__(PyObject *dict, const char* key, 
const char* group, PyObject *entry)
+{
+  PyObject *group_dict;
+  int ok;
+  int create_dict = 0;
+
+  // Check we got valid arguments.
+  if (!PyDict_Check(dict))
+  {
+    PyErr_SetString(PyExc_TypeError, "Expected type 'dict' for first argument 
(dict)");
+    return NULL;
+  }
+
+  if (!entry)
+  {
+    PyErr_SetString(PyExc_TypeError, "Expected type non NULL for fourth 
argument (entry)");
+    return NULL;
+  }
+
+  if (key == NULL || strlen(key) == 0)
+  {
+    PyErr_SetString(PyExc_TypeError, "Expected type 'string' for second 
argument (key)");
+    return NULL;
+  }
+
+  if (group == NULL || strlen(group) == 0)
+  {
+    PyErr_SetString(PyExc_TypeError, "Expected type 'string' for second 
argument (group)");
+    return NULL;
+  }
+
+  // Get the dictionary for the entry's group. If it doesn't exist, create it.
+  group_dict = (PyObject*)PyDict_GetItemString(dict, group); // Borrowed 
reference
+  if (!group_dict)
+  {
+    create_dict = 1;
+    group_dict = PyDict_New(); // New reference    
+  }
+
+  ok = PyDict_SetItemString(group_dict, key, entry); // Steals reference
+  if (ok != 0)
+  {
+    PyErr_SetString(PyExc_RuntimeError, "Failed to set dictionary entry");
+    if (create_dict)
     {
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_NONE:
-               data = Py_None;
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_STRING:
-               assert(entry.type == PLAYERC_BLACKBOARD_DATA_TYPE_COMPLEX);
-                       str = malloc(entry.data_count);
-                       assert(str);
-                       memcpy(str, entry.data, entry.data_count);
-               data = PyString_FromString(str);
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_INT:
-               assert(entry.type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
-                       i = 0;
-                       memcpy(&i, entry.data, entry.data_count);
-               data = PyLong_FromLong(i);
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_DOUBLE:
-               assert(entry.type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
-                       d = 0.0;
-                       memcpy(&d, entry.data, entry.data_count);
-               data = PyFloat_FromDouble(d);
-       break;
-       default:
-               data = Py_None;
-       break;
+       Py_XDECREF(group_dict);
     }
+    return NULL;
+  }
 
-       assert(data != NULL);
-       PyDict_SetItemString(entry_dict, "data", data);
-       
-       if (PyLong_AsLong(PyTuple_GetItem(device->py_private,2)) > 0)
-       {
-               list = (PyObject*)PyTuple_GetItem(device->py_private,1);
-               assert(list);
-               PyList_Append(list, PyDict_Copy(entry_dict));
-       }
+  // If we created the dictionary, we need to put it in the parent dictionary.
+  if (create_dict == 1)
+  {
+    ok = PyDict_SetItemString(dict, group, group_dict); // Steals reference
+    if (ok != 0)
+    {
+       PyErr_SetString(PyExc_RuntimeError, "Failed to set dictionary entry");
+       Py_XDECREF(group_dict);
+       return NULL;
+    }
+  }
+
+  Py_RETURN_NONE;
 }
 
-// Will always return a list object. Returns a list of dictionary objects 
containing the event data.
-PyObject *get_events()
+// Helper function used to keep track to the number of subscriptions a 
group/key combination has
+int __increment_reference_count__(PyObject *dict, const char* key, const char* 
group, int inc)
 {
-  PyObject *list, *copy;
-  int i, j;
+  PyObject *group_dict, *entry;
+  int old_value, new_value;
+
+  // Get the dictionary for the entry's group. If it doesn't exist.
+  group_dict = (PyObject*)PyDict_GetItemString(dict, group); // Borrowed 
reference
+  if (!group_dict)
+  {
+    playerc_blackboard___set_nested_dictionary_entry__(self, dict, key, group, 
(PyObject*)PyInt_FromLong(inc)); // Steals reference to entry
+    return inc;
+  }
+
+  entry = PyDict_GetItemString(group_dict, key); // Borrowed reference
+  if (entry)
+  {
+       old_value = PyLong_AsLong(entry);
+  }
+  else
+  {
+       old_value = 0;
+  }
+  
+  new_value = old_value + inc;
+  if (new_value < 0)
+  {
+       new_value = 0;
+  }
+
+  playerc_blackboard___set_nested_dictionary_entry__(self, dict, key, group, 
PyInt_FromLong(new_value)); // Steals reference to entry
+
+  return new_value;
+}
+
+// Replacement for the existing on_blackboard_event method. The python 
implementation uses this instead.
+static void __python_on_blackboard_event__(playerc_blackboard_t *device, 
player_blackboard_entry_t entry)
+{
+  PyObject *entry_dict, *groups_dict, *list, *queue_events;
+
+  // Get the main groups dictionary
+  assert(device->py_private);
+  groups_dict = (PyObject*)PyTuple_GetItem(device->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+
+  // Get a dictionary of the entry
+  entry_dict = playerc_blackboard___convert_blackboard_entry__(device, 
&entry); // New reference
+  assert(entry_dict);
+  
+  // If we are queueing events, add them to the list
+  queue_events = PyTuple_GetItem(device->py_private, BOOL_QUEUE_EVENTS_INDEX); 
// Borrowed reference
+  assert(queue_events);
+  if (PyLong_AsLong(queue_events) != 0)
+  {
+    list = (PyObject*)PyTuple_GetItem(device->py_private, LIST_EVENTS_INDEX); 
// Borrowed reference
+    assert(list);
+    PyList_Append(list, entry_dict); // Increments refcount for us
+  }
+  
+  // Set the entry in the groups dictionary
+  playerc_blackboard___set_nested_dictionary_entry__(device, groups_dict, 
entry.key, entry.group, entry_dict); // Steals reference to entry_dict
+}
+
+// Returns a list of dictionary objects containing the entries.
+// Requires that set_queue_events is set to True for any events to be queued.
+// All events will be retrieved.
+// Will always return a list object.
+%feature("autodoc", "1");
+PyObject *GetEvents()
+{
+  PyObject *list, *copy, *item;
+  int i, j, ok;
+
   assert(self->py_private);
+  list = (PyObject*)PyTuple_GetItem(self->py_private, LIST_EVENTS_INDEX); // 
Borrowed reference
+  assert(list);
 
-  list = (PyObject*)PyTuple_GetItem(self->py_private,1);
-  copy = PyList_New(0);
   j = PyList_Size(list);
+  copy = PyList_New(j); // New reference
+  
   for (i = 0; i < j; i++)
   {
-    PyList_Append(copy, PyList_GetItem(list, 0));
+       item = PyList_GetItem(list, 0); // Borrowed reference
+       Py_INCREF(item);
+       assert(item);
+    ok = PyList_SetItem(copy, i, item); // Steals reference
+    if (ok != 0)
+    {
+       PyErr_SetString(PyExc_RuntimeError, "Failed to set list entry");
+       Py_XDECREF(copy);
+       Py_XDECREF(item);
+       return NULL;
+    }
     PySequence_DelItem(list,0);
   }
+
   return copy;
 }
 
-void set_queue_events(PyObject *boolean)
+// Returns a dictionary of the entry dictionary objects indexed by group.
+// Will only contain the latest values.
+// Will always return a dictionary object.
+PyObject *GetDict()
 {
+  PyObject *dict, *copy;
+
+  assert(self->py_private);
+  dict = (PyObject*)PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); // 
Borrowed reference
+  assert(dict);
+
+  copy = PyDict_Copy(dict); // New reference
+  return copy;
+}
+
+// Set whether events should be put into the list, to be retrieved by 
get_events_list().
+PyObject *SetQueueEvents(PyObject *boolean)
+{
+  int ok;
+
   if(!PyBool_Check(boolean))
   {
-    PyErr_SetString(PyExc_RuntimeError, "Expected a bool object.");
-    return;
+    PyErr_SetString(PyExc_TypeError, "Expected type 'bool'");
+    return NULL;
   }
-  if (boolean==Py_False)
+  if (boolean == Py_False)
   {
-    PyTuple_SetItem(self->py_private,2,PyLong_FromLong(0));
+    ok = PyTuple_SetItem((PyObject*)self->py_private, BOOL_QUEUE_EVENTS_INDEX, 
PyLong_FromLong(0)); // Steals reference
+    if (ok != 0)
+    {
+      PyErr_SetString(PyExc_RuntimeError, "Failed to set tuple entry");
+      return NULL;
+    }
   }
   else
   {
-    PyTuple_SetItem(self->py_private,2,PyLong_FromLong(1));
+    ok = PyTuple_SetItem((PyObject*)self->py_private, BOOL_QUEUE_EVENTS_INDEX, 
PyLong_FromLong(1)); // Steals reference
+    if (ok != 0)
+    {
+      PyErr_SetString(PyExc_RuntimeError, "Failed to set tuple entry");
+      return NULL;
+    }
   }
+
+  Py_RETURN_NONE;
 }
 
-void unsubscribe_from_key_py(const char *key, const char *group)
+PyObject *Subscribe(int access)
 {
-       long refcount;
-       PyObject *group_dict, *entry_dict, *groups;
+       int result;
+       result = playerc_blackboard_subscribe(self, access);
+       return PyInt_FromLong(result);
+}
 
-       groups = (PyObject *)PyTuple_GetItem(self->py_private,0);
-       assert(groups);
+PyObject *Unsubscribe()
+{
+       int result;
+       result = playerc_blackboard_unsubscribe(self);
+       return PyInt_FromLong(result);
+}
 
-       group_dict = (PyObject*) PyDict_GetItemString(groups, group);
-       if (!group_dict)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Group does not exist");
-               return;
-       }
-       
-       // find our key in the group, if it exists
-       entry_dict = (PyObject*) PyDict_GetItemString(group_dict, key);
-       if (!entry_dict)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Key does not exist");
-               return;
-       }
-       refcount = PyLong_AsLong(PyDict_GetItemString(entry_dict, 
"___refcount"));
-       if(refcount==0)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "No subscription to key to 
unsubscribe from.");
-               return;
-       }
-       if(refcount==1)
-       {
-               playerc_blackboard_unsubscribe_from_key(self, key, group);
-       }
-       PyDict_SetItemString(entry_dict, "___refcount", 
PyLong_FromLong(refcount-1));
+PyObject *SetString(const char* key, const char* group, const char* value)
+{
+  int result;
+  result = playerc_blackboard_set_string(self, key, group, value);
+  return PyInt_FromLong(result);
 }
 
-PyObject * subscribe_to_key_py(const char *key, const char *group)
+PyObject *SetInt(const char* key, const char* group, const int value)
 {
-       char *str;
-       int i, result, refcount;
-       double d;
-       PyObject *data, *entry_dict, *group_dict, *groups;
-       player_blackboard_entry_t *entry;
+  int result;
+  result = playerc_blackboard_set_int(self, key, group, value);
+  return PyInt_FromLong(result);
+}
 
-       groups = (PyObject *)PyTuple_GetItem(self->py_private,0);
-       assert(groups);
-       // grab our group dict, if it doesnt exist create a new one
-       group_dict = (PyObject*) PyDict_GetItemString(groups, group);
-       if (!group_dict)
-       {
-               group_dict = PyDict_New();
-               assert (group_dict);
-               result = PyDict_SetItemString(groups,group,group_dict);
-               if (result != 0)
-               {
-                       PyErr_SetString(PyExc_RuntimeError, "Failed to set 
group key");
-                       return Py_None;
-               }
-       }
-       // find our key in the group, if it exists, create it if not
-       entry_dict = (PyObject*) PyDict_GetItemString(group_dict, key);
-       if (!entry_dict)
-       {
-               entry_dict = PyDict_New();
-               assert (entry_dict);
-               PyDict_SetItemString(entry_dict, "key", 
PyString_FromString(key));
-               PyDict_SetItemString(entry_dict, "group", 
PyString_FromString(group));
-               PyDict_SetItemString(entry_dict, "___refcount", 
PyLong_FromLong(1));
-       }
-       else
-       {
-               // key already exists, so increment our ref count and return 
current value
-               refcount = PyLong_AsLong(PyDict_GetItemString(entry_dict, 
"___refcount"));
-               if(refcount>=1000)
-               {
-                       PyErr_SetString(PyExc_RuntimeError, "Reached over 1000 
subscriptions");
-               }
-               if(refcount!=0)
-               {
-                       PyDict_SetItemString(entry_dict, "___refcount", 
PyLong_FromLong(refcount+1));
-                       Py_INCREF(entry_dict);
-                       return entry_dict;
-               }
-       }
-       assert(self);
-       result = playerc_blackboard_subscribe_to_key(self, key, group, &entry);
-       if (result != 0)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Failed to subscribe to 
key");
-               return Py_None;
-       }
-       assert(entry);
-       assert(entry->key);
-       assert(entry->key_count > 0);
-       PyDict_SetItemString(entry_dict, "type", PyLong_FromLong(entry->type));
-       PyDict_SetItemString(entry_dict, "subtype", 
PyLong_FromLong(entry->subtype));
-       PyDict_SetItemString(entry_dict, "timestamp_sec", 
PyLong_FromLong(entry->timestamp_sec));
-       PyDict_SetItemString(entry_dict, "timestamp_usec", 
PyLong_FromLong(entry->timestamp_usec));
-    switch(entry->subtype)
+PyObject *SetDouble(const char* key, const char* group, const double value)
+{
+  int result;
+  result = playerc_blackboard_set_double(self, key, group, value);
+  return PyInt_FromLong(result);
+}
+
+int UnsubscribeFromKey(const char *key, const char *group)
+{
+  PyObject *groups_dict, *group_dict, *subscription_data;
+  int result, ref_count;
+
+  subscription_data = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_SUBSCRIPTION_DATA_INDEX); // Borrowed reference
+  assert(subscription_data);
+  ref_count = playerc_blackboard___increment_reference_count__(self, 
subscription_data, key, group, -1);
+  if (ref_count <= 0)
+  {
+    groups_dict = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+    assert(groups_dict);
+    group_dict = PyDict_GetItemString(groups_dict, group); // Borrowed 
reference
+    assert(group_dict);
+    PyDict_DelItemString(group_dict, key);
+  }
+
+  result = playerc_blackboard_unsubscribe_from_key(self, key, group);
+  return result;
+}
+
+PyObject *SubscribeToKey(const char *key, const char *group)
+{
+  int result;
+  PyObject *subscription_data, *groups_dict, *entry_dict;
+  player_blackboard_entry_t *entry;
+
+  result = playerc_blackboard_subscribe_to_key(self, key, group, &entry);
+  if (result != 0)
+  {
+    PyErr_SetString(PyExc_RuntimeError, "Failed to subscribe to key");
+    return NULL;
+  }
+
+  entry_dict = playerc_blackboard___convert_blackboard_entry__(self, entry); 
// New reference
+  assert(entry_dict);
+  groups_dict = (PyObject*)PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+
+  Py_INCREF(entry_dict);
+  playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, key, 
group, entry_dict); // Steals reference to entry_dict
+
+  subscription_data = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_SUBSCRIPTION_DATA_INDEX); // Borrowed reference
+  assert(subscription_data);
+
+  playerc_blackboard___increment_reference_count__(self, subscription_data, 
key, group, 1);
+
+  free(entry->key);
+  free(entry->group);
+  free(entry->data);
+  free(entry);
+
+  return entry_dict;
+}
+
+int UnsubscribeFromGroup(const char *group)
+{
+  int i, j, ref_count;
+  PyObject *groups_dict, *group_dict, *list, *tuple, *subscription_data, 
*items, *item;
+  const char* key;
+  int result;
+
+  result = playerc_blackboard_unsubscribe_from_group(self, group);
+
+  assert(self->py_private);
+  groups_dict = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+
+  group_dict = PyDict_GetItemString(groups_dict, group); // Borrowed reference
+  if (group_dict)
+  {
+    list = PyList_New(0); // New reference
+    subscription_data = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_SUBSCRIPTION_DATA_INDEX); // Borrowed reference
+    assert(subscription_data);
+
+    items = PyDict_Items(group_dict);
+    j = PyList_Size(items);
+    for (i = 0; i < j; i++)
     {
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_NONE:
-               data = Py_None;
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_STRING:
-               assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_COMPLEX);
-                       str = malloc(entry->data_count);
-                       assert(str);
-                       memcpy(str, entry->data, entry->data_count);
-               data = PyString_FromString(str);
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_INT:
-               assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
-                       i = 0;
-                       memcpy(&i, entry->data, entry->data_count);
-               data = PyLong_FromLong(i);
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_DOUBLE:
-               assert(entry->type == PLAYERC_BLACKBOARD_DATA_TYPE_SIMPLE);
-                       d = 0.0;
-                       memcpy(&d, entry->data, entry->data_count);
-               data = PyFloat_FromDouble(d);
-       break;
-       default:
-               data = Py_None;
-       break;
+      tuple = PyList_GetItem(items, 0); // Borrowed reference
+      assert(tuple);
+      item = PyTuple_GetItem(tuple, 0); // Borrowed reference
+      assert(item);
+      key = PyString_AsString(item);
+      ref_count = playerc_blackboard___increment_reference_count__(self, 
subscription_data, key, group, 0);
+      if (ref_count <= 0)
+      {  
+        PyList_Append(list, item); // Increments refcount for us
+      }
+    }
 
+    j = PyList_Size(list);
+    for (i =0; i < j; i++)
+    {
+      item = PyList_GetItem(list, i); // Borrowed reference
+      assert(item);
+      PyDict_DelItem(group_dict, item);
     }
-    PyDict_SetItemString(entry_dict, "data", data);
-       result = PyDict_SetItemString(group_dict,key,entry_dict);
-       if (result != 0)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Failed to create key 
dictionary entry");
-               return Py_None;
-       }
-    free(entry->key);
-    free(entry->group);
-    free(entry->data);
-    free(entry);
-    Py_INCREF(entry_dict);
-    return entry_dict;
+
+    Py_DECREF(list);
   }
 
-PyObject * subscribe_to_key(const char *key, const char *group)
+  return result;
+}
+
+int SubscribeToGroup(const char *group)
 {
-       return playerc_blackboard_subscribe_to_key_py( self, key, group);
+  int result;
+  result = playerc_blackboard_subscribe_to_group(self, group);
+  return result;
 }
 
-PyObject * get_value(const char *key, const char *group)
+PyObject *GetEntry(const char *key, const char *group)
 {
-       PyObject *group_dict, *entry_dict, *groups;
-       int refcount;
-       
-       groups = (PyObject *)PyTuple_GetItem(self->py_private,0);
-       assert(groups);
+  int result;
+  PyObject *entry_dict, *groups_dict;
+  player_blackboard_entry_t *entry;
+  
+  result = playerc_blackboard_get_entry(self, key, group, &entry);
+  if (result != 0)
+  {
+    PyErr_SetString(PyExc_RuntimeError, "Failed to get entry");
+    return NULL;
+  }
+  entry_dict = playerc_blackboard___convert_blackboard_entry__(self, entry); 
// New reference
+  
+  groups_dict = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+  assert(entry_dict);
+  Py_INCREF(entry_dict);
+  playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, key, 
group, entry_dict); // Steals reference to entry_dict
 
-       group_dict = (PyObject*) PyDict_GetItemString(groups, group);
-       if (!group_dict)
-               return playerc_blackboard_subscribe_to_key_py( self, key, 
group);
-       // find our key in the group, if it exists
-       entry_dict = (PyObject*) PyDict_GetItemString(group_dict, key);
-       if (!entry_dict)
-               return playerc_blackboard_subscribe_to_key_py( self, key, 
group);
-       refcount = PyLong_AsLong(PyDict_GetItemString(entry_dict, 
"___refcount"));
-       if(refcount==0)
-               return playerc_blackboard_subscribe_to_key_py( self, key, 
group);
-       Py_INCREF(entry_dict);
-       return entry_dict;
+  free(entry->key);
+  free(entry->group);
+  free(entry->data);
+  free(entry);
+
+  return entry_dict;
 }
+
+PyObject *SetEntryRaw(player_blackboard_entry_t *entry)
+{
+  int result;
+  PyObject *entry_dict, *groups_dict;
   
-  void set_entry_raw(player_blackboard_entry_t *entry)
+  entry_dict = playerc_blackboard___convert_blackboard_entry__(self, entry); 
// New reference
+  assert(entry_dict);
+  groups_dict = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+
+  playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, 
entry->key, entry->group, entry_dict); // Steals reference to entry_dict
+  result = playerc_blackboard_set_entry(self, entry);
+  return PyInt_FromLong(result);
+}
+
+PyObject *SetEntry(PyObject *dict)
+{
+  PyObject *key, *group, *type, *subtype, *timestamp_sec, *timestamp_usec, 
*data, *groups_dict;
+  char *str;
+  int i, result, length;
+  double d;
+
+  if (!PyDict_Check(dict))
   {
-    playerc_blackboard_set_entry(self,entry);
+    PyErr_SetString(PyExc_RuntimeError, "Expected a dictionary object.");
+    return NULL;
   }
 
-  PyObject *set_entry(PyObject *dict)
-  {
-       PyObject *key;
-       PyObject *group;
-       PyObject *type;
-       PyObject *subtype;
-       PyObject *timestamp_sec;
-       PyObject *timestamp_usec;
-       PyObject *data;
-       char *str;
-       int i, result;
-       double d;
-       int length;
+  player_blackboard_entry_t entry;
+  memset(&entry, 0, sizeof(entry));
 
-       if (!PyDict_Check(dict))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Expected a dictionary 
object.");
-               return PyLong_FromLong(-1);
-       }
+  key = PyDict_GetItem(dict, PyString_FromString("key")); // Borrowed reference
+  group = PyDict_GetItem(dict, PyString_FromString("group")); // Borrowed 
reference
+  type = PyDict_GetItem(dict, PyString_FromString("type")); // Borrowed 
reference
+  subtype = PyDict_GetItem(dict, PyString_FromString("subtype")); // Borrowed 
reference
+  timestamp_sec = PyDict_GetItem(dict, PyString_FromString("timestamp_sec")); 
// Borrowed reference
+  timestamp_usec = PyDict_GetItem(dict, 
PyString_FromString("timestamp_usec")); // Borrowed reference
+  data = PyDict_GetItem(dict, PyString_FromString("data")); // Borrowed 
reference
 
-    player_blackboard_entry_t entry;
-    memset(&entry, 0, sizeof(entry));
+  if (key == NULL || group == NULL || type == NULL || subtype == NULL || 
timestamp_sec == NULL || timestamp_usec == NULL || data == NULL)
+  {
+    PyErr_SetString(PyExc_RuntimeError, "Dictionary object missing keys. One 
or more of the following keys were not found: 'key', 'group', 'type', 
'subtype', 'timestamp_sec', 'timestamp_usec', 'data'.");
+    return NULL;
+  }
 
-    key = PyDict_GetItem(dict, PyString_FromString("key"));
-    group = PyDict_GetItem(dict, PyString_FromString("group"));
-       type = PyDict_GetItem(dict, PyString_FromString("type"));
-       subtype = PyDict_GetItem(dict, PyString_FromString("subtype"));
-       timestamp_sec = PyDict_GetItem(dict, 
PyString_FromString("timestamp_sec"));
-       timestamp_usec = PyDict_GetItem(dict, 
PyString_FromString("timestamp_usec"));
-       data = PyDict_GetItem(dict, PyString_FromString("data"));
+  if (!PyString_Check(key))
+  {
+    PyErr_SetString(PyExc_TypeError, "'key' should be a 'string' type.");
+    return NULL;
+  }
 
-       if (key == NULL || group == NULL || type == NULL || subtype == NULL || 
timestamp_sec == NULL || timestamp_usec == NULL || data == NULL)
-       {
-               PyErr_SetString(PyExc_RuntimeError, "Dictionary object missing 
keys.");
-               return PyLong_FromLong(-1);
-       }
+  if (!PyString_Check(group))
+  {
+    PyErr_SetString(PyExc_TypeError, "'group' should be a 'string' type.");
+    return NULL;
+  }
 
-       if (!PyString_Check(key))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "key should be a string 
type.");
-               return PyLong_FromLong(-1);
-       }
+  if (!PyLong_Check(type))
+  {
+    PyErr_SetString(PyExc_TypeError, "'type' should be a 'long' type.");
+    return NULL;
+  }
 
-       if (!PyString_Check(group))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "group should be a string 
type.");
-               return PyLong_FromLong(-1);
-       }
+  if (!PyLong_Check(subtype))
+  {
+    PyErr_SetString(PyExc_TypeError, "'subtype' should be a 'long' type.");
+    return PyLong_FromLong(-1);
+  }
 
-       if (!PyLong_Check(type))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "type should be a long 
type.");
-               return PyLong_FromLong(-1);
-       }
+  if (!PyLong_Check(timestamp_sec))
+  {
+    PyErr_SetString(PyExc_TypeError, "'timestamp_sec' should be a 'long' 
type.");
+    return NULL;
+  }
 
-       if (!PyLong_Check(subtype))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "subtype should be a long 
type.");
-               return PyLong_FromLong(-1);
-       }
+  if (!PyLong_Check(timestamp_usec))
+  {
+    PyErr_SetString(PyExc_TypeError, "'timestamp_usec' should be a 'long' 
type");
+    return NULL;
+  }
 
-       if (!PyLong_Check(timestamp_sec))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "timestamp_sec should be a 
long type.");
-               return PyLong_FromLong(-1);
-       }
+  entry.key = PyString_AsString(key);
+  entry.key_count = strlen(entry.key) + 1;
+  entry.group = PyString_AsString(key);
+  entry.group_count = strlen(entry.group) + 1;
+  entry.type = PyInt_AsLong(type);
+  entry.subtype = PyInt_AsLong(subtype);
+  entry.timestamp_sec = PyInt_AsLong(timestamp_sec);
+  entry.timestamp_usec = PyInt_AsLong(timestamp_usec);
 
-       if (!PyLong_Check(timestamp_usec))
-       {
-               PyErr_SetString(PyExc_RuntimeError, "timestamp_usec should be a 
long type");
-               return PyLong_FromLong(-1);
-       }
+  switch (entry.subtype)
+  {
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_NONE:
+      entry.data = NULL;
+      entry.data_count = 0;
+      break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_STRING:
+      if (!PyString_Check(data))
+      {
+        PyErr_SetString(PyExc_TypeError, "'data' should be a 'string' type.");
+        return NULL;
+      }
+      str = NULL;
+      str = PyString_AsString(data);
+      length = strlen(str) + 1;
+      entry.data = malloc(length);
+      assert(entry.data);
+      memcpy(entry.data, str, length);
+      entry.data_count = length;
+      break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_INT:
+      if (!PyLong_Check(data))
+      {
+        PyErr_SetString(PyExc_TypeError, "'data' should be a 'long' type.");
+        return NULL;
+      }
+      i = 0;
+      i = PyInt_AsLong(data);
+      length = sizeof(i);
+      entry.data = malloc(length);
+      assert(entry.data);
+      memcpy(&entry.data, &i, length);
+      entry.data_count = length;
+      break;
+    case PLAYERC_BLACKBOARD_DATA_SUBTYPE_DOUBLE:
+      if (!PyLong_Check(data))
+      {
+        PyErr_SetString(PyExc_TypeError, "'data' should be a 'long' type.");
+        return NULL;
+      }
+      d = 0.0;
+      d = PyLong_AsDouble(data);
+      length = sizeof(d);
+      entry.data = malloc(length);
+      assert(entry.data);
+      memcpy(&entry.data, &d, length);
+      entry.data_count = length;
+      break;
+    default:
+      entry.data = NULL;
+      entry.data_count = 0;
+      break;
+  }
 
-       entry.key = PyString_AsString(key);
-       entry.key_count = strlen(entry.key) + 1;
-       entry.group = PyString_AsString(key);
-       entry.group_count = strlen(entry.group) + 1;
-       entry.type = PyInt_AsLong(type);
-       entry.subtype = PyInt_AsLong(subtype);
-       entry.timestamp_sec = PyInt_AsLong(timestamp_sec);
-       entry.timestamp_usec = PyInt_AsLong(timestamp_usec);
+  result = playerc_blackboard_set_entry(self, &entry);
+  groups_dict = PyTuple_GetItem((PyObject*)self->py_private, 
DICT_GROUPS_INDEX); // Borrowed reference
+  assert(groups_dict);
+  Py_INCREF(dict);
+  playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, 
entry.key, entry.group, dict); // Steals reference to entry
+  free(entry.data);
 
-    switch (entry.subtype)
-    {
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_NONE:
-               entry.data = NULL;
-               entry.data_count = 0;
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_STRING:
-               if (!PyString_Check(data))
-               {
-                       PyErr_SetString(PyExc_RuntimeError, "data should be a 
string type.");
-                       return PyLong_FromLong(-1);
-               }
-               str = NULL;
-               str = PyString_AsString(data);
-               length = strlen(str) + 1;
-               entry.data = malloc(length);
-               assert(entry.data);
-               memcpy(entry.data, str, length);
-               entry.data_count = length;
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_INT:
-               if (!PyLong_Check(data))
-               {
-                       PyErr_SetString(PyExc_RuntimeError, "data should be a 
long type.");
-                       return PyLong_FromLong(-1);
-               }
-               i = 0;
-               i = PyInt_AsLong(data);
-               length = sizeof(i);
-               entry.data = malloc(length);
-               assert(entry.data);
-               memcpy(&entry.data, &i, length);
-               entry.data_count = length;
-       break;
-       case PLAYERC_BLACKBOARD_DATA_SUBTYPE_DOUBLE:
-               if (!PyLong_Check(data))
-               {
-                       PyErr_SetString(PyExc_RuntimeError, "data should be a 
long type.");
-                       return PyLong_FromLong(-1);
-               }
-               d = 0.0;
-               d = PyLong_AsDouble(data);
-               length = sizeof(d);
-               entry.data = malloc(length);
-               assert(entry.data);
-               memcpy(&entry.data, &d, length);
-               entry.data_count = length;
-       break;
-       default:
-               entry.data = NULL;
-               entry.data_count = 0;
-       break;
-    }
-    result = playerc_blackboard_set_entry(self, &entry);
-    if (result != 0)
-    {
-       return PyLong_FromLong(-1);
-    }
-    free(entry.data);
+  return PyInt_FromLong(result);
+}
 
-    return PyLong_FromLong(0);
-  }
 }
 
 %{
 // This code overrides the default swig wrapper. We use a different callback 
function for the python interface.
-// It performs the same functionality, but points the callback function to the 
new python one.
+// It performs the same functionality, but points the callback function to the 
new python one. 
 playerc_blackboard_t *python_playerc_blackboard_create(playerc_client_t 
*client, int index)
 {
   playerc_blackboard_t *device= playerc_blackboard_create(client, index);
@@ -850,10 +1090,14 @@
     PyErr_SetString(PyExc_RuntimeError, "Failed to create blackboard");
     return NULL;
   }
+
+  // Tuple item 0: Dictionary of group/keys/values. As in 
dict['group']['key']=value. Updated on client.read() callback.
+  // Tuple item 1: Dictionary of keys that have been subscribed to.
+  // Tuple item 2: List of all events that have occurred. Need to call 
set_queue_events(True). Updated on client.read() callback.
+  // Tuple item 3: Whether to queue all events or not. Default not. (0 for 
false, non-zero for true)
+  device->py_private = (void*)Py_BuildValue("({},{},[],i)",0); // tuple of a 
dict and a list to store our keys and events in
   
-  device->py_private = Py_BuildValue("({},[],i)",0); // tuple of a dict and a 
list to store our keys and events in
-  
-  device->on_blackboard_event = playerc_blackboard_python_on_blackboard_event;
+  device->on_blackboard_event = 
playerc_blackboard___python_on_blackboard_event__;
   return device;
 
 }
@@ -866,7 +1110,7 @@
   playerc_device_term(&device->info);
   if (device->py_private != NULL)
   {
-       Py_DECREF((PyObject*)device->py_private);
+       Py_XDECREF((PyObject*)device->py_private);
   }
   free(device);
 }

Modified: code/player/trunk/client_libs/libplayerc/dev_blackboard.c
===================================================================
--- code/player/trunk/client_libs/libplayerc/dev_blackboard.c   2008-07-23 
06:40:18 UTC (rev 6912)
+++ code/player/trunk/client_libs/libplayerc/dev_blackboard.c   2008-07-23 
07:14:31 UTC (rev 6913)
@@ -295,6 +295,43 @@
   return 0;
 }
 
+// Get the value of a key
+int playerc_blackboard_get_entry(playerc_blackboard_t* device, const char* 
key, const char* group, player_blackboard_entry_t** entry_out)
+{
+  player_blackboard_entry_t req;
+  memset(&req, 0, sizeof(req));
+  req.key = strdup(key);
+  req.key_count = strlen(key) + 1;
+  
+  req.group = strdup(group);
+  req.group_count = strlen(group) + 1;
+
+  if (playerc_client_request(device->info.client, &device->info, 
+  PLAYER_BLACKBOARD_REQ_GET_ENTRY, &req, (void**)entry_out) < 0)
+  {
+       if (req.key != NULL)
+       {
+               free(req.key);
+       }
+       if (req.group != NULL)
+       {
+               free(req.group);
+       }
+    PLAYERC_ERR("failed to get to blackboard entry");
+    return -1;
+  }
+
+  if (req.key != NULL)
+  {
+    free(req.key);
+  }
+  if (req.group != NULL)
+  {
+    free(req.group);
+  }
+  return 0;
+}
+
 // Unsubscribe from a blackboard key
 int playerc_blackboard_unsubscribe_from_key(playerc_blackboard_t* device, 
const char* key, const char* group)
 {

Modified: code/player/trunk/client_libs/libplayerc/playerc.h
===================================================================
--- code/player/trunk/client_libs/libplayerc/playerc.h  2008-07-23 06:40:18 UTC 
(rev 6912)
+++ code/player/trunk/client_libs/libplayerc/playerc.h  2008-07-23 07:14:31 UTC 
(rev 6913)
@@ -1174,6 +1174,10 @@
  * responsible for freeing it. */
 int playerc_blackboard_subscribe_to_key(playerc_blackboard_t *device, const 
char* key, const char* group, player_blackboard_entry_t** entry);
 
+/** @brief Get the current value of a key, without subscribing. If entry is 
none null it will be filled in with the response. The caller is
+ * responsible for freeing it. */
+int playerc_blackboard_get_entry(playerc_blackboard_t *device, const char* 
key, const char* group, player_blackboard_entry_t** entry);
+
 /** @brief Unsubscribe from a key. */
 int playerc_blackboard_unsubscribe_from_key(playerc_blackboard_t *device, 
const char* key, const char* group);
 

Modified: code/player/trunk/libplayercore/interfaces/064_blackboard.def
===================================================================
--- code/player/trunk/libplayercore/interfaces/064_blackboard.def       
2008-07-23 06:40:18 UTC (rev 6912)
+++ code/player/trunk/libplayercore/interfaces/064_blackboard.def       
2008-07-23 07:14:31 UTC (rev 6913)
@@ -13,6 +13,8 @@
 message { REQ, SUBSCRIBE_TO_GROUP, 4, player_blackboard_entry_t };
 /** Request/reply subtype: unsubscribe from group. */
 message { REQ, UNSUBSCRIBE_FROM_GROUP, 5, player_blackboard_entry_t };
+/** Request/reply subtype: get a value. */
+message { REQ, GET_ENTRY, 6, player_blackboard_entry_t };
+
 /** Data update reply */
 message { DATA, UPDATE, 1, player_blackboard_entry_t };
-

Modified: code/player/trunk/server/drivers/blackboard/localbb/localbb.cpp
===================================================================
--- code/player/trunk/server/drivers/blackboard/localbb/localbb.cpp     
2008-07-23 06:40:18 UTC (rev 6912)
+++ code/player/trunk/server/drivers/blackboard/localbb/localbb.cpp     
2008-07-23 07:14:31 UTC (rev 6913)
@@ -69,12 +69,14 @@
 #include <stdlib.h>
 #include <libplayercore/playercore.h>
 #include <libplayercore/error.h>
-// #include <libplayerc++/playererror.h>
 #include <vector>
 #include <map>
 #include <strings.h>
 #include <iostream>
 
+bool operator ==(const player_devaddr_t &a, const player_devaddr_t &b);
+bool operator <(const player_devaddr_t &a, const player_devaddr_t &b);
+
 /[EMAIL PROTECTED] EntryData
  * @brief Custom blackboard-entry data representation used internally by the 
driver. */
 typedef struct EntryData
@@ -180,6 +182,8 @@
                int Setup();
                /** @brief Driver de-initialisation. */
                int Shutdown();
+               /** Override the unsubscribe to stop sending events to devices 
which are no longer subscribed. */
+               int Unsubscribe(player_devaddr_t addr);
 
                // MessageHandler
                /** @brief Process incoming messages.
@@ -203,6 +207,15 @@
                 * @return 0 for success, -1 on error.
                 */
                int ProcessSubscribeKeyMessage(QueuePointer &resp_queue, 
player_msghdr * hdr, void * data);
+               /** @brief Process a get entry message.
+                * Retrieves the entry for the given key, but does not 
subscribe to the key.
+                * Publishes the entry.
+                * @param resp_queue Player response queue.
+                * @param hdr Message header.
+                * @param data Message data.
+                * @return 0 for success, -1 on error.
+                * */
+               int ProcessGetEntryMessage(QueuePointer &resp_queue, 
player_msghdr * hdr, void * data);
                /** @brief Process an unsubscribe from key message.
                 * Removes the response queue from the list of devices 
listening to that key in the map.
                 * @param resp_queue Player response queue.
@@ -245,7 +258,7 @@
                 * @param group Second identifier.
                 * @param resp_queue Player response queue of the subscriber.
                 */
-               BlackBoardEntry SubscribeKey(const string &key, const string 
&group, const QueuePointer &resp_queue);
+               BlackBoardEntry SubscribeKey(const string &key, const string 
&group, const QueuePointer &resp_queue, const player_devaddr_t addr);
                /** @brief Remove the key and queue combination from the 
listeners hash-map.
                 * @param key Entry key.
                 * @param group Second identifier.
@@ -258,7 +271,7 @@
                 * @param qp resp_queue Player response queue of the subscriber
                 * @return Vector of blackboard entries of that group
                 */
-               vector<BlackBoardEntry> SubscribeGroup(const string &group, 
const QueuePointer &qp);
+               vector<BlackBoardEntry> SubscribeGroup(const string &group, 
const QueuePointer &qp, const player_devaddr_t addr);
                /**
                 * @brief Remove the group from the group listeners hash-map.
                 * @param group Entry group
@@ -267,8 +280,8 @@
                void UnsubscribeGroup(const string &group, const QueuePointer 
&qp);
 
                /** @brief Set the entry in the entries hashmap. *
-                * @param entry BlackBoardEntry that must be put in the hashmap.
-                */
+               * @param entry BlackBoardEntry that must be put in the hashmap.
+               */
                void SetEntry(const BlackBoardEntry &entry);
 
                // Helper functions
@@ -281,17 +294,80 @@
                /** Map of labels to entry data.
                * map<group, map<key, entry> >
                */
+
                map<string, map<string, BlackBoardEntry> > entries;
                /** Map of labels to listening queues.
                * map<group, map<key, vector<device queue> > >
                */
                map<string, map<string, vector<QueuePointer> > > listeners;
+
                /** Map of groups to queues subscribed to groups.
                * map<group, vector<device queue> >
                */
                map<string, vector<QueuePointer> > group_listeners;
+
+               /** Map of queues to devices. Used to remove unneeded queues 
when a device is unsubscribed. */
+               map<player_devaddr_t, QueuePointer> subscribed_devices;
 };
+////////////////////////////////////////////////////////////////////////////////
+// Override the unsubscribe. Stop sending out events to unsubscribed devices.
+int LocalBB::Unsubscribe(player_devaddr_t addr)
+{
+       QueuePointer &qp = subscribed_devices[addr];
+       for (map<string, map<string, vector<QueuePointer> > >::iterator itr = 
listeners.begin(); itr != listeners.end(); itr++)
+       {
+               map<string, vector<QueuePointer> > &keys = (*itr).second;
+               for (map<string, vector<QueuePointer> >::iterator jtr = 
keys.begin(); jtr != keys.end(); jtr++)
+               {
+                       vector<QueuePointer> &qps = (*jtr).second;
+                       vector<vector<QueuePointer>::iterator> remove_list;
+                       for (vector<QueuePointer>::iterator ktr = qps.begin(); 
ktr != qps.end(); ktr++)
+                       {
+                               if ((*ktr) == qp)
+                               {
+                                       remove_list.push_back(ktr);
+                               }
+                       }
+                       for (vector<vector<QueuePointer>::iterator>::iterator 
ltr = remove_list.begin(); ltr != remove_list.end(); ltr++)
+                       {
+                               qps.erase(*ltr);
+                       }
+               }
+       }
 
+       for (map<string, vector<QueuePointer> >::iterator itr = 
group_listeners.begin(); itr != group_listeners.end(); itr++)
+       {
+               vector<QueuePointer> &qps = (*itr).second;
+               vector<vector<QueuePointer>::iterator> remove_list;
+               for (vector<QueuePointer>::iterator jtr = qps.begin(); jtr != 
qps.end(); jtr++)
+               {
+                       if ((*jtr) == qp)
+                       {
+                               remove_list.push_back(jtr);
+                       }
+               }
+               for (vector<vector<QueuePointer>::iterator>::iterator ltr = 
remove_list.begin(); ltr != remove_list.end(); ltr++)
+               {
+                       qps.erase(*ltr);
+               }
+       }
+       int shutdownResult;
+
+       if(subscriptions == 0)
+               shutdownResult = -1;
+       else if ( subscriptions == 1)
+       {
+               shutdownResult = Shutdown();
+               subscriptions--;
+       }
+       else
+       {
+               subscriptions--;
+               shutdownResult = 0;
+       }
+
+       return shutdownResult;
+}
 
////////////////////////////////////////////////////////////////////////////////
 // Factory method.
 Driver* LocalBB_Init(ConfigFile* cf, int section)
@@ -364,6 +440,10 @@
        {
                return ProcessUnsubscribeGroupMessage(resp_queue, hdr, data);
        }
+       else if (Message::MatchMessage(hdr, PLAYER_MSGTYPE_REQ, 
PLAYER_BLACKBOARD_REQ_GET_ENTRY, this->device_addr))
+       {
+               return ProcessGetEntryMessage(resp_queue, hdr, data);
+       }
        // Don't know how to handle this message
        return -1;
 }
@@ -377,7 +457,7 @@
 
        // Add the device to the listeners map
        player_blackboard_entry_t *request = 
reinterpret_cast<player_blackboard_entry_t*>(data);
-       BlackBoardEntry current_value = SubscribeKey(request->key, 
request->group, resp_queue);
+       BlackBoardEntry current_value = SubscribeKey(request->key, 
request->group, resp_queue, hdr->addr);
 
        // Get the entry for the given key
        player_blackboard_entry_t response = 
ToPlayerBlackBoardEntry(current_value);
@@ -408,7 +488,47 @@
 
        return 0;
 }
+////////////////////////////////////////////////////////////////////////////////
+// Retrieve an entry for a key.
+int LocalBB::ProcessGetEntryMessage(QueuePointer &resp_queue, player_msghdr * 
hdr, void * data)
+{
+       if (!CheckHeader(hdr))
+               return -1;
 
+       // Retrieve the entry for the key
+       player_blackboard_entry_t *request = 
reinterpret_cast<player_blackboard_entry_t*>(data);
+       BlackBoardEntry current_value = 
entries[string(request->group)][string(request->key)];
+
+       // Convert the entry
+       player_blackboard_entry_t response = 
ToPlayerBlackBoardEntry(current_value);
+       size_t response_size = sizeof(player_blackboard_entry_t) + 
response.key_count + response.group_count + response.data_count;
+
+       // Publish the blackboard entry
+       this->Publish(
+               this->device_addr,
+               resp_queue,
+               PLAYER_MSGTYPE_RESP_ACK,
+               PLAYER_BLACKBOARD_REQ_GET_ENTRY,
+               &response,
+               response_size,
+               NULL);
+
+       if (response.key)
+       {
+               delete [] response.key;
+       }
+       if (response.group)
+       {
+               delete [] response.group;
+       }
+       if (response.data)
+       {
+               delete [] response.data;
+       }
+
+       return 0;
+}
+
 
////////////////////////////////////////////////////////////////////////////////
 // Unsubscribe a device from a key.
 int LocalBB::ProcessUnsubscribeKeyMessage(QueuePointer &resp_queue, 
player_msghdr * hdr, void * data)
@@ -438,14 +558,13 @@
 int LocalBB::ProcessSubscribeGroupMessage(QueuePointer &resp_queue, 
player_msghdr * hdr, void * data)
 {
        if (!CheckHeader(hdr))
-               return -1;
+                       return -1;
 
        // Add the device to the listeners map
        player_blackboard_entry_t *request = 
reinterpret_cast<player_blackboard_entry_t*>(data);
-       vector<BlackBoardEntry> current_values = SubscribeGroup(request->group, 
resp_queue);
+       vector<BlackBoardEntry> entries = SubscribeGroup(request->group, 
resp_queue, hdr->addr);
 
-       // Get the entries for the given key and send the data updates
-       for (vector<BlackBoardEntry>::iterator itr=current_values.begin(); itr 
!= current_values.end(); itr++)
+       for (vector<BlackBoardEntry>::iterator itr = entries.begin(); itr != 
entries.end(); itr++)
        {
                BlackBoardEntry current_value = *itr;
                player_blackboard_entry_t response = 
ToPlayerBlackBoardEntry(current_value);
@@ -503,7 +622,7 @@
                this->device_addr,
                resp_queue,
                PLAYER_MSGTYPE_RESP_ACK,
-               PLAYER_BLACKBOARD_REQ_UNSUBSCRIBE_FROM_KEY,
+               PLAYER_BLACKBOARD_REQ_UNSUBSCRIBE_FROM_GROUP,
                NULL,
                0,
                NULL);
@@ -566,9 +685,10 @@
 
 
////////////////////////////////////////////////////////////////////////////////
 // Add a device to the listener list for a key. Return the current value of 
the entry.
-BlackBoardEntry LocalBB::SubscribeKey(const string &key, const string &group, 
const QueuePointer &resp_queue)
+BlackBoardEntry LocalBB::SubscribeKey(const string &key, const string &group, 
const QueuePointer &resp_queue, const player_devaddr_t addr)
 {
        listeners[group][key].push_back(resp_queue);
+       subscribed_devices[addr] = resp_queue;
        BlackBoardEntry entry = entries[group][key];
        if (entry.key == "")
        {
@@ -596,17 +716,27 @@
 
 
////////////////////////////////////////////////////////////////////////////////
 // Add a device to the group listener map. Return vector of entries for that 
group.
-vector<BlackBoardEntry> LocalBB::SubscribeGroup(const string &group, const 
QueuePointer &qp)
+vector<BlackBoardEntry> LocalBB::SubscribeGroup(const string &group, const 
QueuePointer &qp, const player_devaddr_t addr)
 {
        group_listeners[group].push_back(qp);
+       subscribed_devices[addr] = qp;
+
        vector<BlackBoardEntry> group_entries;
+       //map<group, map<key, entry> >
+       map<string, BlackBoardEntry> &entry_map = entries[group];
 
-       // Add all entries for a group to the group_entries vector
-       //map<group, map<key, entry> >
-       map<string, BlackBoardEntry> entry_map = entries[group];
        for (map<string, BlackBoardEntry>::iterator itr = entry_map.begin(); 
itr != entry_map.end(); itr++)
        {
-               group_entries.push_back((*itr).second);
+               BlackBoardEntry current_value = (*itr).second;
+               if (current_value.key == "")
+               {
+                       current_value.key = (*itr).first;
+               }
+               if (current_value.group == "")
+               {
+                       current_value.group = group;
+               }
+               group_entries.push_back(current_value);
        }
        return group_entries;
 }
@@ -645,3 +775,27 @@
        }
        return true;
 }
+
+
+bool operator ==(const player_devaddr_t &a, const player_devaddr_t &b)
+{
+       return (a.host == b.host) && (a.robot == b.robot) && (a.interf == 
b.interf) && (a.index == b.index);
+}
+
+bool operator <(const player_devaddr_t &a, const player_devaddr_t &b)
+{
+       if (a.host != b.host)
+       {
+               return a.host < b.host;
+       }
+       else if (a.robot != b.robot)
+       {
+               return a.robot < b.robot;
+       }
+       else if (a.interf != b.interf)
+       {
+               return a.interf < b.interf;
+       }
+
+       return a.index < b.index;
+}


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.

-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
Playerstage-commit mailing list
Playerstage-commit@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/playerstage-commit

Reply via email to