ABI

From PaparazziUAV
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.

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 described in conf/abi.xml analoguous to 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/modules/core/abi_common.h (sw/airborne/modules/core/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 "modules/core/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 "modules/core/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 "modules/core/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 "modules/core/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 !