2010-03-04 04:04:39 +01:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
*
|
|
|
|
* @file eventdispatcher.c
|
|
|
|
* @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
|
2010-03-10 10:12:31 +01:00
|
|
|
* @brief Event dispatcher, distributes object events as callbacks. Alternative
|
|
|
|
* to using tasks and queues. All callbacks are invoked from the event task.
|
2010-03-04 04:04:39 +01:00
|
|
|
* @see The GNU Public License (GPL) Version 3
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
|
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
|
|
* for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
2010-03-06 07:17:17 +01:00
|
|
|
#include "openpilot.h"
|
2010-03-04 04:04:39 +01:00
|
|
|
|
|
|
|
// Private constants
|
2010-04-04 04:11:51 +02:00
|
|
|
#define MAX_QUEUE_SIZE 20
|
|
|
|
#define STACK_SIZE configMINIMAL_STACK_SIZE
|
2010-04-24 06:05:39 +02:00
|
|
|
#define TASK_PRIORITY (tskIDLE_PRIORITY + 3)
|
2010-03-04 04:04:39 +01:00
|
|
|
#define MAX_UPDATE_PERIOD_MS 1000
|
|
|
|
|
|
|
|
// Private types
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event callback information
|
|
|
|
*/
|
|
|
|
typedef struct {
|
|
|
|
UAVObjEvent ev; /** The actual event */
|
2010-04-05 02:59:51 +02:00
|
|
|
UAVObjEventCallback cb; /** The callback function, or zero if none */
|
|
|
|
xQueueHandle queue; /** The queue or zero if none */
|
2010-03-04 04:04:39 +01:00
|
|
|
} EventCallbackInfo;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of object properties that are needed for the periodic updates.
|
|
|
|
*/
|
|
|
|
struct PeriodicObjectListStruct {
|
|
|
|
EventCallbackInfo evInfo; /** Event callback information */
|
|
|
|
int32_t updatePeriodMs; /** Update period in ms or 0 if no periodic updates are needed */
|
|
|
|
int32_t timeToNextUpdateMs; /** Time delay to the next update */
|
|
|
|
struct PeriodicObjectListStruct* next; /** Needed by linked list library (utlist.h) */
|
|
|
|
};
|
|
|
|
typedef struct PeriodicObjectListStruct PeriodicObjectList;
|
|
|
|
|
|
|
|
// Private variables
|
2010-04-04 04:11:51 +02:00
|
|
|
static PeriodicObjectList* objList;
|
|
|
|
static xQueueHandle queue;
|
|
|
|
static xTaskHandle eventTaskHandle;
|
|
|
|
static xSemaphoreHandle mutex;
|
2010-03-04 04:04:39 +01:00
|
|
|
|
|
|
|
// Private functions
|
2010-04-04 04:11:51 +02:00
|
|
|
static int32_t processPeriodicUpdates();
|
|
|
|
static void eventTask();
|
2010-04-05 02:59:51 +02:00
|
|
|
static int32_t eventPeriodicCreate(UAVObjEvent* ev, UAVObjEventCallback cb, xQueueHandle queue, int32_t periodMs);
|
|
|
|
static int32_t eventPeriodicUpdate(UAVObjEvent* ev, UAVObjEventCallback cb, xQueueHandle queue, int32_t periodMs);
|
|
|
|
|
2010-03-04 04:04:39 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the dispatcher
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
|
|
|
int32_t EventDispatcherInitialize()
|
|
|
|
{
|
|
|
|
// Initialize list
|
|
|
|
objList = NULL;
|
|
|
|
|
|
|
|
// Create mutex
|
|
|
|
mutex = xSemaphoreCreateRecursiveMutex();
|
|
|
|
if (mutex == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
// Create event queue
|
2010-04-04 04:11:51 +02:00
|
|
|
queue = xQueueCreate(MAX_QUEUE_SIZE, sizeof(EventCallbackInfo));
|
2010-03-04 04:04:39 +01:00
|
|
|
|
|
|
|
// Create task
|
|
|
|
xTaskCreate( eventTask, (signed char*)"Event", STACK_SIZE, NULL, TASK_PRIORITY, &eventTaskHandle );
|
|
|
|
|
|
|
|
// Done
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatch an event by invoking the supplied callback. The function
|
|
|
|
* returns imidiatelly, the callback is invoked from the event task.
|
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] cb The callback function
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
2010-04-05 02:59:51 +02:00
|
|
|
int32_t EventCallbackDispatch(UAVObjEvent* ev, UAVObjEventCallback cb)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
EventCallbackInfo evInfo;
|
|
|
|
// Initialize event callback information
|
|
|
|
memcpy(&evInfo.ev, ev, sizeof(UAVObjEvent));
|
|
|
|
evInfo.cb = cb;
|
2010-04-05 02:59:51 +02:00
|
|
|
evInfo.queue = 0;
|
2010-03-04 04:04:39 +01:00
|
|
|
// Push to queue
|
|
|
|
return xQueueSend(queue, &evInfo, 0); // will not block if queue is full
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2010-04-05 02:59:51 +02:00
|
|
|
* Dispatch an event at periodic intervals.
|
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] cb The callback to be invoked
|
|
|
|
* \param[in] periodMs The period the event is generated
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
|
|
|
int32_t EventPeriodicCallbackCreate(UAVObjEvent* ev, UAVObjEventCallback cb, int32_t periodMs)
|
|
|
|
{
|
|
|
|
return eventPeriodicCreate(ev, cb, 0, periodMs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the period of a periodic event.
|
2010-03-04 04:04:39 +01:00
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] cb The callback to be invoked
|
|
|
|
* \param[in] periodMs The period the event is generated
|
2010-04-05 02:59:51 +02:00
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
|
|
|
int32_t EventPeriodicCallbackUpdate(UAVObjEvent* ev, UAVObjEventCallback cb, int32_t periodMs)
|
|
|
|
{
|
|
|
|
return eventPeriodicUpdate(ev, cb, 0, periodMs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatch an event at periodic intervals.
|
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] queue The queue that the event will be pushed in
|
|
|
|
* \param[in] periodMs The period the event is generated
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
|
|
|
int32_t EventPeriodicQueueCreate(UAVObjEvent* ev, xQueueHandle queue, int32_t periodMs)
|
|
|
|
{
|
|
|
|
return eventPeriodicCreate(ev, 0, queue, periodMs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the period of a periodic event.
|
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] queue The queue
|
|
|
|
* \param[in] periodMs The period the event is generated
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
|
|
|
int32_t EventPeriodicQueueUpdate(UAVObjEvent* ev, xQueueHandle queue, int32_t periodMs)
|
|
|
|
{
|
|
|
|
return eventPeriodicUpdate(ev, 0, queue, periodMs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatch an event through a callback at periodic intervals.
|
|
|
|
* \param[in] ev The event to be dispatched
|
|
|
|
* \param[in] cb The callback to be invoked or zero if none
|
|
|
|
* \param[in] queue The queue or zero if none
|
|
|
|
* \param[in] periodMs The period the event is generated
|
2010-03-04 04:04:39 +01:00
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
2010-04-05 02:59:51 +02:00
|
|
|
static int32_t eventPeriodicCreate(UAVObjEvent* ev, UAVObjEventCallback cb, xQueueHandle queue, int32_t periodMs)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
PeriodicObjectList* objEntry;
|
|
|
|
// Get lock
|
|
|
|
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
|
|
|
|
// Check that the object is not already connected
|
|
|
|
LL_FOREACH(objList, objEntry)
|
|
|
|
{
|
2010-04-05 02:59:51 +02:00
|
|
|
if (objEntry->evInfo.cb == cb &&
|
|
|
|
objEntry->evInfo.queue == queue &&
|
2010-04-15 04:15:46 +02:00
|
|
|
objEntry->evInfo.ev.obj == ev->obj &&
|
|
|
|
objEntry->evInfo.ev.instId == ev->instId &&
|
|
|
|
objEntry->evInfo.ev.event == ev->event)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
// Already registered, do nothing
|
|
|
|
xSemaphoreGiveRecursive(mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Create handle
|
2010-03-21 15:19:38 +01:00
|
|
|
objEntry = (PeriodicObjectList*)pvPortMalloc(sizeof(PeriodicObjectList));
|
2010-03-04 04:04:39 +01:00
|
|
|
if (objEntry == NULL) return -1;
|
2010-04-15 04:15:46 +02:00
|
|
|
objEntry->evInfo.ev.obj = ev->obj;
|
|
|
|
objEntry->evInfo.ev.instId = ev->instId;
|
|
|
|
objEntry->evInfo.ev.event = ev->event;
|
2010-04-04 04:11:51 +02:00
|
|
|
objEntry->evInfo.cb = cb;
|
2010-04-05 02:59:51 +02:00
|
|
|
objEntry->evInfo.queue = queue;
|
2010-03-04 04:04:39 +01:00
|
|
|
objEntry->updatePeriodMs = periodMs;
|
|
|
|
objEntry->timeToNextUpdateMs = 0;
|
|
|
|
// Add to list
|
|
|
|
LL_APPEND(objList, objEntry);
|
|
|
|
// Release lock
|
|
|
|
xSemaphoreGiveRecursive(mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update the period of a periodic event.
|
|
|
|
* \param[in] ev The event to be dispatched
|
2010-04-05 02:59:51 +02:00
|
|
|
* \param[in] cb The callback to be invoked or zero if none
|
|
|
|
* \param[in] queue The queue or zero if none
|
2010-03-04 04:04:39 +01:00
|
|
|
* \param[in] periodMs The period the event is generated
|
|
|
|
* \return Success (0), failure (-1)
|
|
|
|
*/
|
2010-04-05 02:59:51 +02:00
|
|
|
static int32_t eventPeriodicUpdate(UAVObjEvent* ev, UAVObjEventCallback cb, xQueueHandle queue, int32_t periodMs)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
PeriodicObjectList* objEntry;
|
|
|
|
// Get lock
|
|
|
|
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
|
|
|
|
// Find object
|
|
|
|
LL_FOREACH(objList, objEntry)
|
|
|
|
{
|
2010-04-05 02:59:51 +02:00
|
|
|
if (objEntry->evInfo.cb == cb &&
|
|
|
|
objEntry->evInfo.queue == queue &&
|
2010-04-15 04:15:46 +02:00
|
|
|
objEntry->evInfo.ev.obj == ev->obj &&
|
|
|
|
objEntry->evInfo.ev.instId == ev->instId &&
|
|
|
|
objEntry->evInfo.ev.event == ev->event)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
// Object found, update period
|
|
|
|
objEntry->updatePeriodMs = periodMs;
|
|
|
|
objEntry->timeToNextUpdateMs = 0;
|
|
|
|
// Release lock
|
|
|
|
xSemaphoreGiveRecursive(mutex);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If this point is reached the object was not found
|
|
|
|
xSemaphoreGiveRecursive(mutex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event task, responsible of invoking callbacks.
|
|
|
|
*/
|
2010-04-04 04:11:51 +02:00
|
|
|
static void eventTask()
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
int32_t timeToNextUpdateMs;
|
|
|
|
int32_t delayMs;
|
|
|
|
EventCallbackInfo evInfo;
|
|
|
|
|
|
|
|
// Initialize time
|
|
|
|
timeToNextUpdateMs = xTaskGetTickCount()*portTICK_RATE_MS;
|
|
|
|
|
|
|
|
// Loop forever
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
// Calculate delay time
|
|
|
|
delayMs = timeToNextUpdateMs-(xTaskGetTickCount()*portTICK_RATE_MS);
|
|
|
|
if (delayMs < 0)
|
|
|
|
{
|
|
|
|
delayMs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for queue message
|
|
|
|
if ( xQueueReceive(queue, &evInfo, delayMs/portTICK_RATE_MS) == pdTRUE )
|
|
|
|
{
|
2010-04-05 02:59:51 +02:00
|
|
|
// Invoke callback, if one
|
|
|
|
if ( evInfo.cb != 0)
|
|
|
|
{
|
|
|
|
evInfo.cb(&evInfo.ev); // the function is expected to copy the event information
|
|
|
|
}
|
2010-03-04 04:04:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Process periodic updates
|
|
|
|
if ((xTaskGetTickCount()*portTICK_RATE_MS) >= timeToNextUpdateMs )
|
|
|
|
{
|
|
|
|
timeToNextUpdateMs = processPeriodicUpdates();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Handle periodic updates for all objects.
|
|
|
|
* \return The system time until the next update (in ms) or -1 if failed
|
|
|
|
*/
|
2010-04-04 04:11:51 +02:00
|
|
|
static int32_t processPeriodicUpdates()
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
PeriodicObjectList* objEntry;
|
2010-04-04 04:11:51 +02:00
|
|
|
int32_t timeNow;
|
|
|
|
int32_t timeToNextUpdate;
|
2010-03-04 04:04:39 +01:00
|
|
|
|
|
|
|
// Get lock
|
|
|
|
xSemaphoreTakeRecursive(mutex, portMAX_DELAY);
|
|
|
|
|
|
|
|
// Iterate through each object and update its timer, if zero then transmit object.
|
|
|
|
// Also calculate smallest delay to next update.
|
2010-04-04 04:11:51 +02:00
|
|
|
timeToNextUpdate = xTaskGetTickCount()*portTICK_RATE_MS + MAX_UPDATE_PERIOD_MS;
|
2010-03-04 04:04:39 +01:00
|
|
|
LL_FOREACH(objList, objEntry)
|
|
|
|
{
|
|
|
|
// If object is configured for periodic updates
|
|
|
|
if (objEntry->updatePeriodMs > 0)
|
|
|
|
{
|
|
|
|
// Check if time for the next update
|
2010-04-04 04:11:51 +02:00
|
|
|
timeNow = xTaskGetTickCount()*portTICK_RATE_MS;
|
|
|
|
if (objEntry->timeToNextUpdateMs <= timeNow)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
|
|
|
// Reset timer
|
2010-04-04 04:11:51 +02:00
|
|
|
objEntry->timeToNextUpdateMs = timeNow + objEntry->updatePeriodMs;
|
2010-04-05 02:59:51 +02:00
|
|
|
// Invoke callback, if one
|
|
|
|
if ( objEntry->evInfo.cb != 0)
|
|
|
|
{
|
|
|
|
objEntry->evInfo.cb(&objEntry->evInfo.ev); // the function is expected to copy the event information
|
|
|
|
}
|
|
|
|
// Push event to queue, if one
|
|
|
|
if ( objEntry->evInfo.queue != 0)
|
|
|
|
{
|
|
|
|
xQueueSend(objEntry->evInfo.queue, &objEntry->evInfo.ev, 0); // do not block if queue is full
|
|
|
|
}
|
2010-03-04 04:04:39 +01:00
|
|
|
}
|
|
|
|
// Update minimum delay
|
2010-04-04 04:11:51 +02:00
|
|
|
if (objEntry->timeToNextUpdateMs < timeToNextUpdate)
|
2010-03-04 04:04:39 +01:00
|
|
|
{
|
2010-04-04 04:11:51 +02:00
|
|
|
timeToNextUpdate = objEntry->timeToNextUpdateMs;
|
2010-03-04 04:04:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Done
|
|
|
|
xSemaphoreGiveRecursive(mutex);
|
2010-04-04 04:11:51 +02:00
|
|
|
return timeToNextUpdate;
|
2010-03-04 04:04:39 +01:00
|
|
|
}
|
|
|
|
|