Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

`config_utilities` is a minimal but powerful C++ library, providing tools for automated modular software configuration. Parse, verify, modify, and print C++ config structs to build modular object-oriented software systems at run-time.

> **ℹ️ Note**</br>
> With the deprecation of ROS1, we no longer support parsing configs from ROS parameters. The last version of `config_utilities` to support this is available [here](https://github.com/MIT-SPARK/config_utilities/tree/archive/ros_noetic).

## Table of contents

- [Credits](#credits)
Expand All @@ -14,7 +17,7 @@

## Credits

This library was developed by [Lukas Schmid](https://schmluk.github.io/) and [Nathan Hughes](http://mit.edu/sparklab/people.html) at the [MIT-SPARK Lab](http://mit.edu/sparklab), based on functionalities in [ethz-asl/config_utilities](https://github.com/ethz-asl/config_utilities) and [Hydra](https://github.com/MIT-SPARK/Hydra), and is released under a [BSD-3-Clause License](LICENSE)! Additional contributions welcome! This work was supported in part by the Swiss National Science Foundation and Amazon.
This library was developed by [Lukas Schmid](https://schmluk.github.io/) and [Nathan Hughes](https://nathanhhughes.github.io/) at the [MIT-SPARK Lab](http://mit.edu/sparklab), based on functionalities in [ethz-asl/config_utilities](https://github.com/ethz-asl/config_utilities) and [Hydra](https://github.com/MIT-SPARK/Hydra), and is released under a [BSD-3-Clause License](LICENSE)! Additional contributions welcome! This work was supported in part by the Swiss National Science Foundation and Amazon.

## Why `config_utilities`?

Expand Down Expand Up @@ -128,7 +131,7 @@ This package is compatible with `ROS2`/`colcon`. Just clone it into your workspa
```bash
cd ~/my_ws/src
git clone [email protected]:MIT-SPARK/config_utilities.git
colcon build config_utilities_ros
colcon build --packages-up-to config_utilities_ros
```

If you want to build and install without colcon/ROS, that is easy, too! Just clone this repository and build via CMake:
Expand Down
2 changes: 1 addition & 1 deletion config_utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,6 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/config_utilitiesConfig.cmake

# Exposes this package to ament for ros2 run and other utilities to work
find_package(ament_cmake_core QUIET)
if (${ament_cmake_core_FOUND})
if (${ament_cmake_core_FOUND})
ament_package()
endif()
89 changes: 80 additions & 9 deletions docs/Parsing.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This tutorial explains how to create configs and other objects from source data.
- [Parse from yaml](#parse-from-yaml)
- [Parse from the command line](#parse-from-the-command-line)
- [Parse via global context](#parse-via-global-context)
- [Parsing in ROS2](#parsing-in-ros2)

## Parse from yaml

Expand Down Expand Up @@ -75,19 +76,19 @@ std::unique_ptr<MyBase> object = createFromYamlFileWithNamespace<MyBase>(file_na

## Parse from the command line

It is also possible to use the same interfaces as in the yaml or ROS case but via aggregate YAML read from the command line. To use it, include `parsing/command_line.h`.
It is also possible to parse aggregate yaml read from the command line. To use it, include `parsing/command_line.h`.
`config_utilities` supports parsing the following command line flags:

- `--config-utilities-file SOME_FILE_PATH`: Specify a file to load YAML from.
- `--config-utilities-yaml SOME_ARBITRARY_YAML`: Specify YAML directly from the command line.
- `--config-utilities-file SOME_FILE_PATH`: Specify a file to load yaml from.
- `--config-utilities-yaml SOME_ARBITRARY_YAML`: Specify yaml directly from the command line.
- `--config-utilities-var KEY=VALUE`: Specify a new variable for the substitution context.
- `--disable-substitutions/--no-disable-substitutions`: Turn off resolving substitutions

> **✅ Supports**<br>
> Note that the `--config-utilities-file` flag allows for a namespace (i.e., `some/custom/ns`) to apply to the file globally. This is specified as `--config-utilities-file SOME_FILE@some/custom/ns`.

Both command line flags can be specified as many times as needed.
When aggregating the YAML from the command line, the various flags are merged left to right (where conflicting keys from the last specified flag take precedence) and any sequences are appended together.
When aggregating the yaml from the command line, the various flags are merged left to right (where conflicting keys from the last specified flag take precedence) and any sequences are appended together.
For those familiar with how the ROS parameter server works, this is the same behavior.
See [here](Compositing.md#controlling-compositing-behavior) for an in-depth discussion of options as to how to control this behavior.
Please also note that the `--config-utilities-yaml` currently accepts multiple space-delimited tokens (because the ROS2 launch file infrastructure does not currently correctly handle escaped substitutions), so
Expand All @@ -102,7 +103,7 @@ and
some_command --config-utilities-yaml {my: {cool: config}} --config-utilities-file some_file.yaml
```

will result in the same behavior (that the resulting parsed YAML will be `{my: {cool: config}}` merged with the contents of `some_file.yaml`).
will result in the same behavior (that the resulting parsed yaml will be `{my: {cool: config}}` merged with the contents of `some_file.yaml`).

Parsing directly from the command line takes one of three forms:

Expand All @@ -121,15 +122,15 @@ int main(int argc, char** argv) {

# Parse via global context

Usually the command line arguments or parsed YAML are not globally available to every part of an executable.
Similar to the ROS1 parameter server (and access to the parameter server by `ros::NodeHandle`), we provide a global `config::internal::Context` object (included via `parsing/context.h`) that handles tracking parsed YAML.
Usually the command line arguments or parsed yaml are not globally available to every part of an executable.
Similar to the ROS1 parameter server (and access to the parameter server by `ros::NodeHandle`), we provide a global `config::internal::Context` object (included via `parsing/context.h`) that handles tracking parsed yaml.
This `config::internal::Context` is not intended to be manipulated directly.
Instead, you should use one of the following methods:

```cpp
int main(int argc, char** argv) {
// pushes config-utilities specific flags to the end of argv and decrements argc so that it
// looks like the command was run without any config-utilities specific flags
// pushes config_utilities specific flags to the end of argv and decrements argc so that it
// looks like the command was run without any config_utilities specific flags
const bool remove_config_utils_args = true;
config::initContext(argc, argv, remove_config_utils_args);

Expand Down Expand Up @@ -162,3 +163,73 @@ const auto object_2 = config::createFromContext<MyBase>("optional/namespace", ba

> **✅ Supports**<br>
> Parsing via the gobal context is the recommended mode, as this supports all functionalities in a simple manner. In addition, [introspection](Introspection.md) works best on the global context.

# Parsing in ROS2

Certain design choices with how parameters work in ROS2 made it impossible to bring forward our original ROS1 parameters parsing code (that leveraged `XmlRPC`).
Instead, we recommend also using the global context for parsing configs in ROS2 code.
When doing this, there are two things to watch out for.
The first is that you should parse and remove `config_utilities` command-line arguments **before** calling `rclcpp::init`.
Roughly, your top-level executable code should take this general structure:

```cpp
#include <config_utilities/config_utilities.h>
#include <config_utilities/parsing/context.h>
// ...

namespace my_ros2_package {

struct NodeSettings {
size_t robot_id = 0;
std::filesystem::path log_path;
// ...
};

void declare_config(NodeSettings& config) {
using namespace config;
name("NodeSettings");
field(config.robot_id, "robot_id");
field<Path::Absolute>(config.log_path, "log_path");
// ...
}

} // namespace my_ros2_package

int main(int argc, char* argv[]) {
config::initContext(argc, argv, true);
config::setConfigSettingsFromContext();

rclcpp::init(argc, argv);

const auto node_info = config::fromContext<my_ros2_package::NodeSettings>();
// ...

rclcpp::shutdown();
return 0;
}
```

The second thing to watch out for is that nodes included in a launch file will not display stdout statements.
Both the default logger (which uses stdout/stderr) and the glog-based logger will appropriately display warnings and errors,
but you may want to implement your own `config_utilities` logger that forwards messages to the `rclcpp` logging infrastructure.

To actually specify configuration information for a node, you just need to supply the appropriate command-line information under the `args` section of the node.
Using a portion of Hydra's launch file as example, this would look like:
```yaml
launch:
- ...
- node:
pkg: hydra_ros
exec: hydra_ros_node
name: hydra
args: >
--config-utilities-file $(find-pkg-share hydra_ros)/config/sinks/mesh_segmenter_sinks.yaml@frontend/objects
--config-utilities-file $(find-pkg-share hydra_ros)/config/sinks/active_window_sinks.yaml@active_window
--config-utilities-yaml {robot_id: $(var robot_id), log_path: $(var log_path)}
- ...
```

> **:warning: Warning**</br>
> Note that you cannot escape any portion of the command-line information when you use ROS2 substitutions.
> The command-line parsing for `config_utilities` was developed for use with ROS2 launch files originally,
> so the parsed yaml should accurately reflect the specified information without required any escaping (i.e., we handle cases where inline yaml is broken over multiple strings in `argv`).
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ The following tutorials will guide you through functionalities of `config_utilit
- [Parse from yaml](Parsing.md#parse-from-yaml)
- [Parse from the command line](Parsing.md#parse-from-the-command-line)
- [Parse via global context](Parsing.md#parse-via-global-context)
- [Parsing in ROS2](Parsing.md#parsing-in-ros2)

4. [**Handling complex configs or types**](Types.md)

Expand Down