Difference between revisions of "ABI"

From PaparazziUAV
Jump to navigation Jump to search
(Redirected page to Abi)
 
(move from Abi page)
Line 1: Line 1:
#REDIRECT [[Abi]]
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 !

Revision as of 07:11, 26 November 2014

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 !