Configuration

When you start a command line tool, you can supply it with one or more configuration destinations. The configuration is written in JSON.

Reading

Hierarchy

The configuration follows a hierarchy that is separated by dots (.) in the names of the keys. The hierarchy is inferred from the file system and the structure of the JSON in the files.

For example, assume you supply the config destination config/local/ and there is a file config/local/some/path/Test.json and it contains the following content:

{
        "some_key": {
                "other_key": {
                        "the_answer": 42,
                        "the_question": ["life", "universe", "everything"]
                }
        }
}

In this case, the config key some.path.Test.some_key.other_key.the_answer will be read and set to 42 and the key some.path.Test.some_key.other_key.the_question will contain a list of strings as shown.

A config destination can contain the special file called parent. It should consist of a single line giving a path (absolute or relative to the current destination) to include. This means the contents of the given destination will be treated as if they were present in the current destination. Additionally, the parent destination will be read first which is important for cascading (see below).

Cascading

The structure of the configuration makes it possible (and actually good and common practice) to split a config object across multiple locations. For example your config destination may have a file Test.json that contains

{ "field_1": 42 }

but also reference a parent directory that also has a file Test.json that contains

{ "field_2": 23 }

In effect, both Test.field_1 and Test.field_2 are correctly read. By default, we use this technique to separate

  • configuration specific to an installation (in config/local/ in the pilot home folder), which includes
  • configuration specific to a robot model (in config/default/XXX/ in the installation folder), which includes
  • generic configuration (in config/default/generic/ in the installation folder).

This makes the configuration easy to maintain.

Given the rules for the hierachy and for parent destinations, it is very much possible to specify the same key multiple times. In that case the key that is read last overwrites any previous one of the same name. In particular, a destination always overrides its parent.

Appending

It is also possible to append values to an array, by adding a + to the variable name as such:

{ "array": [1, 2, 3] }
{ "array+": [4, 5, 6] }

The resulting array config value will be [1, 2, 3, 4, 5, 6].

Command line

You can also supply configuration on the command line. For example, to set MyModule.max_velocity to 5, append --MyModule.max_velocity 5 to the command line.

Values are read as JSON objects. Make sure to use the correct quoting according to the shell you use. When setting a boolean value to true, you can omit the value and just give the key.

Arrays and objects can be provided on the command line as follows:

command --array [1, 2, 3, 4] --object {"field": 1234, "array": ["a", "b", "c"]}

Config keys given at the command line are read as the ultimate last ones, so they override any previous keys with the same names. This is useful for testing out certain values without constantly having to edit files.

Assignment

While some config keys are read explicitly by the application, most of the assignment happens automagically.

Modules

A module that is started with the name MyModule will automatically be assigned all configuration below the MyModule. key. From then on it behaves like a class (see below).

Classes

If a configuration key is applied to an instance of a VNX class, the members of the class are assigned the subkeys where the names match.

For example if you have an object of the class Test below

class Test2 {
        string what;
        int over;
}

class Test {
        string name;
        int value;
        vector<int> numbers;
        Test2 other_one;
}

and you assign it the config

{
        "name": "Picard",
        "value": 1234,
        "numbers": [1, 2, 3, 4],
        "other_one": {
                "what": "ever",
                "over": 9000
        }
}

then all the fields get assigned the config key with the matching name. Notice that other_one is another object which will get the same treatment recursively with the embedded config object.

Fields without a matching config key are default initialized and spare keys without a matching field are silently ignored.