2015-08-12 10:53:41 +00:00
|
|
|
/**
|
|
|
|
* Class definition for a MicroBitMessageBus.
|
|
|
|
*
|
|
|
|
* The MicroBitMessageBus handles all messages passed between components.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "MicroBit.h"
|
|
|
|
|
|
|
|
/**
|
2016-01-13 16:16:18 +00:00
|
|
|
* Constructor.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Create a new Message Bus.
|
|
|
|
*/
|
|
|
|
MicroBitMessageBus::MicroBitMessageBus()
|
|
|
|
{
|
|
|
|
this->listeners = NULL;
|
2015-08-19 22:35:45 +00:00
|
|
|
this->evt_queue_head = NULL;
|
|
|
|
this->evt_queue_tail = NULL;
|
2015-10-31 10:27:38 +00:00
|
|
|
this->queueLength = 0;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Invokes a callback on a given MicroBitListener
|
|
|
|
*
|
|
|
|
* Internal wrapper function, used to enable
|
2015-08-31 22:25:10 +00:00
|
|
|
* parameterised callbacks through the fiber scheduler.
|
2015-08-12 10:53:41 +00:00
|
|
|
*/
|
|
|
|
void async_callback(void *param)
|
|
|
|
{
|
|
|
|
MicroBitListener *listener = (MicroBitListener *)param;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
// OK, now we need to decide how to behave depending on our configuration.
|
|
|
|
// If this a fiber f already active within this listener then check our
|
2016-01-13 16:16:18 +00:00
|
|
|
// configuration to determine the correct course of action.
|
2015-09-11 15:39:38 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_BUSY)
|
|
|
|
{
|
|
|
|
// Drop this event, if that's how we've been configured.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_DROP_IF_BUSY)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Queue this event up for later, if that's how we've been configured.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY)
|
|
|
|
{
|
|
|
|
listener->queue(listener->evt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
// Determine the calling convention for the callback, and invoke...
|
2015-09-09 23:04:27 +00:00
|
|
|
// C++ is really bad at this! Especially as the ARM compiler is yet to support C++ 11 :-/
|
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
// Record that we have a fiber going into this listener...
|
|
|
|
listener->flags |= MESSAGE_BUS_LISTENER_BUSY;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
// Firstly, check for a method callback into an object.
|
|
|
|
if (listener->flags & MESSAGE_BUS_LISTENER_METHOD)
|
|
|
|
listener->cb_method->fire(listener->evt);
|
|
|
|
|
|
|
|
// Now a parameterised C function
|
|
|
|
else if (listener->flags & MESSAGE_BUS_LISTENER_PARAMETERISED)
|
|
|
|
listener->cb_param(listener->evt, listener->cb_arg);
|
|
|
|
|
|
|
|
// We must have a plain C function
|
|
|
|
else
|
|
|
|
listener->cb(listener->evt);
|
|
|
|
|
2015-10-28 14:17:12 +00:00
|
|
|
// If there are more events to process, dequeue the next one and process it.
|
2015-09-11 15:39:38 +00:00
|
|
|
if ((listener->flags & MESSAGE_BUS_LISTENER_QUEUE_IF_BUSY) && listener->evt_queue)
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = listener->evt_queue;
|
|
|
|
|
|
|
|
listener->evt = item->evt;
|
|
|
|
listener->evt_queue = listener->evt_queue->next;
|
|
|
|
delete item;
|
2015-10-17 19:35:16 +00:00
|
|
|
|
|
|
|
// We spin the scheduler here, to preven any particular event handler from continuously holding onto resources.
|
|
|
|
schedule();
|
2015-09-11 15:39:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The fiber of exiting... clear our state.
|
|
|
|
listener->flags &= ~MESSAGE_BUS_LISTENER_BUSY;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
/**
|
|
|
|
* Queue the given event for processing at a later time.
|
|
|
|
* Add the given event at the tail of our queue.
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* @param The event to queue.
|
2015-08-19 22:35:45 +00:00
|
|
|
*/
|
|
|
|
void MicroBitMessageBus::queueEvent(MicroBitEvent &evt)
|
|
|
|
{
|
2015-09-18 22:20:44 +00:00
|
|
|
int processingComplete;
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
MicroBitEventQueueItem *prev = evt_queue_tail;
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
// Now process all handler regsitered as URGENT.
|
2015-11-01 15:16:27 +00:00
|
|
|
// These pre-empt the queue, and are useful for fast, high priority services.
|
|
|
|
processingComplete = this->process(evt, true);
|
2015-09-18 22:20:44 +00:00
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
// If we've already processed all event handlers, we're all done.
|
|
|
|
// No need to queue the event.
|
|
|
|
if (processingComplete)
|
|
|
|
return;
|
2015-09-18 22:20:44 +00:00
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
// If we need to queue, but there is no space, then there's nothg we can do.
|
2015-10-31 10:27:38 +00:00
|
|
|
if (queueLength >= MESSAGE_BUS_LISTENER_MAX_QUEUE_DEPTH)
|
|
|
|
return;
|
2015-09-18 22:20:44 +00:00
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
// Otherwise, we need to queue this event for later processing...
|
|
|
|
// We queue this event at the tail of the queue at the point where we entered queueEvent()
|
|
|
|
// This is important as the processing above *may* have generated further events, and
|
|
|
|
// we want to maintain ordering of events.
|
2015-10-31 10:27:38 +00:00
|
|
|
MicroBitEventQueueItem *item = new MicroBitEventQueueItem(evt);
|
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
// The queue was empty when we entered this function, so queue our event at the start of the queue.
|
2015-10-31 10:27:38 +00:00
|
|
|
__disable_irq();
|
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
if (prev == NULL)
|
2015-09-18 22:20:44 +00:00
|
|
|
{
|
2015-11-01 15:16:27 +00:00
|
|
|
item->next = evt_queue_head;
|
|
|
|
evt_queue_head = item;
|
2016-01-13 16:16:18 +00:00
|
|
|
}
|
2015-11-01 15:16:27 +00:00
|
|
|
else
|
2015-10-31 10:27:38 +00:00
|
|
|
{
|
2015-11-01 15:16:27 +00:00
|
|
|
item->next = prev->next;
|
|
|
|
prev->next = item;
|
2015-09-18 22:20:44 +00:00
|
|
|
}
|
|
|
|
|
2015-11-01 15:16:27 +00:00
|
|
|
if (item->next == NULL)
|
|
|
|
evt_queue_tail = item;
|
|
|
|
|
2015-10-31 10:27:38 +00:00
|
|
|
queueLength++;
|
|
|
|
|
|
|
|
__enable_irq();
|
2015-08-19 22:35:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extract the next event from the front of the event queue (if present).
|
2016-01-13 16:16:18 +00:00
|
|
|
* @return
|
2015-08-19 22:35:45 +00:00
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* @param The event to queue.
|
2015-08-19 22:35:45 +00:00
|
|
|
*/
|
|
|
|
MicroBitEventQueueItem* MicroBitMessageBus::dequeueEvent()
|
|
|
|
{
|
|
|
|
MicroBitEventQueueItem *item = NULL;
|
|
|
|
|
|
|
|
__disable_irq();
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
if (evt_queue_head != NULL)
|
2016-01-13 16:16:18 +00:00
|
|
|
{
|
2015-08-19 22:35:45 +00:00
|
|
|
item = evt_queue_head;
|
|
|
|
evt_queue_head = item->next;
|
|
|
|
|
|
|
|
if (evt_queue_head == NULL)
|
|
|
|
evt_queue_tail = NULL;
|
2015-10-31 10:27:38 +00:00
|
|
|
|
|
|
|
queueLength--;
|
2015-08-19 22:35:45 +00:00
|
|
|
}
|
2016-01-13 16:16:18 +00:00
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
__enable_irq();
|
|
|
|
|
2015-10-31 10:27:38 +00:00
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
return item;
|
|
|
|
}
|
|
|
|
|
2015-10-29 00:08:33 +00:00
|
|
|
/**
|
|
|
|
* Cleanup any MicroBitListeners marked for deletion from the list.
|
|
|
|
* @return The number of listeners removed from the list.
|
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::deleteMarkedListeners()
|
|
|
|
{
|
|
|
|
MicroBitListener *l, *p;
|
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
l = listeners;
|
|
|
|
p = NULL;
|
|
|
|
|
|
|
|
// Walk this list of event handlers. Delete any that match the given listener.
|
|
|
|
while (l != NULL)
|
|
|
|
{
|
2015-11-01 12:59:52 +00:00
|
|
|
if (l->flags & MESSAGE_BUS_LISTENER_DELETING && !l->flags & MESSAGE_BUS_LISTENER_BUSY)
|
2015-10-29 00:08:33 +00:00
|
|
|
{
|
|
|
|
if (p == NULL)
|
|
|
|
listeners = l->next;
|
2016-01-13 16:16:18 +00:00
|
|
|
else
|
2015-10-29 00:08:33 +00:00
|
|
|
p->next = l->next;
|
|
|
|
|
|
|
|
// delete the listener.
|
|
|
|
MicroBitListener *t = l;
|
|
|
|
l = l->next;
|
|
|
|
|
|
|
|
delete t;
|
|
|
|
removed++;
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return removed;
|
|
|
|
}
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
/**
|
|
|
|
* Periodic callback from MicroBit.
|
|
|
|
* Process at least one event from the event queue, if it is not empty.
|
2015-08-31 22:25:10 +00:00
|
|
|
* We then continue processing events until something appears on the runqueue.
|
2016-01-13 16:16:18 +00:00
|
|
|
*/
|
2015-08-19 22:35:45 +00:00
|
|
|
void MicroBitMessageBus::idleTick()
|
|
|
|
{
|
2015-10-29 00:08:33 +00:00
|
|
|
// Clear out any listeners marked for deletion
|
|
|
|
this->deleteMarkedListeners();
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
MicroBitEventQueueItem *item = this->dequeueEvent();
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// Whilst there are events to process and we have no useful other work to do, pull them off the queue and process them.
|
|
|
|
while (item)
|
2015-08-19 22:35:45 +00:00
|
|
|
{
|
2015-09-18 22:20:44 +00:00
|
|
|
// send the event to all standard event listeners.
|
2015-08-31 22:25:10 +00:00
|
|
|
this->process(item->evt);
|
2015-08-19 22:35:45 +00:00
|
|
|
|
|
|
|
// Free the queue item.
|
|
|
|
delete item;
|
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
// If we have created some useful work to do, we stop processing.
|
|
|
|
// This helps to minimise the number of blocked fibers we create at any point in time, therefore
|
2016-01-13 16:16:18 +00:00
|
|
|
// also reducing the RAM footprint.
|
2015-08-31 22:25:10 +00:00
|
|
|
if(!scheduler_runqueue_empty())
|
|
|
|
break;
|
|
|
|
|
2015-08-19 22:35:45 +00:00
|
|
|
// Pull the next event to process, if there is one.
|
|
|
|
item = this->dequeueEvent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Indicates whether or not we have any background work to do.
|
2016-01-13 16:16:18 +00:00
|
|
|
* @ return 1 if there are any events waitingto be processed, 0 otherwise.
|
2015-08-19 22:35:45 +00:00
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::isIdleCallbackNeeded()
|
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
return !(evt_queue_head == NULL);
|
2015-08-19 22:35:45 +00:00
|
|
|
}
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
/**
|
2015-08-31 22:25:10 +00:00
|
|
|
* Queues the given event to be sent to all registered recipients.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* @param The event to send.
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
2015-08-31 22:25:10 +00:00
|
|
|
* n.b. THIS IS NOW WRAPPED BY THE MicroBitEvent CLASS FOR CONVENIENCE...
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-09-12 10:34:16 +00:00
|
|
|
* MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN,ticks,CREATE_ONLY);
|
2015-08-12 10:53:41 +00:00
|
|
|
* evt.fire();
|
2015-09-12 10:34:16 +00:00
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* //OR YOU CAN DO THIS...
|
2015-08-31 22:25:10 +00:00
|
|
|
* MicroBitEvent evt(id,MICROBIT_BUTTON_EVT_DOWN);
|
2015-08-12 10:53:41 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-08-31 22:25:10 +00:00
|
|
|
void MicroBitMessageBus::send(MicroBitEvent evt)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
2015-08-31 22:25:10 +00:00
|
|
|
// We simply queue processing of the event until we're scheduled in normal thread context.
|
|
|
|
// We do this to avoid the possibility of executing event handler code in IRQ context, which may bring
|
|
|
|
// hidden race conditions to kids code. Queuing all events ensures causal ordering (total ordering in fact).
|
|
|
|
this->queueEvent(evt);
|
|
|
|
}
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
/*
|
|
|
|
* Deliver the given event to all registered event handlers.
|
|
|
|
* Event handlers are called using the invoke() mechanism provided by the fier scheduler
|
|
|
|
* This will attempt to call the event handler directly, but spawn a fiber should that
|
|
|
|
* event handler attempt a blocking operation.
|
|
|
|
* @param evt The event to be delivered.
|
2015-10-31 10:27:38 +00:00
|
|
|
* @param urgent The type of listeners to process (optional). If set to true, only listeners defined as urgent and non-blocking will be processed
|
|
|
|
* otherwise, all other (standard) listeners will be processed.
|
|
|
|
* @return 1 if all matching listeners were processed, 0 if further processing is required.
|
2015-08-31 22:25:10 +00:00
|
|
|
*/
|
2015-10-31 10:27:38 +00:00
|
|
|
int MicroBitMessageBus::process(MicroBitEvent &evt, bool urgent)
|
2015-08-31 22:25:10 +00:00
|
|
|
{
|
|
|
|
MicroBitListener *l;
|
2015-09-18 22:20:44 +00:00
|
|
|
int complete = 1;
|
2015-10-31 10:27:38 +00:00
|
|
|
bool listenerUrgent;
|
2015-08-19 22:35:45 +00:00
|
|
|
|
2015-08-31 22:25:10 +00:00
|
|
|
l = listeners;
|
2015-09-11 15:39:38 +00:00
|
|
|
while (l != NULL)
|
|
|
|
{
|
|
|
|
if((l->id == evt.source || l->id == MICROBIT_ID_ANY) && (l->value == evt.value || l->value == MICROBIT_EVT_ANY))
|
|
|
|
{
|
2015-10-31 10:27:38 +00:00
|
|
|
listenerUrgent = (l->flags & MESSAGE_BUS_LISTENER_IMMEDIATE) == MESSAGE_BUS_LISTENER_IMMEDIATE;
|
|
|
|
if(listenerUrgent == urgent && !(l->flags & MESSAGE_BUS_LISTENER_DELETING))
|
2015-09-18 22:20:44 +00:00
|
|
|
{
|
|
|
|
l->evt = evt;
|
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
// OK, if this handler has regisitered itself as non-blocking, we just execute it directly...
|
2015-09-18 22:20:44 +00:00
|
|
|
// This is normally only done for trusted system components.
|
|
|
|
// Otherwise, we invoke it in a 'fork on block' context, that will automatically create a fiber
|
|
|
|
// should the event handler attempt a blocking operation, but doesn't have the overhead
|
|
|
|
// of creating a fiber needlessly. (cool huh?)
|
|
|
|
if (l->flags & MESSAGE_BUS_LISTENER_NONBLOCKING)
|
|
|
|
async_callback(l);
|
|
|
|
else
|
|
|
|
invoke(async_callback, l);
|
|
|
|
}
|
2015-09-11 15:39:38 +00:00
|
|
|
else
|
2015-09-18 22:20:44 +00:00
|
|
|
{
|
|
|
|
complete = 0;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
2015-09-18 22:20:44 +00:00
|
|
|
return complete;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Register a listener function.
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
|
|
|
* @param id The source of messages to listen for. Events sent from any other IDs will be filtered.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Use MICROBIT_ID_ANY to receive events from all components.
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* @param value The value of messages to listen for. Events with any other values will be filtered.
|
2015-08-12 10:53:41 +00:00
|
|
|
* Use MICROBIT_VALUE_ANY to receive events of any value.
|
|
|
|
*
|
2015-10-25 21:51:33 +00:00
|
|
|
* @param handler The function to call when an event is received.
|
|
|
|
*
|
|
|
|
* @return MICROBIT_OK on success MICROBIT_INVALID_PARAMETER
|
2015-08-12 10:53:41 +00:00
|
|
|
*
|
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-08-12 10:53:41 +00:00
|
|
|
* void onButtonBClick(MicroBitEvent evt)
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
* uBit.MessageBus.listen(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick); // call function when ever a click event is detected.
|
|
|
|
* @endcode
|
|
|
|
*/
|
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
int MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent), uint16_t flags)
|
2015-09-09 23:04:27 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
MicroBitListener *newListener = new MicroBitListener(id, value, handler, flags);
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-10-26 16:37:12 +00:00
|
|
|
if(add(newListener) == MICROBIT_OK)
|
|
|
|
return MICROBIT_OK;
|
|
|
|
|
|
|
|
delete newListener;
|
2015-11-01 18:22:27 +00:00
|
|
|
|
2015-10-26 16:37:12 +00:00
|
|
|
return MICROBIT_NO_RESOURCES;
|
2015-10-25 21:51:33 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
int MicroBitMessageBus::listen(int id, int value, void (*handler)(MicroBitEvent, void*), void* arg, uint16_t flags)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-11 15:39:38 +00:00
|
|
|
MicroBitListener *newListener = new MicroBitListener(id, value, handler, arg, flags);
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-10-26 16:37:12 +00:00
|
|
|
if(add(newListener) == MICROBIT_OK)
|
2015-11-01 18:22:27 +00:00
|
|
|
return MICROBIT_OK;
|
2015-10-26 16:37:12 +00:00
|
|
|
|
|
|
|
delete newListener;
|
2015-11-01 18:22:27 +00:00
|
|
|
|
2015-10-26 16:37:12 +00:00
|
|
|
return MICROBIT_NO_RESOURCES;
|
2015-09-10 11:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a listener function.
|
|
|
|
* Listners are identified by the Event ID, Event VALUE and handler registered using listen().
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
2015-09-10 11:53:39 +00:00
|
|
|
* @param id The Event ID used to register the listener.
|
|
|
|
* @param value The Event VALUE used to register the listener.
|
|
|
|
* @param handler The function used to register the listener.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-09-10 11:53:39 +00:00
|
|
|
* void onButtonBClick()
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
|
2015-09-10 11:53:39 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent))
|
2015-09-10 11:53:39 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-09-10 11:53:39 +00:00
|
|
|
|
|
|
|
MicroBitListener listener(id, value, handler);
|
|
|
|
remove(&listener);
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-09-10 11:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unregister a listener function.
|
|
|
|
* Listners are identified by the Event ID, Event VALUE and handler registered using listen().
|
2016-01-13 16:16:18 +00:00
|
|
|
*
|
2015-09-10 11:53:39 +00:00
|
|
|
* @param id The Event ID used to register the listener.
|
|
|
|
* @param value The Event VALUE used to register the listener.
|
|
|
|
* @param handler The function used to register the listener.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Example:
|
2016-01-13 16:16:18 +00:00
|
|
|
* @code
|
2015-09-10 11:53:39 +00:00
|
|
|
* void onButtonBClick(void *arg)
|
|
|
|
* {
|
|
|
|
* //do something
|
|
|
|
* }
|
|
|
|
*
|
2016-01-13 16:16:18 +00:00
|
|
|
* uBit.MessageBus.ignore(MICROBIT_ID_BUTTON_B, MICROBIT_BUTTON_EVT_CLICK, onButtonBClick);
|
2015-09-10 11:53:39 +00:00
|
|
|
* @endcode
|
|
|
|
*/
|
2015-10-25 21:51:33 +00:00
|
|
|
int MicroBitMessageBus::ignore(int id, int value, void (*handler)(MicroBitEvent, void*))
|
2015-09-10 11:53:39 +00:00
|
|
|
{
|
|
|
|
if (handler == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-09-10 11:53:39 +00:00
|
|
|
|
2015-09-16 19:08:09 +00:00
|
|
|
// The remove function is not comparing the [arg] anyhow.
|
|
|
|
MicroBitListener listener(id, value, handler, NULL);
|
2015-09-10 11:53:39 +00:00
|
|
|
remove(&listener);
|
2015-10-25 21:51:33 +00:00
|
|
|
|
|
|
|
return MICROBIT_OK;
|
2015-09-09 23:04:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the given MicroBitListener to the list of event handlers, unconditionally.
|
|
|
|
* @param listener The MicroBitListener to validate.
|
|
|
|
* @return 1 if the listener is valid, 0 otherwise.
|
|
|
|
*/
|
|
|
|
int MicroBitMessageBus::add(MicroBitListener *newListener)
|
|
|
|
{
|
2015-08-12 10:53:41 +00:00
|
|
|
MicroBitListener *l, *p;
|
2015-09-09 23:04:27 +00:00
|
|
|
int methodCallback;
|
|
|
|
|
|
|
|
//handler can't be NULL!
|
|
|
|
if (newListener == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
l = listeners;
|
|
|
|
|
|
|
|
// Firstly, we treat a listener as an idempotent operation. Ensure we don't already have this handler
|
|
|
|
// registered in a that will already capture these events. If we do, silently ignore.
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// We always check the ID, VALUE and CB_METHOD fields.
|
|
|
|
// If we have a callback to a method, check the cb_method class. Otherwise, the cb function point is sufficient.
|
|
|
|
while (l != NULL)
|
|
|
|
{
|
2015-09-10 11:53:39 +00:00
|
|
|
methodCallback = (newListener->flags & MESSAGE_BUS_LISTENER_METHOD) && (l->flags & MESSAGE_BUS_LISTENER_METHOD);
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
if (l->id == newListener->id && l->value == newListener->value && (methodCallback ? *l->cb_method == *newListener->cb_method : l->cb == newListener->cb))
|
2015-11-01 12:59:52 +00:00
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
// We have a perfect match for this event listener already registered.
|
2015-11-01 12:59:52 +00:00
|
|
|
// If it's marked for deletion, we simply resurrect the listener, and we're done.
|
|
|
|
// Either way, we return an error code, as the *new* listener should be released...
|
|
|
|
if(l->flags & MESSAGE_BUS_LISTENER_DELETING)
|
|
|
|
l->flags &= ~MESSAGE_BUS_LISTENER_DELETING;
|
|
|
|
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_NOT_SUPPORTED;
|
2015-11-01 12:59:52 +00:00
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
l = l->next;
|
|
|
|
}
|
2015-08-12 10:53:41 +00:00
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
// We have a valid, new event handler. Add it to the list.
|
|
|
|
// if listeners is null - we can automatically add this listener to the list at the beginning...
|
2015-08-12 10:53:41 +00:00
|
|
|
if (listeners == NULL)
|
|
|
|
{
|
|
|
|
listeners = newListener;
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2016-01-13 16:16:18 +00:00
|
|
|
// We maintain an ordered list of listeners.
|
2015-09-09 23:04:27 +00:00
|
|
|
// The chain is held stictly in increasing order of ID (first level), then value code (second level).
|
2015-08-12 10:53:41 +00:00
|
|
|
// Find the correct point in the chain for this event.
|
2015-09-09 23:04:27 +00:00
|
|
|
// Adding a listener is a rare occurance, so we just walk the list...
|
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
p = listeners;
|
|
|
|
l = listeners;
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
while (l != NULL && l->id < newListener->id)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
2015-09-09 23:04:27 +00:00
|
|
|
while (l != NULL && l->id == newListener->id && l->value < newListener->value)
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
p = l;
|
|
|
|
l = l->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
//add at front of list
|
2015-09-09 23:04:27 +00:00
|
|
|
if (p == listeners && (newListener->id < p->id || (p->id == newListener->id && p->value > newListener->value)))
|
2015-08-12 10:53:41 +00:00
|
|
|
{
|
|
|
|
newListener->next = p;
|
|
|
|
|
|
|
|
//this new listener is now the front!
|
|
|
|
listeners = newListener;
|
|
|
|
}
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-08-12 10:53:41 +00:00
|
|
|
//add after p
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newListener->next = p->next;
|
|
|
|
p->next = newListener;
|
|
|
|
}
|
2015-09-09 23:04:27 +00:00
|
|
|
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_OK;
|
2015-08-12 10:53:41 +00:00
|
|
|
}
|
|
|
|
|
2015-09-10 11:53:39 +00:00
|
|
|
/**
|
2015-10-25 21:51:33 +00:00
|
|
|
* Remove the given MicroBitListener from the list of event handlers.
|
|
|
|
* @param listener The MicroBitListener to remove.
|
|
|
|
* @return MICROBIT_OK if the listener is valid, MICROBIT_INVALID_PARAMETER otherwise.
|
|
|
|
*/
|
2015-09-10 11:53:39 +00:00
|
|
|
int MicroBitMessageBus::remove(MicroBitListener *listener)
|
|
|
|
{
|
2015-10-29 00:08:33 +00:00
|
|
|
MicroBitListener *l;
|
2015-09-10 11:53:39 +00:00
|
|
|
int removed = 0;
|
|
|
|
|
|
|
|
//handler can't be NULL!
|
|
|
|
if (listener == NULL)
|
2015-10-25 21:51:33 +00:00
|
|
|
return MICROBIT_INVALID_PARAMETER;
|
2015-09-10 11:53:39 +00:00
|
|
|
|
|
|
|
l = listeners;
|
|
|
|
|
|
|
|
// Walk this list of event handlers. Delete any that match the given listener.
|
|
|
|
while (l != NULL)
|
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
if ((listener->flags & MESSAGE_BUS_LISTENER_METHOD) == (l->flags & MESSAGE_BUS_LISTENER_METHOD))
|
2015-09-10 11:53:39 +00:00
|
|
|
{
|
2016-01-13 16:16:18 +00:00
|
|
|
if(((listener->flags & MESSAGE_BUS_LISTENER_METHOD) && (*l->cb_method == *listener->cb_method)) ||
|
2015-10-29 00:08:33 +00:00
|
|
|
((!(listener->flags & MESSAGE_BUS_LISTENER_METHOD) && l->cb == listener->cb)))
|
2015-09-10 11:53:39 +00:00
|
|
|
{
|
2015-09-28 20:40:44 +00:00
|
|
|
if ((listener->id == MICROBIT_ID_ANY || listener->id == l->id) && (listener->value == MICROBIT_EVT_ANY || listener->value == l->value))
|
|
|
|
{
|
2015-10-29 00:08:33 +00:00
|
|
|
// Found a match. mark this to be removed from the list.
|
|
|
|
l->flags |= MESSAGE_BUS_LISTENER_DELETING;
|
2015-09-28 20:40:44 +00:00
|
|
|
removed++;
|
|
|
|
}
|
2015-09-10 11:53:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
l = l->next;
|
|