Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
Schmluk committed Aug 18, 2024
1 parent 1f685ca commit c77bf2c
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 18 deletions.
12 changes: 10 additions & 2 deletions config_utilities/demos/dynamic_config_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,29 @@
from tkinter import *
import customtkinter as ctk
import yaml
import copy

ctk.set_appearance_mode("System") # Modes: "System" (standard), "Dark", "Light"
ctk.set_default_color_theme("blue") # Themes: "blue" (standard), "green", "dark-blue"
PAD_X = 10
PAD_Y = 10
GUI_NAME = "[Config Utilities Dynamic Config GUI] "

"""
TODO(lschmid): Type Info known limitations:
- Make sure namespaces are handled correctly.
- Enable updating from values without re-building the entire GUI
- Add registered type information for virtual configs.
- Add support for config maps and vectors.
- Find a good interface and visualization for int/float types (consider exteded type information).
"""


class Settings:
"""
A class to store settings for the DynamicConfigGUI. This can also be opened as a top-level window.
"""

METHOD_OPTIONS = ["Type Info", "Plain YAML"]
METHOD_OPTIONS = [ "Plain YAML", "Type Info (Experimental)"]
APPEARANCE_OPTIONS = ["System", "Light", "Dark"]
COLOR_THEME_OPTIONS = ["blue", "green", "dark-blue"]
SACLE_MIN = 0.5
Expand Down
4 changes: 2 additions & 2 deletions docs/Configs.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@ void declare_config(MyConfig& config) { ... } // Works!
> The declaration of `declare_config` *must* be in the same namespace as the object type being declared.
```c++
#include external/other_object.h
#include <external/other_object.h>
namespace external {
void declare_config(OtherObject& config) { ... } // Also works!
} // namespace external
```
```c++
#include external/other_object.h
#include <external/other_object.h>
void declare_config(external::OtherObject& config) { ... } // Will not work!
```
Expand Down
8 changes: 0 additions & 8 deletions docs/DynamicConfigs.md

This file was deleted.

122 changes: 122 additions & 0 deletions docs/Dynamic_Configs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# Dynamic Configs

This tutorial demonstrates how to register configs as dynamic, i.e., they can receive get/set requests at runtime to update their values, and how to use the dynamic config server to access these configs.


**Contents:**
- [Declaring a dynamic config](#declaring-a-dynamic-config)
- [Dynamic Config Callbacks](#dynamic-config-callbacks)
- [Setting Dynamic Configs](#setting-dynamic-configs)
- [Custom Dynamic Config Servers](#custom-dynamic-config-servers)


## Declaring a dynamic config
Any `config_utilities` config can be declared as a dynamic config. For this simply use the `DynamicConfig` struct.
Note that all dynamic configs must have a unique key that used to get/set their values:

```c++
#include <config_utilities/dynamic_config.h>

// Define your configs as usual in some header:
struct MyConfig { ... };
void declare_config(MyConfig& config) { ... }

// Instantiate a dynamic config:
config::DynamicConfig<MyConfig> dynamic_config("my_config"); // key: my_config
```
Dynamic configs are thread-safe, you can get and set their values as follows:
```c++
// Getting single values:
if (dynamic_config.get().some_param >= some_value) {
doMagic();
}
```

> **ℹ️ Note**<br>
> Dynamic config `get()` returns a copy for thread safety, to get multiple values, this is more efficient:

```c++
// Getting multiple values:
const auto current_config = dynamic_config.get();
float z = current_config.x * current_config.y;
```

You can also set dynamic configs using the `set()` function. Note that this takes an entire config as input. To only update specific values, get the current config first:

```c++
// Set the dynamic config to a config:
MyConfig new_values;
dynamic_config.set(new_values); // Works! Sets all values.

// To update single params, get the config first:
auto values = dynamic_config.get();
values.x = new_x;
dynamic_config.set(values); // Works! Will only update MyConfig.x.

// Recall that config.get() returns a copy:
dynamic_config.get().x = new_x; // Won't work! Only modifies the copy.
```

## Dynamic Config Callbacks

All dynamic configs allow registering callback functions that are triggered whenever the dynamic config is updated:

```c++
config::DynamicConfig<MyConfig> dynamic_config("my_config");

// Register a callback:
dynamic_config.setCallback([&dynamic_config](){
std::cout << "Got new config values: " << config::toString(dynamic_config) << std::endl;
};)
```
## Setting Dynamic Configs
We provide a base interface to set dynamic configs via the `DynamicConfigServer`. The server can get configs by key and uses `YAML` as an interface to interact with the config:
```c++
#include <config_utilities/dynamic_config.h>
config::DynamicConfigServer server;
// Get the values of a dynamic config:
YAML::Node values = server.getValues("my_config");
// Set the values of a dynamic config:
YAML::Node new_values = ...;
server.setValues("my_config", new_values); // Works!
// Note that the new values can also contain only a subset of params:
server.setValues("my_config", YAML::Load("x: 123")); // Works! Only sets the x param.
```

Similarly to the dynamic configs, also the server allows registering hooks to keep track of which configs are registered and updated:

```c++
DynamicConfigServer::Hooks hooks;
hooks.onRegister = { ... };
hooks.onDeregister = { ... };
hooks.onUpdate = { ... };

server.setHooks(hooks); // All done!
```
To see further functionalities and use cases see the demo and source code.
## Custom Dynamic Config Servers
Custom servers or client can easily be implemented by building on top of the provided `DynamicConfigServer`.
An example of this is given in the `RosDynamicConfigServer` in `config_utilities/parsing/ros.h`, which advertizes all config get/set interfaces via ROS topics.
This can be used to, for example, modify the C++ configs using a python GUI, as demonstrated in our `demo_dynamic_config`. Give it a try:
```bash
# Required for the GUI:
pip install customtkinter
# Run the demo:
roslaunch config_utilities demo_dynamic_config.launch
```

> **ℹ️ Note**<br>
We further provide an initial implementation of a field-based GUI. However, this is still experimental and we recommend using the `Plain YAML` editor.
To try it and get some inspiration how the config info from the server can be used, click `Settings` in te GUI and pick the `Type Info (Experimental)` UI Method in the demo.
24 changes: 18 additions & 6 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,19 @@ The following tutorials will guide you through functionalities of `config_utilit
- [Managed instances](External.md#managed-instances)
- [Debugging](External.md#debugging)

8. [**Varia**](Varia.md)
8. [**Dynamic Configs**](Dynamic_Configs.md)
- [Declaring a dynamic config](Dynamix_Configs.md#declaring-a-dynamic-config)
- [Dynamic Config Callbacks](Dynamix_Configs.md#dynamic-config-callbacks)
- [Setting Dynamic Configs](Dynamix_Configs.md#setting-dynamic-configs)
- [Custom Dynamic Config Servers](Dynamix_Configs.md#custom-dynamic-config-servers)

9. [**Varia**](Varia.md)
- [Settings](Varia.md#settings)
- [Globals](Varia.md#globals)


## Demos
The (non-ros) demos can be run via the `run_demo.py` utility in the scripts directory. If you are building this library via catkin, you can run one of the following to see the results of one of the corresponding demo files:
```
```bash
python3 scripts/run_demo.py config
python3 scripts/run_demo.py inheritance
python3 scripts/run_demo.py factory
Expand All @@ -56,9 +61,16 @@ python3 scripts/run_demo.py factory
> **ℹ️ Note**<br>
> If you're building via cmake, you can point `run_demo.py` to the build directory with `-b/--build_path`.
The ros demo can be run via:
```
The ros demos can be run via:
```bash
roslaunch config_utilities demo_ros.launch
roslaunch config_utilities demo_dynamic_config.launch
```

If you are looking for a specific use case that is not in the tutorials or demos, chances are you can find a good example in the `tests/` directory!
Note that for the `dynamic config demo` customtkinter is required to run the GUI:
```bash
pip install customtkinter
```

> **ℹ️ Note**<br>
If you are looking for a specific use case that is not in the tutorials or demos, chances are you can find a good example in the `tests/` directory! Try and give it a look!

0 comments on commit c77bf2c

Please sign in to comment.