Difference between revisions of "Abi"

From PaparazziUAV
Jump to navigation Jump to search
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 EXTERN
  #define ABI_EXTERN
  #else
  #else
  #define EXTERN extern
  #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
  cb(a,b);
    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 "abi_messages.h"
  #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 "abi_messages.h"
  #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 "abi_messages.h"
  #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 !
A dev branch of ABI with a test program is in the github repository git://github.com/gautierhattenberger/paparazzi.git, branch abi

Revision as of 10: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 !