mirror of
https://github.com/paparazzi/paparazzi.git
synced 2026-05-09 22:49:53 +08:00
[doc] Adds ABI page to sphinx doc. (#2628)
Co-authored-by: Gautier Hattenberger <gautier.hattenberger@enac.fr>
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
.. developer_guide abi
|
||||
|
||||
=============
|
||||
ABI messaging
|
||||
=============
|
||||
|
||||
Paparazzi use a custom publish/subscribe middleware to exchange data between software components named ``ABI``.
|
||||
|
||||
.. note:: This middleware was named *ABI* for *AirBorne 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.
|
||||
|
||||
.. warning:: This middleware is **NOT** thread safe. Threads are not widely used in Paparazzi but remember this if you use them.
|
||||
|
||||
|
||||
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 valid C type or pointer.
|
||||
|
||||
.. code-block:: xml
|
||||
|
||||
<class name="airborne">
|
||||
<message name="DATA" id="0">
|
||||
<field name="a" type="float"/>
|
||||
<field name="b" type="struct bla"/>
|
||||
<field name="c" type="struct foo *"/>
|
||||
</message>
|
||||
...
|
||||
</class>
|
||||
|
||||
Airborne code for subscriber
|
||||
----------------------------
|
||||
|
||||
Include header and declare an ``abi_event`` as a global ``static`` variable (but **not** ``const``). Write the callback function with the proper prototype, matching the message.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#include "subsystems/abi.h"
|
||||
|
||||
static abi_event ev;
|
||||
|
||||
void data_cb(uint8_t sender_id, float a, struct bla b, struct foo * c) {
|
||||
// do something here
|
||||
}
|
||||
|
||||
In the initialization function (or later) call the binding function for the message you want to receive.
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
AbiBindMsgDATA(ABI_BROADCAST, &ev, data_cb);
|
||||
|
||||
|
||||
The first parameter is the sender ID you want to receive the message from.
|
||||
|
||||
* ``ABI_BROADCAST`` is used to receive messages from all senders.
|
||||
* ``ABI_DISABLE`` disable the callback (it will never be called).
|
||||
* Senders IDs can be found in the file ``sw/airborne/subsystems/abi_sender_ids.h``
|
||||
|
||||
The second parameter is a pointer to the global ``abi_event`` you declared. This variable **can't** be reused for another bind. You must declare one abi_event per bind.
|
||||
|
||||
The last parameter is your callback.
|
||||
|
||||
.. note:: When creating your module with the Module Creator (in the Paparazzi Center : Tools -> Module Creator), you can add subcriptions to ABI messages.
|
||||
|
||||
Airborne code for publisher
|
||||
---------------------------
|
||||
|
||||
Include header, then call the send function with the appropriate parameters
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
float var = 2.;
|
||||
struct bla s;
|
||||
struct foo f;
|
||||
AbiSendMsgDATA(SENDER_ID, var, s, &f);
|
||||
|
||||
Replace ``SENDER_ID`` by your sender ID defined in ``sw/airborne/subsystems/abi_sender_ids.h``.
|
||||
|
||||
Your sender ID identifier should be constructed as the concatenation of the name of the message and the name of your module, suffixed with ``_ID``.
|
||||
|
||||
.. admonition:: example
|
||||
|
||||
A good sender ID for a module ``toto`` sending the message ``DATA`` may be ``DATA_TOTO_ID``
|
||||
|
||||
.. warning::
|
||||
|
||||
The values 0 and 255 are reserved for ``ABI_DISABLE`` and ``ABI_BROADCAST`` and thus shall not be used.
|
||||
|
||||
You must also avoid using an ID already used to send the same message type.
|
||||
|
||||
|
||||
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.
|
||||
|
||||
The code is generated by ``/sw/tools/generators/gen_abi.xml``.
|
||||
|
||||
In depth
|
||||
--------
|
||||
|
||||
Generated code
|
||||
______________
|
||||
|
||||
Here is the code of ``sw/airborne/subsystems/abi_common.h``:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
/* Include here headers with structure definition you may want to use with ABI
|
||||
* Ex: '#include "subsystems/gps.h"' in order to use the GpsState structure
|
||||
*/
|
||||
#include "subsystems/gps.h"
|
||||
|
||||
#include "subsystems/abi_sender_ids.h"
|
||||
|
||||
#ifdef ABI_C
|
||||
#define ABI_EXTERN
|
||||
#else
|
||||
#define ABI_EXTERN extern
|
||||
#endif
|
||||
|
||||
/** Generic callback definition */
|
||||
typedef void (*abi_callback)(void);
|
||||
|
||||
// ABI Broadcast address.
|
||||
#define ABI_BROADCAST 255
|
||||
|
||||
// ABI disable address
|
||||
#define ABI_DISABLE 0
|
||||
|
||||
/** Event structure to store callbacks in a linked list */
|
||||
struct abi_struct {
|
||||
uint8_t id;
|
||||
abi_callback cb;
|
||||
struct abi_struct *next;
|
||||
};
|
||||
typedef struct abi_struct abi_event;
|
||||
|
||||
/** Macros for linked list */
|
||||
#define ABI_FOREACH(head,el) for(el=head; el; el=el->next)
|
||||
#define ABI_PREPEND(head,add) { (add)->next = head; head = add; }
|
||||
|
||||
|
||||
|
||||
|
||||
The generated code in ``var/include/abi_messages.h`` for the message defined above will look like this:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
// Code generated in var/include/abi_messages.h
|
||||
#include "subsystems/abi_common.h
|
||||
// Message IDs
|
||||
#define ABI_DATA_ID 0
|
||||
|
||||
// Array and linked list
|
||||
#define ABI_MESSAGE_NB <highest id of the messages in airborne class + 1>
|
||||
ABI_EXTERN abi_event* abi_queues[ABI_MESSAGE_NB]; // Magic trick to avoid generating .c file
|
||||
|
||||
// Callbacks
|
||||
typedef void (*abi_callbackDATA)(uint8_t sender_id, float a, struct bla b, struct foo * c); // Specific callback for DATA message (arguments are const to prevent modifying them)
|
||||
|
||||
// Bind and Send for each messages
|
||||
static inline void AbiBindMsgDATA(uint8_t sender_id, abi_event * ev, abi_callbackDATA cb) {
|
||||
if (abi_queues[ABI_BARO_ABS_ID] == ev) return;
|
||||
ev->id = sender_id;
|
||||
ev->cb = (abi_callback)cb;
|
||||
ABI_PREPEND(abi_queues[ABI_BARO_ABS_ID],ev);
|
||||
}
|
||||
|
||||
static inline void AbiSendMsgDATA(uint8_t sender_id, float a, struct bla b, struct foo * c) {
|
||||
// Call all callback functions
|
||||
abi_event* e;
|
||||
ABI_FOREACH(abi_queues[ABI_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, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Using ABI in custom code
|
||||
________________________
|
||||
|
||||
|
||||
If you want to use ABI outside of one of the Paparazzi firmwares (``rotorcraft``, ``rover`` or ``fixedwing``), this code snippet should be written and called only once in a .c file (most probably your ``main.c``) to enable ABI:
|
||||
|
||||
.. code-block:: C
|
||||
|
||||
#define ABI_C 1
|
||||
#include "subsystems/abi.h"
|
||||
|
||||
@@ -12,4 +12,5 @@ TBD
|
||||
system_overview
|
||||
communication
|
||||
modules
|
||||
abi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user