Difference between revisions of "Abi"
Line 16: | Line 16: | ||
<message name="DATA" id="0"> | <message name="DATA" id="0"> | ||
<field name="a" type="float"/> | <field name="a" type="float"/> | ||
<field name="b" type="struct bla | <field name="b" type="struct bla"/> | ||
</message> | </message> | ||
... | ... | ||
Line 23: | Line 23: | ||
== Code generation == | == Code generation == | ||
The generated code will be in var/include/abi_messages.h and include some structure definition from sw/airborne/subsystems/abi_common.h. Bind and Send functions are generated, as well as callback type definition. A linked list is used to store the binded callbacks for each message. The head of the list is in an array to allow a fast access. | The generated code will be in var/include/abi_messages.h and include some structure definition from sw/airborne/subsystems/abi_common.h (sw/airborne/subsystems/abi.h is a convenience header that only includes var/include/abi_messages.h). Bind and Send functions are generated, as well as callback type definition. A linked list is used to store the binded callbacks for each message. The head of the list is in an array to allow a fast access. | ||
// Code in abi_common.h | // Code in abi_common.h | ||
Line 32: | Line 32: | ||
struct _abi_event_struct { | struct _abi_event_struct { | ||
uint8_t id; | |||
abi_callback cb; | abi_callback cb; | ||
struct _abi_event_struct * next; | struct _abi_event_struct * next; | ||
Line 38: | Line 39: | ||
#ifdef ABI_C | #ifdef ABI_C | ||
#define | #define ABI_EXTERN | ||
#else | #else | ||
#define | #define ABI_EXTERN extern | ||
#endif | #endif | ||
Line 53: | Line 54: | ||
// Callbacks | // Callbacks | ||
typedef void (*abi_callbackDATA)(const float a, const struct bla * b); // Specific callback for DATA message (arguments are const to prevent modifying them) | typedef void (*abi_callbackDATA)(uint8_t sender_id, const float * a, const struct bla * b); // Specific callback for DATA message (arguments are const to prevent modifying them) | ||
// Array and linked list | // Array and linked list | ||
Line 61: | Line 62: | ||
// Bind and Send for each messages | // Bind and Send for each messages | ||
static inline void AbiBindMsgDATA(abi_event * ev, abi_callbackDATA cb) { | static inline void AbiBindMsgDATA(uint8_t sender_id, abi_event * ev, abi_callbackDATA cb) { | ||
ev->id = sender_id; // Store sender ID | |||
ev->cb = (abi_callback)cb; // Store callback | ev->cb = (abi_callback)cb; // Store callback | ||
ABI_PREPEND(abi_queues[DATA_ID],ev); // add to the head of the list (because I'm lazy) | ABI_PREPEND(abi_queues[DATA_ID],ev); // add to the head of the list (because I'm lazy) | ||
} | } | ||
static inline void AbiSendMsgDATA(float a, struct bla * b) { | static inline void AbiSendMsgDATA(uint8_t sender_id, const float * a, const struct bla * b) { | ||
// Call all callback functions | // Call all callback functions | ||
abi_event* e; | abi_event* e; | ||
ABI_FOREACH(abi_queues[DATA_ID],e) { | ABI_FOREACH(abi_queues[DATA_ID],e) { | ||
abi_callbackDATA cb = (abi_callbackDATA)(e->cb); // C black magic | if (e->id == ABI_BROADCAST || e->id == sender_id) { // call function only if selected source or broadcast address | ||
abi_callbackDATA cb = (abi_callbackDATA)(e->cb); // C black magic | |||
cb(sender_id, a, b); | |||
} | |||
} | } | ||
} | } | ||
Line 81: | Line 85: | ||
#define ABI_C 1 | #define ABI_C 1 | ||
#include " | #include "subsystems/abi.h" | ||
== Airborne code for subscriber == | == Airborne code for subscriber == | ||
Line 87: | Line 91: | ||
Include header and declare an abi_event as a global variable. Write the callback function with the proper prototype. | Include header and declare an abi_event as a global variable. Write the callback function with the proper prototype. | ||
#include " | #include "subsystems/abi.h" | ||
abi_event ev; | abi_event ev; | ||
void data_cb(const float a, const struct bla * b) { | void data_cb(uint8_t sender_id, const float * a, const struct bla * b) { | ||
// do something here | // do something here | ||
} | } | ||
In the initialization function (or later) call the binding functions for the message you want to receive. | In the initialization function (or later) call the binding functions for the message you want to receive. ABI_BROADCAST is a macro equals to 0 and is used as a broadcast address, which means that input values will be received from all sources for this message. | ||
AbiBindMsgDATA(&ev, data_cb); | AbiBindMsgDATA(ABI_BROADCAST, &ev, data_cb); | ||
== Airborne code for publisher == | == Airborne code for publisher == | ||
Line 103: | Line 107: | ||
Include header of course. | Include header of course. | ||
#include " | #include "subsystems/abi.h" | ||
In any function, call the send function. | In any function, call the send function. Sender id 0 is reserved (broadcast address). | ||
#define SENDER_ID 1 | |||
float var = 2.; | float var = 2.; | ||
struct bla s; | struct bla s; | ||
AbiSendMsgDATA(var,&s); | AbiSendMsgDATA(SENDER_ID, &var, &s); | ||
That's it ! | That's it ! | ||
Revision as of 09:09, 2 September 2013
This page presents some idea for an embedded middleware named ABI based on publish/subscribe like Ivy.
General idea
- Give an easy way to allow software components to exchange data with minimum delay nor execution overhead.
- The components don't need to know each other, they only need the message format.
- Each subscriber set a callback function that will be called when new data are sent.
Implementation
Message definition
The messages are describe in conf/messages.xml like any other messages in paparazzi. Name are unique, IDs starts from 0. "type" field should be a real C type. Field name might be useless.
<class name="airborne"> <message name="DATA" id="0"> <field name="a" type="float"/> <field name="b" type="struct bla"/> </message> ... </class>
Code generation
The generated code will be in var/include/abi_messages.h and include some structure definition from sw/airborne/subsystems/abi_common.h (sw/airborne/subsystems/abi.h is a convenience header that only includes var/include/abi_messages.h). Bind and Send functions are generated, as well as callback type definition. A linked list is used to store the binded callbacks for each message. The head of the list is in an array to allow a fast access.
// Code in abi_common.h // Here, include header custom structures you may want to use in ABI messages // Default is to include pprz algebra libraries typedef void (*abi_callback)(void); // Generic callback definition struct _abi_event_struct { uint8_t id; abi_callback cb; struct _abi_event_struct * next; }; typedef struct _abi_event_struct abi_event; #ifdef ABI_C #define ABI_EXTERN #else #define ABI_EXTERN extern #endif #define ABI_FOREACH(head,el) for(el=head; el; el=el->next) #define ABI_PREPEND(head,new) { (add)->next = head; head = add; }
// Code generated in var/include/abi_messages.h #include "subsystems/abi_common.h // Message IDs #define DATA_ID 0 // Callbacks typedef void (*abi_callbackDATA)(uint8_t sender_id, const float * a, const struct bla * b); // Specific callback for DATA message (arguments are const to prevent modifying them) // Array and linked list #define ABI_MESSAGE_NB <highest id of the messages in airborne class + 1> EXTERN abi_event abi_queues[ABI_MESSAGE_NB]; // Magic trick to avoid generating .c file // Bind and Send for each messages static inline void AbiBindMsgDATA(uint8_t sender_id, abi_event * ev, abi_callbackDATA cb) { ev->id = sender_id; // Store sender ID ev->cb = (abi_callback)cb; // Store callback ABI_PREPEND(abi_queues[DATA_ID],ev); // add to the head of the list (because I'm lazy) } static inline void AbiSendMsgDATA(uint8_t sender_id, const float * a, const struct bla * b) { // Call all callback functions abi_event* e; ABI_FOREACH(abi_queues[DATA_ID],e) { if (e->id == ABI_BROADCAST || e->id == sender_id) { // call function only if selected source or broadcast address abi_callbackDATA cb = (abi_callbackDATA)(e->cb); // C black magic cb(sender_id, a, b); } } }
Airborne code for main AP
This part of the code should be written and called only once in a .c file (like main.c, but it can be somewhere else)
#define ABI_C 1 #include "subsystems/abi.h"
Airborne code for subscriber
Include header and declare an abi_event as a global variable. Write the callback function with the proper prototype.
#include "subsystems/abi.h" abi_event ev; void data_cb(uint8_t sender_id, const float * a, const struct bla * b) { // do something here }
In the initialization function (or later) call the binding functions for the message you want to receive. ABI_BROADCAST is a macro equals to 0 and is used as a broadcast address, which means that input values will be received from all sources for this message.
AbiBindMsgDATA(ABI_BROADCAST, &ev, data_cb);
Airborne code for publisher
Include header of course.
#include "subsystems/abi.h"
In any function, call the send function. Sender id 0 is reserved (broadcast address).
#define SENDER_ID 1 float var = 2.; struct bla s; AbiSendMsgDATA(SENDER_ID, &var, &s);
That's it !