Configuration files

Configuration files are a way to retrieve dynamic parameters values in your own mission code. For example, you may want to define your own horizontal velocity, and change it easily without digging into code lines. To that end, you can define a .cfg file.

<my_config_file>.cfg

Note

At Parrot, configuration files are based on an external library libconfig. This section aims to help you on how to write and use a configuration file as we do it at Parrot, but you are free to use your own library or setup.

As for the configuration file itself, you may write it that way:

<my_config_key_to_be_replaced>:
{
    my_int_value = 1; /* [unit] */
    my_float_value = 1.; /* [unit] */
    my_string_value = "1"; /* [unit] */
}

Its storage is usually <my_mission_folder>/assets/etc/.... We advise you to separate your files into a guidance and a services folder.

Here an example of the Road Runner mission:

../_images/assets_tree.png

Note

airsdk-cli copies all files found in assets into the mission storage, where they can be accessed at run-time.

Important

When the content of the <my_mission_folder>/assets/ directory is copied into the mission file, symbolic links are dereferenced, even if they point to another file into the assets directory.

Whether your custom configuration belongs to a service or a guidance mode, its data retrieval implementation differs a bit. We will tackle those two cases.

Services

Example is shown in C++, but it works similarly for Python.

configuration.hpp

#pragma once

#include <string>

struct MyConfiguration {
    int my_int_value;
    float my_float_value;
    std::string my_string_value;

    MyConfiguration() : my_int_value(0), my_float_value(0.f), my_string_value("0") {}

    // To read <my_config_file>.cfg file and get configurable parameters dynamically
    int read(const std::string &path) override final;
};

configuration.cpp

#define ULOG_TAG <my_service>_cfg
#include <ulog.hpp>
ULOG_DECLARE_TAG(ULOG_TAG);

#include <cfgreader/cfgreader.hpp>

#include "configuration.hpp"

int MyConfiguration::read(const std::string &path) {
    return cfgreader::loadFromFile(*this, path, "<my_config_file>");
}

namespace cfgreader {
#define CFG_CHECK(E) ULOG_ERRNO_RETURN_ERR_IF(E < 0, EINVAL)
template <>
int SettingReader<MyConfiguration>::read(
    const libconfig::Setting &set, T &v) {
    using CR = ConfigReader;
    CFG_CHECK(CR::getField(set, "my_int_value", v.my_int_value));
    CFG_CHECK(CR::getField(set, "my_float_value",
                        v.my_float_value));
    CFG_CHECK(CR::getField(set, "my_string_value",
                        v.my_string_value));
    return 0;
}
} // namespace cfgreader

To use it for example, declare a configuration object in the header of the class responsible for retrieving those values:

#pragma once

#include "configuration.hpp"

class Foo {
    MyConfiguration mMyConfiguration;

public:
    Foo(MyConfiguration p_MyConfiguration);
}

Do not forget to initialize the reading of the configuration file somewhere in your Air SDK service code:

static const std::string <MY_SERVICE>_CONFIG_PATH = "/etc/services/<my_config_file>.cfg";

Foo::Foo(MyConfiguration p_MyConfiguration) : mMyConfiguration(p_MyConfiguration) {
    mMyConfiguration.read(<MY_SERVICE>_CONFIG_PATH);
}

As soon as your configuration class has been instanciated and filled in, you may use any of its value with a simple access from its parent class such as: mMyConfiguration.my_int_value.

Note

Flight supervisor configuration files way to do is similar works similarly.

Guidance modes

C++

The whole implementation is similar to the service case (and could be done the same), but since the guidance class is inherited and already implements the read() function, it is more convenient to use its member methods:

Anafi Ai

#include <guidance.hpp>

static const std::string <MY_GUIDANCE_MODE>_CONFIG_PATH =
"/etc/guidance/<my_guidance_mode>/<my_config_file>.cfg";

Foo::Foo(guidance::Guidance *guidance) : Mode(guidance) {
    int res;
    /* Read configuration */
    res = mMyConfiguration.read(
        guidance->getConfigFile(<MY_GUIDANCE_MODE>_CONFIG_PATH));
    if (res < 0) {
        ULOG_ERRNO("Guidance::getConfigFile", res);
        goto out;
    }
}

Python

import cfgreader
import guidance.core as gdnc_core

<MY_GUIDANCE_MODE>_CONFIG_FILENAME = "/etc/guidance/<my_guidance_mode>/<my_config_file>.cfg"

class MyGuidanceMode(gdnc_core.Mode):
    def __init__(self, guidance, name):
        super().__init__(guidance,name)

        # Get configuration path
        mode_config_path = guidance.get_config_file(<MY_GUIDANCE_MODE>_CONFIG_FILENAME)

        # Get configuration values
        fields = [
            (
                mode_config_path,
                "my_int_value",
            ),
            (
                mode_config_path,
                "my_float_value",
            ),
            (
                mode_config_path,
                "my_string_value",
            )
        ]
        <my_guidance_mode>_cfg = cfgreader.load(fields)

Anafi UKR

#include <guidance/local.hpp>

static const std::string <MY_GUIDANCE_MODE>_CONFIG_PATH =
"/etc/guidance/<my_guidance_mode>/<my_config_file>.cfg";

Foo::Foo(guidance::GuidanceBase *guidance) : LocalMode(guidance) {
    int res;
    /* Read configuration */
    res = mMyConfiguration.read(
        guidance->getConfigFile(<MY_GUIDANCE_MODE>_CONFIG_PATH));
    if (res < 0) {
        ULOG_ERRNO("Guidance::getConfigFile", res);
        goto out;
    }
}

Python

import cfgreader
import guidance.local.core as gdnc_core

<MY_GUIDANCE_MODE>_CONFIG_FILENAME = "/etc/guidance/<my_guidance_mode>/<my_config_file>.cfg"

class MyGuidanceMode(gdnc_core.LocalMode):
    def __init__(self, guidance, name):
        super().__init__(guidance,name)

        # Get configuration path
        mode_config_path = guidance.get_config_file(<MY_GUIDANCE_MODE>_CONFIG_FILENAME)

        # Get configuration values
        fields = [
            (
                mode_config_path,
                "my_int_value",
            ),
            (
                mode_config_path,
                "my_float_value",
            ),
            (
                mode_config_path,
                "my_string_value",
            )
        ]
        <my_guidance_mode>_cfg = cfgreader.load(fields)

To use your value: <my_guidance_mode>_cfg.<my_guidance_mode>.my_int_value.

mission.yaml

Do not forget to add libconfigreader to the dependencies of the field that will use the configuration file. As an example:

[...]
guidance:
    <my_guidance_mode>:
        lang: c++
        depends:
        - ...
        - libconfigreader
        - ...

How to retrieve configuration file path of the mission in its root filesystem

The path to configuration files of the mission relative to its root directory (where etc, share, etc. directories can be found) can be retrieved:

  • from Services:

    • in C++:

      const char *missionConfigurationRoot = cfgreader::ConfigReader::insertMissionRootDir(
                      <MY_SERVICE>_CONFIG_PATH);
      
    • in Python:

      import cfgreader
      cfgreader.insert_mission_root_dir(<MY_SERVICE>_CONFIG_PATH)
      
  • from Guidance:

    • in C++:

      std::string missionConfigurationRoot = guidance->getConfigDir();
      
    • in Python:

      mission_configuration_root = guidance.get_config_dir()
      
  • from Flight Supervisor:

    mission_configuration_root = self.mission.env.get_product_cfg_dir()