Services#

Description#

../_images/mission_service.png

Services are standalone processes dedicated to a specific Flight mission.

They produce outputs that are used by Flight supervisor states machine or Guidance modes. They have access to all data exchanges (Telemetry, Messages, Video) and all Linux interfaces (Storage, 4G, Wi-fi, USB).

The typical use cases are: computer vision, sensors acquisition, data streaming.

Important

A Service can be written either in C/C++ or in Python. Language must be chosen carefully depending on the Flight mission and required processing. C/C++ has to be chosen when real time is a priority.

A Service is generally implemented using a single event loop (single threaded program) that runs indefinitely. You can use a set of components already present in the drone (see Framework) and you can add your own custom libraries.

Services run as a non privileged linux user.

../_images/14-architecture_linux.png

How to write a service#

C#

Here is a typical skeleton of C service:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <libpomp.h>
#define ULOG_TAG service
#include <ulog.h>
ULOG_DECLARE_TAG(ULOG_TAG);

struct context {
    struct pomp_loop *loop;
    struct pomp_timer *timer;
}

static struct context s_ctx;
static volatile int stop;

static void sighandler(int signum)
{
    ULOGI("signal %d (%s) received", signum, strsignal(signum));

    // ask the event loop to exit
    stop = 1;
    pomp_loop_wakeup(s_ctx.loop);
}

static void timer_cb(struct pomp_timer *timer, void *userdata)
{
    struct ctx *ctx = userdata;

    ULOGI("Hello world");
}

int main(int argc, char *argv[])
{
    // create a new event loop and a timer
    s_ctx->loop = pomp_loop_new();
    s_ctx->timer = pomp_timer_new(s_ctx->loop, timer_cb, s_ctx);

    // signal handler to exit properly on interrupt and termination
    signal(SIGINT, &sighandler);
    signal(SIGTERM, &sighandler);
    signal(SIGPIPE, SIG_IGN);

    // configure the timer every seconds
    pomp_timer_set_periodic(s_ctx->timer, 1000, 1000);

    // run indefinitely until stop
    while (!stop)
            pomp_loop_wait_and_process(s_ctx.loop, -1);

    // clean up
    signal(SIGINT, SIG_DFL);
    signal(SIGTERM, SIG_DFL);
    signal(SIGPIPE, SIG_DFL);

    pomp_timer_destroy(s_ctx->timer);
    pomp_loop_destroy(s_ctx->loop);

    return EXIT_SUCCESS;
}

Python#

Here is a typical skeleton of Python service:

import logging
import os
import sys
import ulog
import libpomp

def update(self):
    logger.info("Hello world")

def main():
    def sighandler(signum, stack):
        # ask the event loop to exit
        nonlocal run, loop
        logging.debug(f"signal {signum} received")
        run = False
        libpomp.pomp_loop_wakeup(loop)

    run = True

    logger = logging.getLogger(None)
    logger.addHandler(ulog.ULogHandler())

    if os.getenv('ULOG_LEVEL') == 'D':
        logger.setLevel(logging.DEBUG)
    else:
        logger.setLevel(logging.INFO)

    # signal handler to exit properly on interrupt and termination
    signal.signal(signal.SIGINT, sighandler)
    signal.signal(signal.SIGTERM, sighandler)

    # create a new event loop and a timer
    loop = libpomp.pomp_loop_new()
    timer_cb = libpomp.pomp_timer_cb_t(lambda *_: update())
    timer = libpomp.pomp_timer_new(loop, timer_cb, None)

    # configure the timer every seconds
    libpomp.pomp_timer_set_periodic(timer, 1000, 1000)

    while run:
        # timeout here is needed for signal handler
        libpomp.pomp_loop_wait_and_process(loop, 500)

    # clean up
    libpomp.pomp_loop_destroy(loop)

if __name__ == '__main__':
    main()

Example#

See Hello Service and the code is available here.

Running multiple services#

It is possible to run multiple services per mission. For this, it is necessary to add a new element in the list named “services” present at the end of the mission.yaml file:

services:
    srv-example1:
      lang: c
      args: ["arg1", "arg2"]
    srv-example2:
      lang: c
      args: ["arg1"]

Note

Reminder: The file mission.yaml is located in the root directory of your mission. The airsdk init is here to help you getting started with a new mission project and can generate your first service boilerplate for you.

Important

The maximum number of simultaneous running services is 8 per mission.