Revision: 6886 http://playerstage.svn.sourceforge.net/playerstage/?rev=6886&view=rev Author: thjc Date: 2008-07-18 03:15:24 +0000 (Fri, 18 Jul 2008)
Log Message: ----------- fixes to the group subscription etc for blackboard proxies. Modified Paths: -------------- code/player/branches/release-2-1-patches/client_libs/libplayerc/bindings/python/playerc.i code/player/branches/release-2-1-patches/client_libs/libplayerc/dev_blackboard.c code/player/branches/release-2-1-patches/client_libs/libplayerc/playerc.h code/player/branches/release-2-1-patches/libplayercore/interfaces/064_blackboard.def code/player/branches/release-2-1-patches/server/drivers/blackboard/localbb/localbb.cpp Modified: code/player/branches/release-2-1-patches/client_libs/libplayerc/bindings/python/playerc.i =================================================================== --- code/player/branches/release-2-1-patches/client_libs/libplayerc/bindings/python/playerc.i 2008-07-17 21:30:23 UTC (rev 6885) +++ code/player/branches/release-2-1-patches/client_libs/libplayerc/bindings/python/playerc.i 2008-07-18 03:15:24 UTC (rev 6886) @@ -416,83 +416,185 @@ %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 +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; - group_dict = (PyObject*) PyDict_GetItemString(groups, entry.group); - if (!group_dict) - { - return; - } - - // find our key in the group, if it exists - entry_dict = (PyObject*) PyDict_GetItemString(group_dict, entry.key); - if (!entry_dict) - { - return; - } + entry_dict = PyDict_New(); + assert (entry_dict); - 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) - { - 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; - } + assert(entry); + assert(entry->key); + assert(entry->key_count > 0); + assert(entry->group); + assert(entry->group_count > 0); - 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)); - } + PyDict_SetItemString(entry_dict, "key", PyString_FromString(entry->key)); + PyDict_SetItemString(entry_dict, "group", PyString_FromString(entry->group)); + 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) + { + 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; + } + + PyDict_SetItemString(entry_dict, "data", data); + return entry_dict; } -// Will always return a list object. Returns a list of dictionary objects containing the event data. -PyObject *get_events() +// Helper function which sets a value in a nested dictionary. Equivalent of dict['group']['key'] = value. +PyObject *__set_nested_dictionary_entry__(PyObject *dict, const char* key, const char* group, PyObject *entry) { + PyObject *group_dict; + 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); + if (!group_dict) + { + create_dict = 1; + group_dict = PyDict_New(); + } + + PyDict_SetItemString(group_dict, key, entry); + + // If we created the dictionary, we need to put it in the parent dictionary. + if (create_dict == 1) + { + PyDict_SetItemString(dict, group, group_dict); + } + + Py_RETURN_NONE; +} + +// 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 *group_dict, *entry; + int old_value, new_value; + + // Get the dictionary for the entry's group. If it doesn't exist, create it and increment it. + group_dict = (PyObject*)PyDict_GetItemString(dict, group); + if (!group_dict) + { + playerc_blackboard___set_nested_dictionary_entry__(self, dict, key, group, (PyObject*)PyInt_FromLong(inc)); + return inc; + } + + entry = PyDict_GetItemString(group_dict, key); + old_value = PyLong_AsLong(entry); + 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)); + + 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); + assert(groups_dict); + + // Get a dictionary of the entry + entry_dict = playerc_blackboard___convert_blackboard_entry__(device, &entry); + assert(entry_dict); + // Set the entry in the groups dictionary + playerc_blackboard___set_nested_dictionary_entry__(device, groups_dict, entry.key, entry.group, entry_dict); + + // If we are queueing events, add them to the list + queue_events = PyTuple_GetItem(device->py_private, BOOL_QUEUE_EVENTS_INDEX); + assert(queue_events); + if (PyLong_AsLong(queue_events) != 0) + { + list = (PyObject*)PyTuple_GetItem(device->py_private, LIST_EVENTS_INDEX); + assert(list); + PyList_Append(list, PyDict_Copy(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; int i, j; + assert(self->py_private); + list = (PyObject*)PyTuple_GetItem(self->py_private, LIST_EVENTS_INDEX); + assert(list); - list = (PyObject*)PyTuple_GetItem(self->py_private,1); copy = PyList_New(0); j = PyList_Size(list); for (i = 0; i < j; i++) @@ -503,342 +605,370 @@ 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, *list, *pair; + int i, j; + + assert(self->py_private); + dict = (PyObject*)PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(dict); + + copy = PyDict_New(); + list = PyDict_Items(dict); + j = PyList_Size(list); + for (i = 0; i < j; i++) + { + pair = PyList_GetItem(list, i); + PyDict_SetItem(copy, PyTuple_GetItem(pair, 0), PyTuple_GetItem(pair, 1)); + } + + return copy; +} + +// Set whether events should be put into the list, to be retrieved by get_events_list(). +PyObject *SetQueueEvents(PyObject *boolean) +{ 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)); + PyTuple_SetItem(self->py_private, BOOL_QUEUE_EVENTS_INDEX, PyLong_FromLong(0)); } else { - PyTuple_SetItem(self->py_private,2,PyLong_FromLong(1)); + PyTuple_SetItem(self->py_private, BOOL_QUEUE_EVENTS_INDEX, PyLong_FromLong(1)); } + + 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(self->py_private, DICT_SUBSCRIPTION_DATA_INDEX); + assert(subscription_data); + ref_count = playerc_blackboard___increment_reference_count__(self, subscription_data, key, group, -1); + if (ref_count <= 0) + { + groups_dict = PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + group_dict = PyDict_GetItemString(groups_dict, group); + 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); + groups_dict = (PyObject*)PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + + assert(entry_dict); + playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, key, group, entry_dict); + + subscription_data = PyTuple_GetItem(self->py_private, DICT_SUBSCRIPTION_DATA_INDEX); + 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; + const char* key; + int result; + result = playerc_blackboard_unsubscribe_from_group(self, group); + + assert(self->py_private); + groups_dict = PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + + group_dict = PyDict_GetItemString(groups_dict, group); + if (group_dict) + { + list = PyList_New(0); + subscription_data = PyTuple_GetItem(self->py_private, DICT_SUBSCRIPTION_DATA_INDEX); + 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); + key = PyString_AsString(PyTuple_GetItem(tuple, 0)); + ref_count = playerc_blackboard___increment_reference_count__(self, subscription_data, key, group, 0); + if (ref_count <= 0) + { + PyList_Append(list, PyTuple_GetItem(tuple, 0)); + } } - 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; + + j = PyList_Size(list); + for (i =0; i < j; i++) + { + PyDict_DelItem(group_dict, PyList_GetItem(list, i)); + } } -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); + + groups_dict = PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + assert(entry_dict); + playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, key, group, 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); + groups_dict = PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + assert(entry_dict); + + playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, entry->key, entry->group, 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) + + player_blackboard_entry_t entry; + memset(&entry, 0, sizeof(entry)); + + 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 (key == NULL || group == NULL || type == NULL || subtype == NULL || timestamp_sec == NULL || timestamp_usec == NULL || data == NULL) { - 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; - - if (!PyDict_Check(dict)) - { - PyErr_SetString(PyExc_RuntimeError, "Expected a dictionary object."); - return PyLong_FromLong(-1); - } + 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; + } - player_blackboard_entry_t entry; - memset(&entry, 0, sizeof(entry)); - - 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 (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(key)) - { - PyErr_SetString(PyExc_RuntimeError, "key should be a string type."); - return PyLong_FromLong(-1); - } - - if (!PyString_Check(group)) - { - PyErr_SetString(PyExc_RuntimeError, "group should be a string 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(subtype)) - { - PyErr_SetString(PyExc_RuntimeError, "subtype should be a long type."); - return PyLong_FromLong(-1); - } - - if (!PyLong_Check(timestamp_sec)) - { - PyErr_SetString(PyExc_RuntimeError, "timestamp_sec should be a long type."); - return PyLong_FromLong(-1); - } - - if (!PyLong_Check(timestamp_usec)) - { - PyErr_SetString(PyExc_RuntimeError, "timestamp_usec 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); - - 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 PyLong_FromLong(0); + if (!PyString_Check(key)) + { + PyErr_SetString(PyExc_TypeError, "'key' should be a 'string' type."); + return NULL; } + + if (!PyString_Check(group)) + { + PyErr_SetString(PyExc_TypeError, "'group' should be a 'string' type."); + return NULL; + } + + if (!PyLong_Check(type)) + { + PyErr_SetString(PyExc_TypeError, "'type' should be a 'long' type."); + return NULL; + } + + if (!PyLong_Check(subtype)) + { + PyErr_SetString(PyExc_TypeError, "'subtype' 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(timestamp_usec)) + { + PyErr_SetString(PyExc_TypeError, "'timestamp_usec' should be a 'long' type"); + return NULL; + } + + 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); + + 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; + } + + result = playerc_blackboard_set_entry(self, &entry); + groups_dict = PyTuple_GetItem(self->py_private, DICT_GROUPS_INDEX); + assert(groups_dict); + assert(entry_dict); + playerc_blackboard___set_nested_dictionary_entry__(self, groups_dict, entry.key, entry.group, dict); + free(entry.data); + + return PyInt_FromLong(result); } +} + %{ // 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. @@ -850,10 +980,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 = 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; } Modified: code/player/branches/release-2-1-patches/client_libs/libplayerc/dev_blackboard.c =================================================================== --- code/player/branches/release-2-1-patches/client_libs/libplayerc/dev_blackboard.c 2008-07-17 21:30:23 UTC (rev 6885) +++ code/player/branches/release-2-1-patches/client_libs/libplayerc/dev_blackboard.c 2008-07-18 03:15:24 UTC (rev 6886) @@ -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/branches/release-2-1-patches/client_libs/libplayerc/playerc.h =================================================================== --- code/player/branches/release-2-1-patches/client_libs/libplayerc/playerc.h 2008-07-17 21:30:23 UTC (rev 6885) +++ code/player/branches/release-2-1-patches/client_libs/libplayerc/playerc.h 2008-07-18 03:15:24 UTC (rev 6886) @@ -1175,6 +1175,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/branches/release-2-1-patches/libplayercore/interfaces/064_blackboard.def =================================================================== --- code/player/branches/release-2-1-patches/libplayercore/interfaces/064_blackboard.def 2008-07-17 21:30:23 UTC (rev 6885) +++ code/player/branches/release-2-1-patches/libplayercore/interfaces/064_blackboard.def 2008-07-18 03:15:24 UTC (rev 6886) @@ -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/branches/release-2-1-patches/server/drivers/blackboard/localbb/localbb.cpp =================================================================== --- code/player/branches/release-2-1-patches/server/drivers/blackboard/localbb/localbb.cpp 2008-07-17 21:30:23 UTC (rev 6885) +++ code/player/branches/release-2-1-patches/server/drivers/blackboard/localbb/localbb.cpp 2008-07-18 03:15:24 UTC (rev 6886) @@ -75,6 +75,9 @@ #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 +183,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 +208,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 +259,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 +272,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 @@ -281,6 +295,7 @@ /** 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> > > @@ -291,8 +306,69 @@ * 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) @@ -365,6 +441,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; } @@ -378,7 +458,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); @@ -409,7 +489,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) @@ -443,10 +563,9 @@ // 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); @@ -504,7 +623,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); @@ -567,9 +686,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 == "") { @@ -597,17 +717,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; - - // Add all entries for a group to the group_entries vector //map<group, map<key, entry> > - map<string, BlackBoardEntry> entry_map = entries[group]; + 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; } @@ -646,3 +776,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