diff --git a/cpu/native/startup.c b/cpu/native/startup.c index 7c3ff65f1106..5de889f74db6 100644 --- a/cpu/native/startup.c +++ b/cpu/native/startup.c @@ -252,8 +252,7 @@ void daemonize(void) */ static void filter_daemonize_argv(char **argv) { - int idx = 0; - for (char **narg = argv; *narg != NULL; narg++, idx++) { + for (char **narg = argv; *narg != NULL; narg++) { if (strcmp("-d", narg[0]) == 0) { char **xarg = narg; do { diff --git a/doc/doxygen/riot.doxyfile b/doc/doxygen/riot.doxyfile index 246a6ac1dbc7..01fa2dba0a8f 100644 --- a/doc/doxygen/riot.doxyfile +++ b/doc/doxygen/riot.doxyfile @@ -1481,7 +1481,11 @@ HTML_EXTRA_FILES = src/css/bootstrap.min.css \ src/js/riot-doxy.js \ ../../sys/net/application_layer/unicoap/docs/unicoap-layers.svg \ ../../sys/net/application_layer/unicoap/docs/unicoap-layers-comms.svg \ - ../../sys/net/application_layer/unicoap/docs/unicoap-layers-comms-apis.svg + ../../sys/net/application_layer/unicoap/docs/unicoap-layers-comms-apis.svg\ + ../../sys/runtime_config/docs/api_structure.svg\ + ../../sys/runtime_config/docs/architecture.svg\ + ../../sys/runtime_config/docs/namespaces_and_storages.svg\ + ../../sys/runtime_config/docs/runtime_config.svg # The HTML_COLORSTYLE tag can be used to specify if the generated HTML output # should be rendered with a dark or light theme. diff --git a/examples/README.md b/examples/README.md index d56b2381d3cd..9005ba1d0caf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -128,7 +128,6 @@ Here is a quick overview of the examples available in the RIOT: | [sock_tcp_echo](./networking/misc/sock_tcp_echo/README.md) | This is a simple TCP echo server / client that uses the SOCK API. | | [lwip_ipv4](./networking/misc/lwip_ipv4/README.md) | This is a simple UDP client / server using LWIP for IPv4. | - ## Advanced Examples | Example | Description | @@ -144,6 +143,7 @@ Here is a quick overview of the examples available in the RIOT: | [senml_saul](./advanced/senml_saul/README.md) | This example demonstrates the usage of the SAUL (Sensor Actuator Uber Layer) module with the SenML (Sensor Measurement Lists) format. | | [opendsme](./advanced/opendsme/README.md) | This example demonstrates the usage of the OpenDSME module in RIOT. | | [xipfs](./advanced/xipfs/README.md) | This example demonstrates the usage of XIPFS for creating and executing an executable file. | +| [runtime_config_core](./advanced/runtime_config/runtime_config_core/README.md) | This example demonstrates the most basic usage of the `Runtime configuration` module. | ## Examples from Guides diff --git a/examples/advanced/runtime_config/runtime_config_core/Makefile b/examples/advanced/runtime_config/runtime_config_core/Makefile new file mode 100644 index 000000000000..c0e269ede759 --- /dev/null +++ b/examples/advanced/runtime_config/runtime_config_core/Makefile @@ -0,0 +1,24 @@ +# name of the application +APPLICATION = runtime_config_example_core + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../../../.. + +# required modules +# enable board_led schema (sys/board_led) of the Runtime configuration module +USEMODULE += runtime_config_namespace_sys_board_led +# enable the ztimer to turn the led on and off every second +USEMODULE += ztimer_sec + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/advanced/runtime_config/runtime_config_core/README.md b/examples/advanced/runtime_config/runtime_config_core/README.md new file mode 100644 index 000000000000..90f9642a1910 --- /dev/null +++ b/examples/advanced/runtime_config/runtime_config_core/README.md @@ -0,0 +1,14 @@ +# Runtime Configuration Core + +This application demonstrates the most basic usage of the `Runtime configuration` module. +It implements a BOARD LED schema and continuously changes its value every second. + +## Usage + +Simply build and flash the application for your target board: + +```shell +BOARD=YOUR_BOARD_NAME_HERE make flash term +``` + +Now you should see the terminal continuously print out different LED states. diff --git a/examples/advanced/runtime_config/runtime_config_core/main.c b/examples/advanced/runtime_config/runtime_config_core/main.c new file mode 100644 index 000000000000..845876c3560d --- /dev/null +++ b/examples/advanced/runtime_config/runtime_config_core/main.c @@ -0,0 +1,126 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Runtime configuration core minimal example application to demonstrate + * how to use the "Runtime configuration" module without any of its extensions. + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include + +#include "periph_cpu.h" +#include "led.h" +#include "board.h" +#include "runtime_config.h" +#include "runtime_config/namespace/sys.h" +#include "runtime_config/namespace/sys/board_led.h" +#include "ztimer.h" + +/* This callback gets called, when all new configurations shall be applied. + * If interacting with configurations of drivers then this callback should be + * implemented by the driver itself. For custom application logic, we need to + * define this for ourselves. */ +static runtime_config_error_t apply_instance_cb( + const runtime_config_schema_instance_t *const schema_instance) +{ + const runtime_config_sys_board_led_instance_t *instance_data = + (const runtime_config_sys_board_led_instance_t *)schema_instance->data; + + /* Get the correct field from the instance_data variable */ + bool led_state = instance_data->enabled; + /* Turn the LED on or off depending on the led_state */ + if (led_state == true) { + /* This is the apply_cb function of instance 0, so we toggle LED 0 as well */ + LED_ON(0); + } + else { + LED_OFF(0); + } + return 0; +} + +/* This callback gets called, when some specific new configurations shall be applied. + * If interacting with configurations of drivers then this callback should be + * implemented by the driver itself. For custom application logic, we need to + * define this for ourselves. */ +static runtime_config_error_t apply_group_or_parameter_cb( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id) +{ + const runtime_config_sys_board_led_instance_t *instance_data = + (const runtime_config_sys_board_led_instance_t *)schema_instance->data; + + /* Either apply all parameters of the instance or only the given parameter. + * For a single LED there is no difference as it only has one parameter. */ + if (group_or_parameter_id == RUNTIME_CONFIG_SYS_BOARD_LED_ENABLED) { + /* Get the correct field from the instance_data variable */ + bool led_state = instance_data->enabled; + /* Turn the LED on or off depending on the led_state */ + if (led_state == true) { + /* This is the apply_cb function of instance 0, so we toggle LED 0 as well */ + LED_ON(0); + } + else { + LED_OFF(0); + } + } + + return 0; +} + +/* This belongs into the BOARD or Driver for example */ +static runtime_config_sys_board_led_instance_t board_led_instance_data = { + .enabled = 0, +}; + +static runtime_config_schema_instance_t board_led_instance = { + .data = &board_led_instance_data, + .apply_schema_instance_cb = &apply_instance_cb, + .apply_group_or_parameter_cb = &apply_group_or_parameter_cb, +}; + +/* This belongs into our main application */ +int main(void) +{ + runtime_config_init(); + + /* init schemas */ + runtime_config_add_schema_instance(&runtime_config_sys_board_led, &board_led_instance); + + bool board_led_enabled = false; + + while (true) { + /* Invert the BOARD LED, to make it turn on and off on each subsequent cycle */ + board_led_enabled = !board_led_enabled; + + /* Create runtime_config_node_t for the board_led_parameter */ + const runtime_config_node_t parameter_node = RUNTIME_CONFIG_NODE_PARAMETER( + &board_led_instance, + &runtime_config_sys_board_led_enabled); + + /* Set new runtime configuration value */ + runtime_config_set(¶meter_node, &board_led_enabled, sizeof(board_led_enabled)); + + /* Apply the runtime configuration value to change the LED state + * (in this case calls the apply_cb function: "board_led_instance_apply_cb") */ + runtime_config_apply(¶meter_node); + + /* Sleep for 1 second and then do it again*/ + ztimer_sleep(ZTIMER_SEC, 1); + } + + return 0; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 698e071dd8a4..17c9a069c3e6 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -355,6 +355,12 @@ PSEUDOMODULES += psa_riot_hashes_sha_512_224 PSEUDOMODULES += psa_riot_hashes_sha_512_256 PSEUDOMODULES += psa_riot_hashes_hmac_sha256 PSEUDOMODULES += fortuna_reseed + +# Runtime configuration modules are pseudomodules unless stated otherwise +PSEUDOMODULES += runtime_config_% +# Runtime configuration sys schemas +NO_PSEUDOMODULES += runtime_config_namespace_sys + PSEUDOMODULES += riotboot_% PSEUDOMODULES += rtt_cmd PSEUDOMODULES += saul_adc diff --git a/sys/Kconfig b/sys/Kconfig index 8052d8e1f037..dc2346d52e18 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -16,6 +16,7 @@ rsource "fido2/Kconfig" rsource "net/Kconfig" rsource "progress_bar/Kconfig" rsource "psa_crypto/Kconfig" +rsource "runtime_config/Kconfig" rsource "shell/Kconfig" rsource "shell_lock/Kconfig" rsource "usb/Kconfig" diff --git a/sys/Makefile b/sys/Makefile index 1143e07b7761..94a8602ddd18 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -131,6 +131,9 @@ endif ifneq (,$(filter netutils,$(USEMODULE))) DIRS += net/netutils endif +ifneq (,$(filter runtime_config,$(USEMODULE))) + DIRS += runtime_config +endif ifneq (,$(filter sema,$(USEMODULE))) DIRS += sema endif diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 9f09204fb76e..e21a46575279 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -730,4 +730,8 @@ ifneq (,$(filter wifi_scan_list,$(USEMODULE))) USEMODULE += l2scan_list endif +ifneq (,$(filter runtime_config%,$(USEMODULE))) + include $(RIOTBASE)/sys/runtime_config/Makefile.dep +endif + include $(RIOTBASE)/sys/test_utils/Makefile.dep diff --git a/sys/include/runtime_config.h b/sys/include/runtime_config.h new file mode 100644 index 000000000000..6a0fe05ce9e8 --- /dev/null +++ b/sys/include/runtime_config.h @@ -0,0 +1,674 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config Runtime configuration + * @ingroup sys + * @brief Runtime configuration module for handling configurations at runtime + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "clist.h" +#include "xfa.h" +#include "modules.h" +#include "runtime_config/error.h" + +#ifndef CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME +/** + * @brief Enables the "name" field inside the @ref runtime_config_namespace_t, + * @ref runtime_config_schema_t, @ref runtime_config_schema_instance_t, + * @ref runtime_config_parameter_t, or @ref runtime_config_group_t structs. + */ +# define CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME 0 +#endif + +#ifndef CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION +/** + * @brief Enables the "description" field inside the @ref runtime_config_namespace_t, + * @ref runtime_config_schema_t, @ref runtime_config_schema_instance_t, + * @ref runtime_config_parameter_t, or @ref runtime_config_group_t structs. + */ +# define CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION 0 +#endif + +/** + * @brief Identifier of a configuration namespace. + * + * It is unique within the scope of the runtime configuration system itself. + */ +typedef uint8_t runtime_config_namespace_id_t; + +/** + * @brief Identifier of a configuration schema. + * + * It is unique within the scope of its parent configuration namespace. + */ +typedef uint32_t runtime_config_schema_id_t; + +/** + * @brief Identifier of a schema instance. + * + * It is unique within the scope of its parent configuration schema. + */ +typedef uint16_t runtime_config_schema_instance_id_t; + +/** + * @brief Identifier of a configuration group or parameter. + * + * It is unique within the scope of its parent schema instance. + * Because a configuration group and a configuration parameter share the same + * namespace, this type exists for cases where it is not clear if an ID belongs + * to a group or a parameter. + */ +typedef uint8_t runtime_config_group_or_parameter_id_t; + +/** + * @brief Identifier of a configuration group. + * + * It is unique within the scope of its parent schema instance. + */ +typedef runtime_config_group_or_parameter_id_t runtime_config_group_id_t; + +/** + * @brief Identifier of a configuration parameter. + * + * It is unique within the scope of its parent schema instance. + */ +typedef runtime_config_group_or_parameter_id_t runtime_config_parameter_id_t; + +/** + * @brief Forward declaration of runtime configuration namespace struct. + */ +typedef struct runtime_config_namespace runtime_config_namespace_t; + +/** + * @brief Forward declaration of runtime configuration schema struct. + */ +typedef struct runtime_config_schema runtime_config_schema_t; + +/** + * @brief Forward declaration of runtime configuration schema instance struct. + */ +typedef struct runtime_config_schema_instance runtime_config_schema_instance_t; + +/** + * @brief Forward declaration of runtime configuration group struct. + */ +typedef struct runtime_config_group runtime_config_group_t; + +/** + * @brief Forward declaration of runtime configuration parameter struct. + */ +typedef struct runtime_config_parameter runtime_config_parameter_t; + +/** + * @brief Supported data types used by configuration parameters of the runtime + * config module. + */ +typedef enum { + RUNTIME_CONFIG_TYPE_BYTES = 1, /**< Bytes. */ + RUNTIME_CONFIG_TYPE_STRING = 2, /**< String. */ + RUNTIME_CONFIG_TYPE_BOOL = 3, /**< Boolean. */ + + RUNTIME_CONFIG_TYPE_UINT8 = 4, /**< 8-bits unsigned integer. */ + RUNTIME_CONFIG_TYPE_UINT16 = 5, /**< 16-bits unsigned integer. */ + RUNTIME_CONFIG_TYPE_UINT32 = 6, /**< 32-bits unsigned integer. */ + RUNTIME_CONFIG_TYPE_UINT64 = 7, /**< 64-bits unsigned integer. */ + + RUNTIME_CONFIG_TYPE_INT8 = 8, /**< 8-bits signed integer. */ + RUNTIME_CONFIG_TYPE_INT16 = 9, /**< 16-bits signed integer. */ + RUNTIME_CONFIG_TYPE_INT32 = 10, /**< 32-bits signed integer. */ + RUNTIME_CONFIG_TYPE_INT64 = 11, /**< 64-bits signed integer. */ + + RUNTIME_CONFIG_TYPE_FLOAT32 = 12, /**< 32-bits float. */ + RUNTIME_CONFIG_TYPE_FLOAT64 = 13, /**< 64-bits float. */ +} runtime_config_type_t; + +/** + * @brief The type of a runtime configuration node. + * + * A runtime configuration node points to a namespace, schema, schema instance, + * group or parameter. + */ +typedef enum { + /** The node points to @ref runtime_config_namespace_t */ + RUNTIME_CONFIG_NODE_TYPE_NAMESPACE = 0, + + /** The node points to @ref runtime_config_schema_t */ + RUNTIME_CONFIG_NODE_TYPE_SCHEMA, + + /** The node points to @ref runtime_config_schema_instance_t */ + RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE, + + /** The node points to @ref runtime_config_group_t */ + RUNTIME_CONFIG_NODE_TYPE_GROUP, + + /** The node points to @ref runtime_config_parameter_t */ + RUNTIME_CONFIG_NODE_TYPE_PARAMETER, +} runtime_config_node_type_t; + +/** + * @brief A runtime configuration node represents a specific location within the + * runtime configuration tree. + * + * It can point to a namespace, schema, schema instance, group or parameter. + * The configuration group and the configuration parameter contain a pointer to + * a schema instance, because they are children of a specific schema instance + * in the configuration tree. The schema instance contains the actual values of + * configuration parameters. + * + * The main use-case of this node is to make the @ref runtime_config_get and + * @ref runtime_config_set functions more ergonomic. + * Using this struct, other configuration identifiers (such as an array of IDs + * or names) that point to a specific location in the tree can be converted to a + * @ref runtime_config_node_t, which can then be passed easily to target functions. + */ +typedef struct { + /** The type of the node */ + runtime_config_node_type_t type; + + /** The location inside of the configuration tree */ + union { + /** Pointer to the configuration namespace */ + const runtime_config_namespace_t *as_namespace; + + /** Pointer to a configuration schema */ + const runtime_config_schema_t *as_schema; + + /** Pointer to a schema instance */ + const runtime_config_schema_instance_t *as_schema_instance; + + /** A configuration group depends on an schema instance */ + struct { + /** Pointer to a schema instance */ + const runtime_config_schema_instance_t *schema_instance; + + /** Pointer to a configuration group */ + const runtime_config_group_t *group; + } as_group; + + /** A configuration parameter depends on an instance */ + struct { + /** Pointer to a schema instance */ + const runtime_config_schema_instance_t *schema_instance; + + /** Pointer to a configuration parameter */ + const runtime_config_parameter_t *parameter; + } as_parameter; + }; +} runtime_config_node_t; + +/** + * @brief Initializes a runtime configuration node pointing to a configuration + * namespace. + * + * @param[in] NAMESPACE Pointer to the target @ref runtime_config_namespace_t. + */ +#define RUNTIME_CONFIG_NODE_NAMESPACE(NAMESPACE) \ + { \ + .type = RUNTIME_CONFIG_NODE_TYPE_NAMESPACE, \ + .as_namespace = NAMESPACE, \ + } + +/** + * @brief Initializes a runtime configuration node pointing to a configuration + * schema. + * + * @param[in] SCHEMA Pointer to the target @ref runtime_config_schema_t. + */ +#define RUNTIME_CONFIG_NODE_SCHEMA(SCHEMA) \ + { \ + .type = RUNTIME_CONFIG_NODE_TYPE_SCHEMA, \ + .as_schema = SCHEMA, \ + } + +/** + * @brief Initializes a runtime configuration node pointing to a configuration + * schema instance. + * + * @param[in] SCHEMA_INSTANCE Pointer to the target @ref runtime_config_schema_instance_t. + */ +#define RUNTIME_CONFIG_NODE_SCHEMA_INSTANCE(SCHEMA_INSTANCE) \ + { \ + .type = RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE, \ + .as_schema_instance = SCHEMA_INSTANCE, \ + } + +/** + * @brief Initializes a runtime configuration node pointing to a configuration group. + * + * @param[in] SCHEMA_INSTANCE Pointer to the @ref runtime_config_schema_instance_t + * that this group belongs to. + * @param[in] GROUP Pointer to the target @ref runtime_config_group_t. + */ +#define RUNTIME_CONFIG_NODE_GROUP(SCHEMA_INSTANCE, GROUP) \ + { \ + .type = RUNTIME_CONFIG_NODE_TYPE_GROUP, \ + .as_group = { \ + .schema_instance = SCHEMA_INSTANCE, \ + .group = GROUP, \ + }, \ + } + +/** + * @brief Initializes a runtime configuration node pointing to a configuration parameter. + * + * @param[in] SCHEMA_INSTANCE Pointer to the @ref runtime_config_schema_instance_t + * that this parameter belongs to. + * @param[in] PARAMETER Pointer to the target @ref runtime_config_parameter_t. + */ +#define RUNTIME_CONFIG_NODE_PARAMETER(SCHEMA_INSTANCE, PARAMETER) \ + { \ + .type = RUNTIME_CONFIG_NODE_TYPE_PARAMETER, \ + .as_parameter = { \ + .schema_instance = SCHEMA_INSTANCE, \ + .parameter = PARAMETER, \ + }, \ + } + +/** + * @brief Callback to apply all parameters within a specific schema instance. + * + * This is triggered when a full synchronization is required or when multiple + * changes occur simultaneously. It allows for atomic updates, such as setting + * R, G, and B values at once to prevent flickering or invalid intermediate states. + * + * @param[in] schema_instance Pointer to the instance containing the full dataset + * and context to be applied. + * + * @return 0 on success, non-zero error code on failure. + */ +typedef runtime_config_error_t (*runtime_config_apply_schema_instance_cb_t)( + const runtime_config_schema_instance_t *const schema_instance); + +/** + * @brief Callback to apply changes to a specific group or parameter. + * + * This is triggered for granular updates. Depending on the system design, the + * @p group_or_parameter_id refers to either a logical collection of parameters + * or a single leaf node value. + * + * @param[in] schema_instance The parent instance context. + * @param[in] group_or_parameter_id The identifier for the specific group or + * parameter that has changed. + * + * @return 0 on success, non-zero error code on failure. + */ +typedef runtime_config_error_t (*runtime_config_apply_group_or_parameter_cb_t)( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id); + +/** + * @brief Instance of a schema containing its configuration parameter values. + * + * The instance of a configuration schema is created by consumers of that schema + * who need to expose runtime configurations. + * The consumers also need to implement the `apply_cb` function to get informed + * when configurations change. + */ +struct runtime_config_schema_instance { + /** Linked list node pointing to the next schema instance. */ + clist_node_t node; + + /** ID of the instance within the scope of its schema. */ + runtime_config_schema_instance_id_t id; + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + /** String describing the instance. */ + const char *const name; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration group with more details. */ + const char *const description; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + + /** Generated struct containing all configuration parameters of the schema. + * Usually generated from schema YAML files using the "make runtime_config + * generate" command. */ + const void *const data; + + /** Configuration schema that the instance belongs to. */ + const runtime_config_schema_t *schema; + + /** + * @brief Called when a full synchronization of the schema instance is + * requested. + * + * Triggered when @ref runtime_config_apply is called targeting the entire + * instance. + * Use this for global updates or atomic multi-parameter synchronization. + */ + runtime_config_apply_schema_instance_cb_t apply_schema_instance_cb; + + /** + * @brief Called when a specific group or parameter update is requested. + * + * Triggered when @ref runtime_config_apply is called targeting a specific + * ID. This allows for localized updates without re-processing the entire + * schema instance. + */ + runtime_config_apply_group_or_parameter_cb_t apply_group_or_parameter_cb; + + /** Optional context used by the schema instance. */ + void *context; +}; + +/** + * @brief Data structure of a configuration group. + * + * A configuration group contains further configuration groups and/or + * configuration parameters. It has an ID that is unique within the scope + * of its parent configuration schema. + */ +struct runtime_config_group { + /** Integer representing the ID of the configuration group. */ + const runtime_config_group_id_t id; + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration group. */ + const char *const name; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration group with more details. */ + const char *const description; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + + /** Configuration schema that the configuration group belongs to. */ + const runtime_config_schema_t *const schema; + + /** Array of pointers to all the configuration groups that belong to this + * group. */ + const runtime_config_group_t **const groups; + + /** Size of groups array. */ + const size_t groups_len; + + /** Array of pointers to all the configuration parameters that belong to + * this group. */ + const runtime_config_parameter_t **const parameters; + + /** Size of parameters array. */ + const size_t parameters_len; +}; + +/** + * @brief Data structure of a configuration parameter. + * + * A configuration parameter mostly holds the type information for a configuration value. + * It has an ID that is unique within the scope of its parent configuration schema. + */ +struct runtime_config_parameter { + /** Integer representing the ID of the configuration parameter. */ + const runtime_config_parameter_id_t id; + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration parameter. */ + const char *const name; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration parameter with more details. */ + const char *const description; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + + /** Configuration schema that the configuration parameter belongs to. */ + const runtime_config_schema_t *const schema; + + /** Type of the configuration parameter. */ + const runtime_config_type_t type; + + /** Number of elements (1 for scalar, >1 for array). */ + const size_t count; +}; + +/** + * @brief Data structure of a configuration schema. + * + * A configuration schema contains configuration groups and/or + * configuration parameters. + * Besides that, it also contains a list of schema instances that hold the + * actual configuration parameter values. + * It has an ID that is unique within the scope of its parent configuration namespace. + */ +struct runtime_config_schema { + /** Integer representing the ID of the schema. */ + const runtime_config_schema_id_t id; + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + /** String describing the schema. */ + const char *const name; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + /** String describing the schema with more details. */ + const char *const description; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + + /** Configuration namespace that the schema belongs to. */ + const runtime_config_namespace_t *const namespace; + + /** Linked list of schema instances (@ref runtime_config_schema_instance_t). */ + clist_node_t instances; + + /** Array of pointers to all the configuration groups that belong to this schema. */ + const runtime_config_group_t **const groups; + + /** Size of groups array. */ + const size_t groups_len; + + /** Array of pointers to all the configuration parameters that belong to this schema. */ + const runtime_config_parameter_t **const parameters; + + /** Size of parameters array. */ + const size_t parameters_len; + + /** + * @brief Get the value of a configuration parameter of a specific schema + * instance. + * + * A configuration schema has many parameters, and a configuration schema + * instance stores their concrete values in a struct provided in the `data` + * field. With this function, it is possible to get a pointer to the values + * of this struct based on the ID of the configuration parameter. + * This way, it is possible for the runtime_config module to make the + * connection between the configuration schema meta-data and the actual + * data in the schema instances. + * + * @param[in] parameter_id ID of the parameter that contains the value. + * @param[in] instance Pointer to the schema instance that contains the parameter. + * @param[out] val Pointer to buffer containing the new value. + * @param[out] val_len Pointer to length of the buffer to store the current value. + * + * @internal + */ + void (*const get_parameter_value_from_instance)( + const runtime_config_parameter_id_t parameter_id, + const runtime_config_schema_instance_t *instance, + void **val, + size_t *val_len); +}; + +/** + * @brief Data structure of a configuration namespace. + * + * A configuration namespace contains configuration schemas. + * It exists to prevent ID collisions between RIOT internal configuration + * schemas and schemas defined by applications running on RIOT. + * It has an ID that is unique within the scope of the runtime configuration + * itself. + */ +struct runtime_config_namespace { + /** Integer representing the ID of the namespace. */ + const runtime_config_namespace_id_t id; + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration namespace. */ + const char *const name; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + /** String describing the configuration namespace with more details. */ + const char *const description; +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + + /** Array of pointers to all the configuration schemas that belong to this namespace. */ + const runtime_config_schema_t **const schemas; + + /** Size of schemas array. */ + const size_t schemas_len; +}; + +/** + * @brief Add a namespace to the cross-file-array containing all runtime + * configuration namespaces. + */ +#define RUNTIME_CONFIG_ADD_NAMESPACE(_name, _namespace) \ + XFA_USE_CONST(runtime_config_namespace_t *, _runtime_config_namespaces_xfa); \ + XFA_ADD_PTR(_runtime_config_namespaces_xfa, _name, _name, &_namespace) + +/** + * @brief Initializes the runtime configuration system. + */ +void runtime_config_init(void); + +/** + * @brief Adds a new instance to a schema. + * + * @param[in] schema Pointer to the schema. + * @param[in] schema_instance Pointer to the new instance. + * + * @return 0 on success, non-zero on failure. + */ +runtime_config_error_t runtime_config_add_schema_instance( + runtime_config_schema_t *schema, + runtime_config_schema_instance_t *schema_instance); + +/** + * @brief Gets a pointer to the current value of a parameter that belongs to an + * instance of a configuration schema. + * + * @param[in] node A location within the runtime configuration tree. + * Must be of the type "RUNTIME_CONFIG_NODE_TYPE_PARAMETER". + * @param[out] buf Pointer to a pointer that will be updated to point to + * the internal configuration value. Must not be NULL. + * @param[out] buf_len Pointer to a variable where the length of the + * configuration value will be written. Must not be NULL. + * + * @return 0 on success, non-zero on failure. + */ +runtime_config_error_t runtime_config_get( + const runtime_config_node_t *node, + void **buf, + size_t *buf_len); + +/** + * @brief Sets the value of a configuration parameter that belongs + * to an instance of a configuration schema. + * + * @param[in] node A location within the runtime configuration tree. + * Must be of the type "RUNTIME_CONFIG_NODE_TYPE_PARAMETER". + * @param[in] buf Pointer to the new value for the configuration parameter. + * Must not be NULL. + * @param[in] buf_len Length of the buffer. Must be larger than 0. + * + * @return 0 on success, non-zero on failure. + */ +runtime_config_error_t runtime_config_set( + const runtime_config_node_t *node, + const void *buf, + const size_t buf_len); + +/** + * @brief Applies every configuration parameter within the given configuration + * location (@ref runtime_config_node_t) of the runtime configuration tree. + * + * @param[in] node Optional location within the runtime configuration tree. + * Applies all existing configurations if NULL. + * + * @return 0 on success, non-zero on failure. + */ +runtime_config_error_t runtime_config_apply(const runtime_config_node_t *node); + +/** + * @brief Callback definition of the @ref runtime_config_traverse_config_tree + * function. + * + * This callback will be called for each location inside of the + * configuration tree that is within the scope of the runtime configuration node + * passed on to the @ref runtime_config_traverse_config_tree function. + * + * @param[in] node A location within the runtime configuration tree. + * @param[in] context Context that is passed by the + * @ref runtime_config_traverse_config_tree function. + * + * @return 0 on success, non-zero on failure. + */ +typedef runtime_config_error_t (*runtime_config_tree_traversal_cb_t)( + const runtime_config_node_t *node, + const void *context); + +/** + * @brief In this mode, the function traverses through every child of the + * given node including all sub-children when calling the + * @ref runtime_config_traverse_config_tree function. + */ +#define RUNTIME_CONFIG_TRAVERSE_ALL 0 + +/** + * @brief In this mode, the function only traverses through the given node when + * calling the @ref runtime_config_traverse_config_tree function. + */ +#define RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE 1 + +/** + * @brief This mode traverses through the tree until the given node plus n-1 + * levels of children when calling the + * @ref runtime_config_traverse_config_tree function. + */ +#define RUNTIME_CONFIG_TRAVERSE_TREE_WITH_N_LEVELS_OF_CHILDREN(_n) (((_n) + 1)) + +/** + * @brief Iterates over every configuration parameter within the given + * configuration location (@ref runtime_config_node_t) of the runtime + * configuration tree. + * + * @param[in] node A location within the runtime configuration tree. + * @param[in] tree_traversal_cb Callback function that will be called for + * each namespace, schema, schema instance, group + * and parameter that are within the + * @p tree_traversal_depth and specified @p node. + * @param[in] tree_traversal_depth Defines how deeply nested child + * groups / parameters will be shown: + * 0 to show all children, + * 1 to only show the exact match, + * and n > 1 to show the exact match plus n - 1 + * levels of children. + * @param[in] context Context that will be passed to @p tree_traversal_cb. + * + * @return 0 on success, non-zero on failure. + */ +runtime_config_error_t runtime_config_traverse_config_tree( + const runtime_config_node_t *node, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context); + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/sys/include/runtime_config/error.h b/sys/include/runtime_config/error.h new file mode 100644 index 000000000000..36a7535d1a6f --- /dev/null +++ b/sys/include/runtime_config/error.h @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config_error Runtime configuration error + * @ingroup sys_runtime_config + * @brief Runtime configuration "Error" module providing runtime + * configuration module specific error codes + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "errno.h" + +/** + * @brief Runtime configuration module specific error codes. + */ +typedef enum { + RUNTIME_CONFIG_ERROR_NONE = 0, + RUNTIME_CONFIG_ERROR_INVALID_ARGUMENT = EINVAL, + RUNTIME_CONFIG_ERROR_NAMESPACE_NOT_FOUND, + RUNTIME_CONFIG_ERROR_SCHEMA_NOT_FOUND, + RUNTIME_CONFIG_ERROR_INSTANCE_NOT_FOUND, + RUNTIME_CONFIG_ERROR_GROUP_NOT_FOUND, + RUNTIME_CONFIG_ERROR_PARAMETER_NOT_FOUND, + RUNTIME_CONFIG_ERROR_NODE_INVALID, + RUNTIME_CONFIG_ERROR_BUF_LEN_TOO_SMALL, + RUNTIME_CONFIG_ERROR_BUF_LEN_TOO_LARGE, +} runtime_config_error_t; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/sys/include/runtime_config/namespace/sys.h b/sys/include/runtime_config/namespace/sys.h new file mode 100644 index 000000000000..70d1272bebec --- /dev/null +++ b/sys/include/runtime_config/namespace/sys.h @@ -0,0 +1,45 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config_namespace_sys Runtime configuration "sys" + * namespace + * @ingroup sys_runtime_config + * @brief Runtime configuration namespace "sys" module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +/** + * @brief Sys namespace for system modules. + */ +extern runtime_config_namespace_t runtime_config_sys; + +/** + * @brief This ENUM defines the IDs of configuration schemas in the "sys" namespace. + */ +typedef enum { + RUNTIME_CONFIG_SYS_BOARD_LED = 0, + RUNTIME_CONFIG_SYS_RGB_LED = 1, +} runtime_config_sys_indices_t; + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/sys/include/runtime_config/namespace/sys/board_led.h b/sys/include/runtime_config/namespace/sys/board_led.h new file mode 100644 index 000000000000..e5bf9f1493b5 --- /dev/null +++ b/sys/include/runtime_config/namespace/sys/board_led.h @@ -0,0 +1,59 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config_namespace_sys_board_led Runtime configuration + * schema: BOARD_LED + * @ingroup sys_runtime_config_namespace_sys + * @brief Runtime configuration BOARD_LED schema representing the basic + * structure of a BOARD LED + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +/** + * @brief Parameter descriptor for the "enabled" parameter of the board LED schema. + */ +extern const runtime_config_parameter_t runtime_config_sys_board_led_enabled; + +/** + * @brief Schema descriptor for a board LED. + */ +extern runtime_config_schema_t runtime_config_sys_board_led; + +/** + * @brief Schema instance data struct of a board LED schema. + */ +typedef struct { + /** Whether the LED is enabled. */ + bool enabled; +} runtime_config_sys_board_led_instance_t; + +/** + * @brief Parameter IDs for the board LED schema. + */ +typedef const enum { + /** Enabled state. */ + RUNTIME_CONFIG_SYS_BOARD_LED_ENABLED, +} runtime_config_sys_board_led_ids_t; + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/sys/include/runtime_config/namespace/sys/rgb_led.h b/sys/include/runtime_config/namespace/sys/rgb_led.h new file mode 100644 index 000000000000..401f42fe77ee --- /dev/null +++ b/sys/include/runtime_config/namespace/sys/rgb_led.h @@ -0,0 +1,81 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config_namespace_sys_rgb_led Runtime configuration + * schema: RGB_LED + * @ingroup sys_runtime_config_namespace_sys + * @brief Runtime configuration RGB_LED schema representing the basic + * structure of an RGB LED + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +/** + * @brief Parameter descriptor for the "red" parameter of the RGB-LED schema. + */ +extern const runtime_config_parameter_t runtime_config_sys_rgb_led_red; + +/** + * @brief Parameter descriptor for the "green" parameter of the RGB-LED schema. + */ +extern const runtime_config_parameter_t runtime_config_sys_rgb_led_green; + +/** + * @brief Parameter descriptor for the "blue" parameter of the RGB-LED schema. + */ +extern const runtime_config_parameter_t runtime_config_sys_rgb_led_blue; + +/** + * @brief Schema descriptor for an RGB-LED. + */ +extern runtime_config_schema_t runtime_config_sys_rgb_led; + +/** + * @brief Schema instance struct of a RGB-LED schema. + */ +typedef struct { + /** Red color. */ + uint8_t red; + + /** Green color. */ + uint8_t green; + + /** Blue color. */ + uint8_t blue; +} runtime_config_sys_rgb_led_instance_t; + +/** + * @brief Parameter IDs for the RGB-LED schema. + */ +typedef const enum { + /** Red color. */ + RUNTIME_CONFIG_SYS_RGB_LED_RED, + + /** Green color. */ + RUNTIME_CONFIG_SYS_RGB_LED_GREEN, + + /** Blue color. */ + RUNTIME_CONFIG_SYS_RGB_LED_BLUE, +} runtime_config_sys_rgb_led_ids_t; + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/sys/runtime_config/Kconfig b/sys/runtime_config/Kconfig new file mode 100644 index 000000000000..3841ed2166b9 --- /dev/null +++ b/sys/runtime_config/Kconfig @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow +# SPDX-FileCopyrightText: 2023-2026 HAW Hamburg +# SPDX-License-Identifier: LGPL-2.1-only + +menuconfig MODULE_RUNTIME_CONFIG + bool "RUNTIME_CONFIG" + help + Runtime configuration module for handling runtime configurations. + +if USEMODULE_RUNTIME_CONFIG + +config RUNTIME_CONFIG_ENABLE_META_NAME + bool "Include name string in schemas" + +config RUNTIME_CONFIG_ENABLE_META_DESCRIPTION + bool "Include description string in schemas" + +endif # USEMODULE_RUNTIME_CONFIG diff --git a/sys/runtime_config/Makefile b/sys/runtime_config/Makefile new file mode 100644 index 000000000000..73f7c2149e71 --- /dev/null +++ b/sys/runtime_config/Makefile @@ -0,0 +1,9 @@ +SRC := runtime_config.c init.c + +SUBMODULES := 1 + +ifneq (,$(filter runtime_config_namespace_%,$(USEMODULE))) + DIRS += namespace +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/runtime_config/Makefile.dep b/sys/runtime_config/Makefile.dep new file mode 100644 index 000000000000..a68c43011497 --- /dev/null +++ b/sys/runtime_config/Makefile.dep @@ -0,0 +1,11 @@ +ifneq (,$(filter runtime_config_namespace%,$(USEMODULE))) + include $(RIOTBASE)/sys/runtime_config/namespace/Makefile.dep +endif + +ifneq (,$(filter runtime_config_util%,$(USEMODULE))) + USEMODULE += base64 +endif + +ifneq (,$(filter runtime_config_%,$(USEMODULE))) + USEMODULE += runtime_config +endif diff --git a/sys/runtime_config/docs/api_structure.svg b/sys/runtime_config/docs/api_structure.svg new file mode 100644 index 000000000000..4a329cf12f54 --- /dev/null +++ b/sys/runtime_config/docs/api_structure.svg @@ -0,0 +1 @@ +
Storage
Storage
Core
Core
Core Setup
Core Setup
get
get
set
set
apply
apply
export
export
add_namespace
add_namespace
add_schema
_instance
add_schema...
Storage Setup
Storage Setup
add_storage
_source
add_storage...
set_storage
_destination
set_storage...
load
load
save
save
Text is not SVG - cannot display
\ No newline at end of file diff --git a/sys/runtime_config/docs/architecture.svg b/sys/runtime_config/docs/architecture.svg new file mode 100644 index 000000000000..97833c17d101 --- /dev/null +++ b/sys/runtime_config/docs/architecture.svg @@ -0,0 +1,3 @@ + + +
«component»
Runtime Configurations
«component»...
«component»
Registry
«component»...
«config manager»
LWM2M
«config manager»...
CoAP
CoAP
Path Based Configuration Managers
«config manager»
Config CLI (Riot Shell)
«config manager»...
Serial
Serial
«config manager»
CoAP API
«config manager»...
UDP
UDP
«config manager»
MQTT API
«config manager»...
TCP
TCP
Applications / Drivers
«application»
My app
«application»...
«driver»
BME280
«driver»...
«driver»
WS2812
«driver»...
Custom Schema Based
Configuration Managers
Custom Schema Based...
«component»
Storage
«component»...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/sys/runtime_config/docs/doc.md b/sys/runtime_config/docs/doc.md new file mode 100644 index 000000000000..64dd00657a31 --- /dev/null +++ b/sys/runtime_config/docs/doc.md @@ -0,0 +1,262 @@ +@addtogroup sys_runtime_config Runtime configuration + +@warning This implementation is not complete and not yet thoroughly tested. + Please do not use this module in production, as it is missing major + features and may contain bugs. + Missing features include: + - A code generator that transpiles YAML schemas to C code + - Persistent storage extension + - Integer path extension + - String path extension + +This module provides a system-level runtime configuration system for RIOT. + +A runtime configuration system provides a mechanism to set and get the values of +configuration parameters used during the execution of the firmware, as well as a +way to persist these values. Runtime configurations are deployment-specific and +can be changed on a per-node basis. Appropriate management tools also enable +remote configuration of RIOT nodes. + +Examples of runtime configurations include: + +- Transmission duty cycles +- Sensor thresholds +- Security credentials +- System state variables +- RGB-LED colors + +The runtime configuration system allows you to: + +- **Apply** multiple configurations at once. +- **Persist** configurations to non-volatile storage. +- **Expose** configurations to external configuration management tools, such as + a CLI or a network-based remote tool. + +## Design + +### Architecture + +The architecture, as shown below, is formed by one or more applications or +configuration managers and the runtime configuration API. The runtime +configuration API acts as a common interface to access runtime configurations +and store them in non-volatile storage. + +All runtime configurations can be accessed from the RIOT application either +using the provided runtime configuration interfaces or through the interfaces +exposed by the configuration managers. A RIOT application may interact with a +configuration manager to modify access control rules or enable different exposed +interfaces. + +#### Path-Based Configuration Managers (Needs `int_path` or `string_path` extension) + +@warning There are no Path-Based Configuration Managers implemented yet. + +These configuration managers mirror the internal structure of the RIOT runtime +configuration tree. They use either the `int_path` or the `string_path` +extension module to expose the parameters via a path of strings or integers. + +#### Custom Schema-Based Configuration Managers + +@warning There are no Custom Schema-Based Configuration Managers implemented yet. + +These configuration managers have their own configuration structure (e.g., +custom predefined object models) and cannot automatically be mapped to or from +the runtime configuration schemas. To make them work, a custom mapping module +must be implemented per configuration manager, mapping each configuration +parameter from the RIOT runtime configuration module to the correct format of +the configuration manager. + +design_architecture + +### Configuration Structure + +The runtime configuration system interacts with RIOT modules via **configuration schemas**, +and with non-volatile storages via **storages**. This ensures the functionality +of the runtime configuration remains independent of specific module or storage +implementations. + +Through this structure, it is possible to get or set the values of **configuration parameters**. +It is also possible to apply configurations, export their values to a buffer, or +print them. To persist configuration values, they can be stored in non-volatile +storages. + +Mechanisms for security (such as access control or encryption of configurations) +are **not** directly in the scope of the runtime configuration module, but are +handled by the configuration managers and the specific implementations of the +configuration schemas and storages. + +The graphic below shows an example of two configuration namespaces (`SYS` and +`APP`). +The `APP` namespace contains an application-specific `My app` configuration schema, +and the `SYS` namespace specifies a `WLAN` and an `LED Strip` configuration schema. +The application `My app` uses the custom `My app` schema to expose custom parameters. +Meanwhile, the drivers `WS2812`, `SK6812`, and `UCS1903` contain instances of +the `LED Strip` configuration schema to expose common LED strip parameters. + +Additionally, there are two storages available: `MTD` and `VFS`. The `MTD` +storage internally uses the RIOT `MTD` driver, while the `VFS` storage +internally uses the RIOT `VFS` module. + +design_namespaces_and_storages + +### Components + +The runtime configuration system is split into multiple components, as seen in +the graphic below: + +#### Runtime Configuration Core + +```makefile +USEMODULE += runtime_config +``` + +This component holds the most basic functionality of the runtime configuration system. +It allows you to `set` and `get` configuration values, transactionally `apply` +them so changes take effect, and `list` all configuration parameters that exist +in a given namespace, schema, or group. Furthermore, it provides the ability to +`add` namespaces or schema instances. + +#### Runtime Configuration Namespace + +The configuration namespaces (such as `SYS` or `APP`) and their respective +schemas are not inherently part of the runtime configuration module itself. +It is possible to `add` custom configuration namespaces depending on your +specific application needs. + +design_runtime_config + +## API + +The graphic below illustrates the API of the runtime configuration system. +The top shows the Core API for managing configuration parameters. On the +right-hand side are functions to `set`, `get`, `apply`, and `list` configuration +parameters to a buffer or terminal. On the left-hand side are setup functions to +`add` namespaces and schema instances. + +The functionality of these functions is detailed in the following sections. + +api_structure + +## Usage + +### Add Namespaces + +To be able to use configuration schemas and their parameters, you must first add +a configuration namespace that will contain the required schemas. + +This is done using the `RUNTIME_CONFIG_ADD_NAMESPACE` macro, providing the +name of the namespace and a pointer to a `runtime_config_namespace_t` object. + +```c +#define RUNTIME_CONFIG_ADD_NAMESPACE(_name, _namespace) +``` + +### Add Configuration Schema Instances + +To expose runtime configurations, it is necessary to add an instance of the +required configuration schema. + +This is done using the `runtime_config_add_schema_instance` function, providing +the schema and the schema instance as arguments. + +```c +#include "runtime_config.h" +#include "runtime_config/namespace/sys.h" +#include "runtime_config/namespace/sys/board_led.h" + +static runtime_config_error_t board_led_instance_apply_cb( + const runtime_config_group_or_parameter_id_t *group_or_parameter_id, + const runtime_config_schema_instance_t *instance) +{ + /* Handle configuration changes */ + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_sys_board_led_instance_t board_led_instance_data = { + .enabled = 0, +}; + +static runtime_config_schema_instance_t board_led_instance = { + .data = &board_led_instance_data, + .apply_cb = &board_led_instance_apply_cb, +}; + +/* Assuming runtime_config_sys_board_led is defined elsewhere */ +runtime_config_add_schema_instance(&runtime_config_sys_board_led, &board_led_instance); +``` + +### Get Configurations + +A configuration value can be retrieved using the `runtime_config_get` function. +The function takes a pointer to a `runtime_config_node_t` as input, alongside +a double pointer to a buffer and a pointer to the buffer's size. + +After execution, the output pointer (`*buf`) will point directly to the internal +buffer holding the actual configuration value, and `*buf_len` will hold its size. + +```c +runtime_config_error_t runtime_config_get( + const runtime_config_node_t *node, + void **buf, + size_t *buf_len +); +``` + +### Set Configurations + +A configuration value can be set using the `runtime_config_set` function. +The function takes a `runtime_config_node_t`, a `const void *` buffer containing +the new value, and the buffer's size as its arguments. + +The buffer must contain the value in its correct C data type. For example, if +the configuration schema expects a `uint8_t` but a `uint16_t` is provided, +the operation will fail. + +```c +runtime_config_error_t runtime_config_set( + const runtime_config_node_t *node, + const void *buf, + const size_t buf_len +); +``` + +### Apply Configurations + +Once the value(s) of one or multiple configuration parameters are changed via +the `runtime_config_set` function, they still need to be applied for the new +values to take effect. + +Parameters can be applied using the `runtime_config_apply` function. +This function applies every configuration parameter currently available in the +runtime configuration tree that is a direct or indirect child of the specified +location. + +When a whole schema instance or a single configuration parameter is applied, it +is passed to the `apply_cb` handler of the schema instance (provided by the +module that owns the configuration). This ensures the module is notified when +parameters are applied and can react to the changes accordingly. + +```c +runtime_config_error_t runtime_config_apply(const runtime_config_node_t *node); +``` + +### List Available Configurations + +Sometimes it is necessary to see what configuration namespaces, schemas, +instances, groups, or parameters are currently available within the deployment. + +This information can be retrieved using the `runtime_config_traverse_config_tree` +function. This function iterates over every configuration object available in +the configuration tree, restricted to the scope of the provided `node` argument. + +Each traversed node will be passed to the `tree_traversal_cb` callback provided +as an argument. + +```c +runtime_config_error_t runtime_config_traverse_config_tree( + const runtime_config_node_t *node, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context +); +``` diff --git a/sys/runtime_config/docs/namespaces_and_storages.svg b/sys/runtime_config/docs/namespaces_and_storages.svg new file mode 100644 index 000000000000..00d717ee7268 --- /dev/null +++ b/sys/runtime_config/docs/namespaces_and_storages.svg @@ -0,0 +1 @@ +
Applications / Modules
   
Applications / Modules...
Riot Registry API
Riot Registry API
«component»
core
«component»...
«component»
storage
«component»...
Namespaces
Namespaces
«schema»
My app
«schema»...
«schema»
LED Strip
«schema»...
«driver»
WS2812
«driver»...
«application»
My app
«application»...
instance
instance
instance
instance
«namespace»
APP
«namespace»...
«namespace»
SYS
«namespace»...
«schema»
WLAN
«schema»...
«driver»
ESP_WIFI
«driver»...
instance
instance
«driver»
SK6812
«driver»...
«driver»
UCS1903
«driver»...
Storage Devices
Storage Devices
«storage»
VFS
«storage»...
«storage»
MTD
«storage»...
«module»
VFS
«module»...
«driver»
MTD
«driver»...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/sys/runtime_config/docs/runtime_config.svg b/sys/runtime_config/docs/runtime_config.svg new file mode 100644 index 000000000000..01667fed506e --- /dev/null +++ b/sys/runtime_config/docs/runtime_config.svg @@ -0,0 +1 @@ +
RIOT Registry API
RIOT Registry API
storage
storage
Path Based
Configuration Managers
Path Based...
core
core
string_path
string_path
CLI
CLI
Namespaces
Namespaces
SYS
SYS
APP
APP
...
...
GET
GET
SET
SET
ADD_NAMESPACE
ADD_NAMESPACE
ADD_SCHEMA_INSTANCE
ADD_SCHEMA_INSTANCE
TO_STRING_PATH
TO_STRING_PATH
FROM_STRING_PATH
FROM_STRING_PATH
CoAP API
CoAP API
MQTT API
MQTT API
LOAD
LOAD
SAVE
SAVE
Schema Based
Configuration
Managers
Schema Based...
LwM2M
LwM2M
REST API
REST API
Storage Devices
Storage Devices
VFS
VFS
...
...
MTD
MTD
APPLY
APPLY
EXPORT
EXPORT
int_path
int_path
TO_INT_PATH
TO_INT_PATH
FROM_INT_PATH
FROM_INT_PATH
Text is not SVG - cannot display
\ No newline at end of file diff --git a/sys/runtime_config/init.c b/sys/runtime_config/init.c new file mode 100644 index 000000000000..c4b281e4350d --- /dev/null +++ b/sys/runtime_config/init.c @@ -0,0 +1,45 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_runtime_config_init + * @brief Runtime configuration init module providing init functions + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include "runtime_config.h" + +#include "auto_init_utils.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief Runtime configuration auto init priority + */ +#ifndef AUTO_INIT_PRIO_MOD_RUNTIME_CONFIG +# define AUTO_INIT_PRIO_MOD_RUNTIME_CONFIG 2000 +#endif + +XFA_USE_CONST(runtime_config_namespace_t *, _runtime_config_namespaces_xfa); + +void runtime_config_init(void) +{ + /* set namespace_id to its index value */ + for (size_t i = 0; i < XFA_LEN(runtime_config_namespace_t *, _runtime_config_namespaces_xfa); i++) { + runtime_config_namespace_t *namespace = _runtime_config_namespaces_xfa[i]; + + *(runtime_config_namespace_id_t *)&namespace->id = i; + } +} + +AUTO_INIT(runtime_config_init, AUTO_INIT_PRIO_MOD_RUNTIME_CONFIG); diff --git a/sys/runtime_config/namespace/Makefile b/sys/runtime_config/namespace/Makefile new file mode 100644 index 000000000000..645865a7c0b5 --- /dev/null +++ b/sys/runtime_config/namespace/Makefile @@ -0,0 +1,5 @@ +ifneq (,$(filter runtime_config_namespace_sys%,$(USEMODULE))) + DIRS += sys +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/runtime_config/namespace/Makefile.dep b/sys/runtime_config/namespace/Makefile.dep new file mode 100644 index 000000000000..6db07d503b82 --- /dev/null +++ b/sys/runtime_config/namespace/Makefile.dep @@ -0,0 +1,3 @@ +ifneq (,$(filter runtime_config_namespace_sys%,$(USEMODULE))) + include $(RIOTBASE)/sys/runtime_config/namespace/sys/Makefile.dep +endif diff --git a/sys/runtime_config/namespace/sys/Makefile b/sys/runtime_config/namespace/sys/Makefile new file mode 100644 index 000000000000..a10bd254325a --- /dev/null +++ b/sys/runtime_config/namespace/sys/Makefile @@ -0,0 +1,11 @@ +# Autogenerated by `make runtime_config generate`, do not edit. + +MODULE := runtime_config_namespace_sys + +BASE_MODULE := runtime_config + +SRC := namespace_sys.c + +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/sys/runtime_config/namespace/sys/Makefile.dep b/sys/runtime_config/namespace/sys/Makefile.dep new file mode 100644 index 000000000000..d82afb15800c --- /dev/null +++ b/sys/runtime_config/namespace/sys/Makefile.dep @@ -0,0 +1,6 @@ +# Autogenerated by `make runtime_config generate`, do not edit. + +# Enable "runtime_config_namespace_sys" module for all schemas +ifneq (,$(filter runtime_config_namespace_sys_%,$(USEMODULE))) + USEMODULE += runtime_config_namespace_sys +endif diff --git a/sys/runtime_config/namespace/sys/definitions/0000_board_led.yaml b/sys/runtime_config/namespace/sys/definitions/0000_board_led.yaml new file mode 100644 index 000000000000..ac7c2d1abb60 --- /dev/null +++ b/sys/runtime_config/namespace/sys/definitions/0000_board_led.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../../../../dist/tools/runtime_config_gen/schema.json +id: 0 +name: board_led +description: A board LED. +items: + - id: 0 + name: enabled + description: Whether the board LED is enabled + type: bool diff --git a/sys/runtime_config/namespace/sys/definitions/0001_rgb_led.yaml b/sys/runtime_config/namespace/sys/definitions/0001_rgb_led.yaml new file mode 100644 index 000000000000..e5bb1213eb55 --- /dev/null +++ b/sys/runtime_config/namespace/sys/definitions/0001_rgb_led.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../../../../../dist/tools/runtime_config_gen/schema.json +id: 1 +name: rgb +description: An RGB-LED +items: + - id: 0 + name: red + description: The red component of the color + type: uint8 + + - id: 1 + name: green + description: The green component of the color + type: uint8 + + - id: 2 + name: blue + description: The blue component of the color + type: uint8 diff --git a/sys/runtime_config/namespace/sys/namespace_sys.c b/sys/runtime_config/namespace/sys/namespace_sys.c new file mode 100644 index 000000000000..855bafc6a646 --- /dev/null +++ b/sys/runtime_config/namespace/sys/namespace_sys.c @@ -0,0 +1,53 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_runtime_config_namespace_sys + * @brief Runtime configuration "Sys Namespace" module + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include + +#include "kernel_defines.h" +#include "runtime_config.h" + +#include "runtime_config/namespace/sys.h" +#include "runtime_config/namespace/sys/rgb_led.h" +#include "runtime_config/namespace/sys/board_led.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static const runtime_config_schema_t *_schemas[] = { +#if IS_USED(MODULE_RUNTIME_CONFIG_NAMESPACE_SYS_RGB_LED) + &runtime_config_sys_rgb_led, +#endif +#if IS_USED(MODULE_RUNTIME_CONFIG_NAMESPACE_SYS_BOARD_LED) + &runtime_config_sys_board_led, +#endif +}; + +runtime_config_namespace_t runtime_config_sys = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "sys", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Contains configurations exposed by RIOT modules", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schemas = _schemas, + .schemas_len = ARRAY_SIZE(_schemas), +}; + +RUNTIME_CONFIG_ADD_NAMESPACE(sys, runtime_config_sys); diff --git a/sys/runtime_config/namespace/sys/namespace_sys_board_led.c b/sys/runtime_config/namespace/sys/namespace_sys_board_led.c new file mode 100644 index 000000000000..ed17c49f40e7 --- /dev/null +++ b/sys/runtime_config/namespace/sys/namespace_sys_board_led.c @@ -0,0 +1,84 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_runtime_config_namespace_sys_board_led + * @brief Runtime configuration "BOARD_LED Schema" representing the basic + * structure of a BOARD LED + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#include "kernel_defines.h" +#include "runtime_config.h" +#include "runtime_config/namespace/sys.h" + +#include "runtime_config/namespace/sys/board_led.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* Get parameter value from instance */ +static void _get_parameter_value_from_instance( + const runtime_config_parameter_id_t parameter_id, + const runtime_config_schema_instance_t *instance, + void **val, size_t *val_len) +{ + runtime_config_sys_board_led_instance_t *_instance = + (runtime_config_sys_board_led_instance_t *)instance->data; + + switch (parameter_id) { + case RUNTIME_CONFIG_SYS_BOARD_LED_ENABLED: + *val = &_instance->enabled; + *val_len = sizeof(_instance->enabled); + break; + } +} + +/* Schema parameters */ +const runtime_config_parameter_t runtime_config_sys_board_led_enabled = { + .id = RUNTIME_CONFIG_SYS_BOARD_LED_ENABLED, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "enabled", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Whether the board LED is enabled", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_sys_board_led, + .type = RUNTIME_CONFIG_TYPE_BOOL, + .count = 1, +}; + +/* Schema */ +runtime_config_schema_t runtime_config_sys_board_led = { + .id = RUNTIME_CONFIG_SYS_BOARD_LED, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "board_led", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "A board LED", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .namespace = &runtime_config_sys, + .get_parameter_value_from_instance = _get_parameter_value_from_instance, + .groups = NULL, + .groups_len = 0, + .parameters = (const runtime_config_parameter_t *[]){ + &runtime_config_sys_board_led_enabled, + }, + .parameters_len = 1, +}; diff --git a/sys/runtime_config/namespace/sys/namespace_sys_rgb_led.c b/sys/runtime_config/namespace/sys/namespace_sys_rgb_led.c new file mode 100644 index 000000000000..6579d30aa7b3 --- /dev/null +++ b/sys/runtime_config/namespace/sys/namespace_sys_rgb_led.c @@ -0,0 +1,122 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_runtime_config_namespace_sys_rgb_led + * @brief Runtime configuration "RGB_LED Schema" representing the basic + * structure of an RGB LED + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#include "kernel_defines.h" +#include "runtime_config.h" +#include "runtime_config/namespace/sys.h" + +#include "runtime_config/namespace/sys/rgb_led.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* Get parameter value from instance */ +static void _get_parameter_value_from_instance( + const runtime_config_parameter_id_t parameter_id, + const runtime_config_schema_instance_t *instance, + void **val, size_t *val_len) +{ + runtime_config_sys_rgb_led_instance_t *_instance = + (runtime_config_sys_rgb_led_instance_t *)instance->data; + + switch (parameter_id) { + case RUNTIME_CONFIG_SYS_RGB_LED_RED: + *val = &_instance->red; + *val_len = sizeof(_instance->red); + break; + + case RUNTIME_CONFIG_SYS_RGB_LED_GREEN: + *val = &_instance->green; + *val_len = sizeof(_instance->green); + break; + + case RUNTIME_CONFIG_SYS_RGB_LED_BLUE: + *val = &_instance->blue; + *val_len = sizeof(_instance->blue); + break; + } +} + +/* Schema parameters */ +const runtime_config_parameter_t runtime_config_sys_rgb_led_red = { + .id = RUNTIME_CONFIG_SYS_RGB_LED_RED, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "red", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "The red component of the color", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_sys_rgb_led, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_sys_rgb_led_green = { + .id = RUNTIME_CONFIG_SYS_RGB_LED_GREEN, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "green", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "The green component of the color", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_sys_rgb_led, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_sys_rgb_led_blue = { + .id = RUNTIME_CONFIG_SYS_RGB_LED_BLUE, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "blue", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "The blue component of the color", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_sys_rgb_led, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +/* Schema */ +runtime_config_schema_t runtime_config_sys_rgb_led = { + .id = RUNTIME_CONFIG_SYS_RGB_LED, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "rgb_led", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "An RGB-LED", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .namespace = &runtime_config_sys, + .get_parameter_value_from_instance = _get_parameter_value_from_instance, + .groups = NULL, + .groups_len = 0, + .parameters = (const runtime_config_parameter_t *[]){ + &runtime_config_sys_rgb_led_red, + &runtime_config_sys_rgb_led_green, + &runtime_config_sys_rgb_led_blue, + }, + .parameters_len = 3, +}; diff --git a/sys/runtime_config/runtime_config.c b/sys/runtime_config/runtime_config.c new file mode 100644 index 000000000000..ed6e39f0bf2b --- /dev/null +++ b/sys/runtime_config/runtime_config.c @@ -0,0 +1,419 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup sys_runtime_config + * @brief Runtime configuration for runtime configuration of modules + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#include "kernel_defines.h" +#include "clist.h" + +#include "runtime_config.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +XFA_INIT_CONST(runtime_config_namespace_t *, _runtime_config_namespaces_xfa); + +runtime_config_error_t runtime_config_add_schema_instance( + runtime_config_schema_t *schema, + runtime_config_schema_instance_t *schema_instance) +{ + assert(schema != NULL); + assert(schema_instance != NULL); + + /* add schema to instance */ + schema_instance->schema = schema; + + /* get instances length to determine the id of the new instance */ + size_t count = clist_count(&schema->instances); + + /* set id of new instance to the instance count */ + schema_instance->id = count; + + /* add instance to schema */ + clist_rpush(&schema->instances, &schema_instance->node); + + return RUNTIME_CONFIG_ERROR_NONE; +} + +runtime_config_error_t runtime_config_get( + const runtime_config_node_t *node, + void **buf, + size_t *buf_len) +{ + assert(node != NULL); + assert(buf != NULL); + assert(buf_len != NULL); + + if (node->type != RUNTIME_CONFIG_NODE_TYPE_PARAMETER) { + return -RUNTIME_CONFIG_ERROR_NODE_INVALID; + } + + /* get pointer to internal value buffer and length */ + void *intern_val = NULL; + size_t intern_val_len; + + const runtime_config_parameter_t *parameter = node->as_parameter.parameter; + + parameter->schema->get_parameter_value_from_instance( + parameter->id, node->as_parameter.schema_instance, &intern_val, &intern_val_len); + + /* update buf pointer to point to its internal value + * and set buf_len accordingly */ + *buf = intern_val; + *buf_len = intern_val_len; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +runtime_config_error_t runtime_config_set( + const runtime_config_node_t *node, + const void *buf, + const size_t buf_len) +{ + assert(node != NULL); + assert(buf != NULL); + assert(buf_len > 0); + + if (node->type != RUNTIME_CONFIG_NODE_TYPE_PARAMETER) { + return -RUNTIME_CONFIG_ERROR_NODE_INVALID; + } + + /* get pointer to the internal value buffer and length */ + void *intern_val = NULL; + size_t intern_val_len; + + const runtime_config_parameter_t *parameter = node->as_parameter.parameter; + + parameter->schema->get_parameter_value_from_instance( + parameter->id, node->as_parameter.schema_instance, &intern_val, &intern_val_len); + + if (buf_len > intern_val_len) { + return -RUNTIME_CONFIG_ERROR_BUF_LEN_TOO_LARGE; + } + + /* copy buffer to apply the new value to the correct instance of the schema */ + memcpy(intern_val, buf, buf_len); + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t _apply_tree_traversal_cb( + const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + assert(node != NULL); + + const runtime_config_schema_instance_t *schema_instance; + + switch (node->type) { + /* The apply function is only called for instance and below */ + case RUNTIME_CONFIG_NODE_TYPE_NAMESPACE: + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA: + return RUNTIME_CONFIG_ERROR_NONE; + + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE: + schema_instance = node->as_schema_instance; + return schema_instance->apply_schema_instance_cb(schema_instance); + + case RUNTIME_CONFIG_NODE_TYPE_GROUP: + schema_instance = node->as_group.schema_instance; + return schema_instance->apply_group_or_parameter_cb( + schema_instance, node->as_group.group->id); + + case RUNTIME_CONFIG_NODE_TYPE_PARAMETER: + schema_instance = node->as_parameter.schema_instance; + return schema_instance->apply_group_or_parameter_cb( + schema_instance, node->as_parameter.parameter->id); + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +runtime_config_error_t runtime_config_apply(const runtime_config_node_t *node) +{ + uint8_t tree_traversal_depth = RUNTIME_CONFIG_TRAVERSE_TREE_WITH_N_LEVELS_OF_CHILDREN(3); + + if (node != NULL) { + switch (node->type) { + case RUNTIME_CONFIG_NODE_TYPE_NAMESPACE: + tree_traversal_depth = RUNTIME_CONFIG_TRAVERSE_TREE_WITH_N_LEVELS_OF_CHILDREN(2); + break; + + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA: + tree_traversal_depth = RUNTIME_CONFIG_TRAVERSE_TREE_WITH_N_LEVELS_OF_CHILDREN(1); + break; + + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE: + case RUNTIME_CONFIG_NODE_TYPE_GROUP: + case RUNTIME_CONFIG_NODE_TYPE_PARAMETER: + tree_traversal_depth = RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE; + break; + } + } + + return runtime_config_traverse_config_tree( + node, + _apply_tree_traversal_cb, + tree_traversal_depth, + NULL); +} + +static runtime_config_error_t _runtime_config_traverse_parameter_tree( + const runtime_config_schema_instance_t *schema_instance, + const runtime_config_parameter_t *parameter, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const void *context) +{ + assert(parameter != NULL); + assert(schema_instance != NULL); + + const runtime_config_node_t tree_traversal_node = RUNTIME_CONFIG_NODE_PARAMETER( + schema_instance, parameter); + + return tree_traversal_cb(&tree_traversal_node, context); +} + +static runtime_config_error_t _runtime_config_traverse_group_tree( + const runtime_config_schema_instance_t *schema_instance, + const runtime_config_group_t *group, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(group != NULL); + assert(schema_instance != NULL); + + /* return the given configuration group */ + const runtime_config_node_t tree_traversal_node = RUNTIME_CONFIG_NODE_GROUP(schema_instance, group); + runtime_config_error_t rc = tree_traversal_cb(&tree_traversal_node, context); + + /* traverse through all children of the given configuration group + * if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE) { + return RUNTIME_CONFIG_ERROR_NONE; + } + else { + /* group */ + for (size_t i = 0; i < group->groups_len; i++) { + rc = _runtime_config_traverse_group_tree( + schema_instance, group->groups[i], tree_traversal_cb, + tree_traversal_depth - 1, context); + + if (rc != RUNTIME_CONFIG_ERROR_NONE) { + return rc; + } + } + + /* parameter */ + for (size_t i = 0; i < group->parameters_len; i++) { + rc = _runtime_config_traverse_parameter_tree( + schema_instance, group->parameters[i], tree_traversal_cb, context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } + } + + return rc; +} + +static runtime_config_error_t _runtime_config_traverse_schema_tree_instance( + const runtime_config_schema_instance_t *schema_instance, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(schema_instance != NULL); + + /* return the given configuration schema instance */ + const runtime_config_node_t tree_traversal_node = RUNTIME_CONFIG_NODE_SCHEMA_INSTANCE(schema_instance); + runtime_config_error_t rc = tree_traversal_cb(&tree_traversal_node, context); + + /* traverse through all groups or parameters of the given configuration + * schema instance if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE) { + return RUNTIME_CONFIG_ERROR_NONE; + } + else { + /* groups */ + for (size_t i = 0; i < schema_instance->schema->groups_len; i++) { + rc = _runtime_config_traverse_group_tree( + schema_instance, schema_instance->schema->groups[i], tree_traversal_cb, + tree_traversal_depth - 1, context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } + + /* parameters */ + for (size_t i = 0; i < schema_instance->schema->parameters_len; i++) { + rc = _runtime_config_traverse_parameter_tree( + schema_instance, schema_instance->schema->parameters[i], tree_traversal_cb, context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } + } + + return rc; +} + +static runtime_config_error_t _runtime_config_traverse_schema_tree( + const runtime_config_schema_t *schema, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(schema != NULL); + + /* return the given configuration schema */ + const runtime_config_node_t tree_traversal_node = RUNTIME_CONFIG_NODE_SCHEMA(schema); + runtime_config_error_t rc = tree_traversal_cb(&tree_traversal_node, context); + + /* traverse through all instances of the given configuration schema + * if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE) { + return RUNTIME_CONFIG_ERROR_NONE; + } + else { + clist_node_t *node = schema->instances.next; + + if (!node) { + return RUNTIME_CONFIG_ERROR_NONE; + } + + do { + node = node->next; + runtime_config_schema_instance_t *schema_instance = container_of( + node, + runtime_config_schema_instance_t, + node); + + rc = _runtime_config_traverse_schema_tree_instance( + schema_instance, + tree_traversal_cb, + tree_traversal_depth - 1, + context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } while (node != schema->instances.next); + } + + return rc; +} + +static runtime_config_error_t _runtime_config_traverse_namespace_tree( + const runtime_config_namespace_t *namespace, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(namespace != NULL); + + /* return the given namespace */ + const runtime_config_node_t tree_traversal_node = RUNTIME_CONFIG_NODE_NAMESPACE(namespace); + runtime_config_error_t rc = tree_traversal_cb(&tree_traversal_node, context); + + /* traverse through all configuration schemas of the given namespace + * if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE) { + return RUNTIME_CONFIG_ERROR_NONE; + } + else { + for (size_t i = 0; i < namespace->schemas_len; i++) { + const runtime_config_schema_t *child = namespace->schemas[i]; + + rc = _runtime_config_traverse_schema_tree( + child, tree_traversal_cb, tree_traversal_depth - 1, context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } + } + + return rc; +} + +runtime_config_error_t runtime_config_traverse_config_tree( + const runtime_config_node_t *node, + const runtime_config_tree_traversal_cb_t tree_traversal_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + runtime_config_error_t rc = RUNTIME_CONFIG_ERROR_NONE; + + if (node == NULL) { + /* don't return anything if tree_traversal depth is 1, + * because 1 means only return the exact match and that would be NULL */ + if (tree_traversal_depth == RUNTIME_CONFIG_TRAVERSE_SINGLE_NODE) { + return RUNTIME_CONFIG_ERROR_NONE; + } + /* iterate through all namespaces */ + for (size_t i = 0; i < XFA_LEN(runtime_config_namespace_t *, _runtime_config_namespaces_xfa); i++) { + runtime_config_namespace_t *namespace = _runtime_config_namespaces_xfa[i]; + + /* we write tree_traversal_depth - 1, because we already iterated over namespaces */ + rc = _runtime_config_traverse_namespace_tree( + namespace, tree_traversal_cb, + tree_traversal_depth - 1, context); + + if (!(rc == RUNTIME_CONFIG_ERROR_NONE)) { + return rc; + } + } + return rc; + } + + switch (node->type) { + case RUNTIME_CONFIG_NODE_TYPE_NAMESPACE: + rc = _runtime_config_traverse_namespace_tree( + node->as_namespace, tree_traversal_cb, tree_traversal_depth, context); + break; + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA: + rc = _runtime_config_traverse_schema_tree( + node->as_schema, tree_traversal_cb, tree_traversal_depth, context); + break; + case RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE: + rc = _runtime_config_traverse_schema_tree_instance( + node->as_schema_instance, tree_traversal_cb, tree_traversal_depth, context); + break; + case RUNTIME_CONFIG_NODE_TYPE_GROUP: + rc = _runtime_config_traverse_group_tree( + node->as_group.schema_instance, node->as_group.group, tree_traversal_cb, + tree_traversal_depth, context); + break; + case RUNTIME_CONFIG_NODE_TYPE_PARAMETER: + rc = _runtime_config_traverse_parameter_tree( + node->as_parameter.schema_instance, node->as_parameter.parameter, + tree_traversal_cb, context); + break; + } + + return rc; +} diff --git a/tests/unittests/tests-runtime_config/Makefile b/tests/unittests/tests-runtime_config/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-runtime_config/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-runtime_config/Makefile.include b/tests/unittests/tests-runtime_config/Makefile.include new file mode 100644 index 000000000000..2d082280dfbf --- /dev/null +++ b/tests/unittests/tests-runtime_config/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE += runtime_config +USEMODULE += tests-runtime_config-namespace diff --git a/tests/unittests/tests-runtime_config/namespace/Makefile b/tests/unittests/tests-runtime_config/namespace/Makefile new file mode 100644 index 000000000000..df44af26d598 --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/Makefile @@ -0,0 +1,5 @@ +# Autogenerated by `make runtime_config generate`, do not edit. + +MODULE = tests-runtime_config-namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-runtime_config/namespace/definitions/0001_full.yaml b/tests/unittests/tests-runtime_config/namespace/definitions/0001_full.yaml new file mode 100644 index 000000000000..ed619df867bc --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/definitions/0001_full.yaml @@ -0,0 +1,71 @@ +# yaml-language-server: $schema=../../../../../dist/tools/runtime_config/schema.json +id: 1 +name: rgb +description: Representation of an rgb color. +items: + - id: 0 + name: bytes + description: Bytes data type property. + type: bytes + size: 50 + + - id: 1 + name: string + description: String data type property. + type: string + size: 50 + + - id: 2 + name: boolean + description: Boolean data type property. + type: bool + + - id: 3 + name: u8 + description: 8 bit unsigned integer data type property. + type: uint8 + + - id: 4 + name: u16 + description: 16 bit unsigned integer data type property. + type: uint16 + + - id: 5 + name: u32 + description: 32 bit unsigned integer data type property. + type: uint32 + + - id: 6 + name: u64 + description: 64 bit unsigned integer data type property. + type: uint64 + + - id: 7 + name: i8 + description: 8 bit integer data type property. + type: int8 + + - id: 8 + name: i16 + description: 16 bit integer data type property. + type: int16 + + - id: 9 + name: i32 + description: 32 bit integer data type property. + type: int32 + + - id: 10 + name: i64 + description: 64 bit integer data type property. + type: int64 + + - id: 11 + name: f32 + description: 64 bit integer data type property. + type: float32 + + - id: 12 + name: f64 + description: 64 bit integer data type property. + type: float64 diff --git a/tests/unittests/tests-runtime_config/namespace/namespace_tests.c b/tests/unittests/tests-runtime_config/namespace/namespace_tests.c new file mode 100644 index 000000000000..3c607fb9f46d --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/namespace_tests.c @@ -0,0 +1,48 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @brief Runtime configuration "tests" namespace providing configuration + * schemas for testing the runtime_config sys module + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include "kernel_defines.h" +#include "runtime_config.h" + +#include "tests.h" +#include "tests/full.h" +#include "tests/nested.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +static const runtime_config_schema_t *_schemas[] = { + &runtime_config_tests_full, + &runtime_config_tests_nested, +}; + +runtime_config_namespace_t runtime_config_tests = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "tests", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Tests namespace", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schemas = _schemas, + .schemas_len = ARRAY_SIZE(_schemas), +}; + +RUNTIME_CONFIG_ADD_NAMESPACE(tests, runtime_config_tests); diff --git a/tests/unittests/tests-runtime_config/namespace/namespace_tests_full.c b/tests/unittests/tests-runtime_config/namespace/namespace_tests_full.c new file mode 100644 index 000000000000..59926fe132dc --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/namespace_tests_full.c @@ -0,0 +1,311 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @brief Runtime configuration "full" schema using all possible data + * types of the "Runtime configuration" module + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#include "kernel_defines.h" +#include "runtime_config.h" +#include "tests.h" +#include "tests/full.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* Mapping */ +static void _get_parameter_value_from_instance( + const runtime_config_parameter_id_t parameter_id, + const runtime_config_schema_instance_t *instance, + void **val, + size_t *val_len) +{ + runtime_config_tests_full_instance_t *_instance = + (runtime_config_tests_full_instance_t *)instance->data; + + switch (parameter_id) { + case RUNTIME_CONFIG_TESTS_FULL_BYTES: + *val = &_instance->bytes; + *val_len = sizeof(_instance->bytes); + break; + + case RUNTIME_CONFIG_TESTS_FULL_STRING: + *val = &_instance->string; + *val_len = sizeof(_instance->string); + break; + + case RUNTIME_CONFIG_TESTS_FULL_BOOLEAN: + *val = &_instance->boolean; + *val_len = sizeof(_instance->boolean); + break; + + case RUNTIME_CONFIG_TESTS_FULL_U8: + *val = &_instance->u8; + *val_len = sizeof(_instance->u8); + break; + + case RUNTIME_CONFIG_TESTS_FULL_U16: + *val = &_instance->u16; + *val_len = sizeof(_instance->u16); + break; + + case RUNTIME_CONFIG_TESTS_FULL_U32: + *val = &_instance->u32; + *val_len = sizeof(_instance->u32); + break; + + case RUNTIME_CONFIG_TESTS_FULL_U64: + *val = &_instance->u64; + *val_len = sizeof(_instance->u64); + break; + + case RUNTIME_CONFIG_TESTS_FULL_I8: + *val = &_instance->i8; + *val_len = sizeof(_instance->i8); + break; + + case RUNTIME_CONFIG_TESTS_FULL_I16: + *val = &_instance->i16; + *val_len = sizeof(_instance->i16); + break; + + case RUNTIME_CONFIG_TESTS_FULL_I32: + *val = &_instance->i32; + *val_len = sizeof(_instance->i32); + break; + + case RUNTIME_CONFIG_TESTS_FULL_I64: + *val = &_instance->i64; + *val_len = sizeof(_instance->i64); + break; + + case RUNTIME_CONFIG_TESTS_FULL_F32: + *val = &_instance->f32; + *val_len = sizeof(_instance->f32); + break; + + case RUNTIME_CONFIG_TESTS_FULL_F64: + *val = &_instance->f64; + *val_len = sizeof(_instance->f64); + break; + } +} + +/* Schema parameters */ +const runtime_config_parameter_t runtime_config_tests_full_bytes = { + .id = RUNTIME_CONFIG_TESTS_FULL_BYTES, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "bytes", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_BYTES, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_string = { + .id = RUNTIME_CONFIG_TESTS_FULL_STRING, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "string", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_STRING, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_boolean = { + .id = RUNTIME_CONFIG_TESTS_FULL_BOOLEAN, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "boolean", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_BOOL, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_u8 = { + .id = RUNTIME_CONFIG_TESTS_FULL_U8, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u8", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_u16 = { + .id = RUNTIME_CONFIG_TESTS_FULL_U16, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u16", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_UINT16, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_u32 = { + .id = RUNTIME_CONFIG_TESTS_FULL_U32, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u32", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_UINT32, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_u64 = { + .id = RUNTIME_CONFIG_TESTS_FULL_U64, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u64", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_UINT64, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_i8 = { + .id = RUNTIME_CONFIG_TESTS_FULL_I8, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i8", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_INT8, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_i16 = { + .id = RUNTIME_CONFIG_TESTS_FULL_I16, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i16", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_INT16, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_i32 = { + .id = RUNTIME_CONFIG_TESTS_FULL_I32, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i32", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_INT32, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_i64 = { + .id = RUNTIME_CONFIG_TESTS_FULL_I64, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i64", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_INT64, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_f32 = { + .id = RUNTIME_CONFIG_TESTS_FULL_F32, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "f32", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_FLOAT32, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_full_f64 = { + .id = RUNTIME_CONFIG_TESTS_FULL_F64, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "f64", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_full, + .type = RUNTIME_CONFIG_TYPE_FLOAT64, + .count = 1, +}; + +/* Schema */ +runtime_config_schema_t runtime_config_tests_full = { + .id = RUNTIME_CONFIG_TESTS_FULL, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "full", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .namespace = &runtime_config_tests, + .get_parameter_value_from_instance = _get_parameter_value_from_instance, + .groups = NULL, + .groups_len = 0, + .parameters = (const runtime_config_parameter_t *[]){ + &runtime_config_tests_full_bytes, + &runtime_config_tests_full_string, + &runtime_config_tests_full_boolean, + &runtime_config_tests_full_u8, + &runtime_config_tests_full_u16, + &runtime_config_tests_full_u32, + &runtime_config_tests_full_u64, + &runtime_config_tests_full_i8, + &runtime_config_tests_full_i16, + &runtime_config_tests_full_i32, + &runtime_config_tests_full_i64, + &runtime_config_tests_full_f32, + &runtime_config_tests_full_f64, + }, + .parameters_len = 13, +}; diff --git a/tests/unittests/tests-runtime_config/namespace/namespace_tests_nested.c b/tests/unittests/tests-runtime_config/namespace/namespace_tests_nested.c new file mode 100644 index 000000000000..e8528f2bc6b8 --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/namespace_tests_nested.c @@ -0,0 +1,121 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @brief Runtime configuration "nested" schema representing different + * nesting levels of a configuration schema + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#include "kernel_defines.h" +#include "runtime_config.h" +#include "tests.h" +#include "tests/nested.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/* Mapping */ +static void _get_parameter_value_from_instance( + const runtime_config_parameter_id_t parameter_id, + const runtime_config_schema_instance_t *instance, + void **val, + size_t *val_len) +{ + runtime_config_tests_nested_instance_t *_instance = + (runtime_config_tests_nested_instance_t *)instance->data; + + switch (parameter_id) { + case RUNTIME_CONFIG_TESTS_NESTED_PARAMETER: + *val = &_instance->parameter; + *val_len = sizeof(_instance->parameter); + break; + + case RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER: + *val = &_instance->group_parameter; + *val_len = sizeof(_instance->group_parameter); + break; + } +} + +/* Schema parameters */ +const runtime_config_parameter_t runtime_config_tests_nested_parameter = { + .id = RUNTIME_CONFIG_TESTS_NESTED_PARAMETER, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "parameter", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_nested, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +const runtime_config_parameter_t runtime_config_tests_nested_group_parameter = { + .id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "parameter", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_nested, + .type = RUNTIME_CONFIG_TYPE_UINT8, + .count = 1, +}; + +/* Schema groups */ +const runtime_config_group_t runtime_config_tests_nested_group = { + .id = RUNTIME_CONFIG_TESTS_NESTED_GROUP, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "group", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .schema = &runtime_config_tests_nested, + .groups = NULL, + .groups_len = 0, + .parameters = (const runtime_config_parameter_t *[]){ + &runtime_config_tests_nested_group_parameter, + }, + .parameters_len = 1, +}; + +/* Schema */ +runtime_config_schema_t runtime_config_tests_nested = { + .id = RUNTIME_CONFIG_TESTS_NESTED, +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "nested", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_DESCRIPTION */ + .namespace = &runtime_config_tests, + .get_parameter_value_from_instance = _get_parameter_value_from_instance, + .groups = (const runtime_config_group_t *[]){ + &runtime_config_tests_nested_group, + }, + .groups_len = 1, + .parameters = (const runtime_config_parameter_t *[]){ + &runtime_config_tests_nested_parameter, + }, + .parameters_len = 1, +}; diff --git a/tests/unittests/tests-runtime_config/namespace/tests.h b/tests/unittests/tests-runtime_config/namespace/tests.h new file mode 100644 index 000000000000..db7dc10188ba --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/tests.h @@ -0,0 +1,45 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @defgroup sys_runtime_config_namespace_tests Runtime configuration "Tests" namespace + * @ingroup tests + * @brief Runtime configuration "tests" namespace providing configuration + * schemas for testing the runtime_config sys module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +extern runtime_config_namespace_t runtime_config_tests; + +/** + * @brief This ENUM defines the IDs of configuration schemas in the "tests" namespace. + * The IDs are needed by the int_path module to identify schemas using IDs instead + * of pointers. + */ +typedef enum { + RUNTIME_CONFIG_TESTS_FULL, + RUNTIME_CONFIG_TESTS_NESTED, +} runtime_config_tests_indices_t; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/tests/unittests/tests-runtime_config/namespace/tests/full.h b/tests/unittests/tests-runtime_config/namespace/tests/full.h new file mode 100644 index 000000000000..4921d703a39d --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/tests/full.h @@ -0,0 +1,79 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @brief Runtime configuration "full" schema using all possible data types + * of the "Runtime configuration" module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +/* FULL */ +extern const runtime_config_parameter_t runtime_config_tests_full_bytes; +extern const runtime_config_parameter_t runtime_config_tests_full_string; +extern const runtime_config_parameter_t runtime_config_tests_full_boolean; +extern const runtime_config_parameter_t runtime_config_tests_full_u8; +extern const runtime_config_parameter_t runtime_config_tests_full_u16; +extern const runtime_config_parameter_t runtime_config_tests_full_u32; +extern const runtime_config_parameter_t runtime_config_tests_full_u64; +extern const runtime_config_parameter_t runtime_config_tests_full_i8; +extern const runtime_config_parameter_t runtime_config_tests_full_i16; +extern const runtime_config_parameter_t runtime_config_tests_full_i32; +extern const runtime_config_parameter_t runtime_config_tests_full_i64; +extern const runtime_config_parameter_t runtime_config_tests_full_f32; +extern const runtime_config_parameter_t runtime_config_tests_full_f64; +extern runtime_config_schema_t runtime_config_tests_full; + +typedef struct { + clist_node_t node; + uint8_t bytes[10]; + char string[50]; + bool boolean; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + int8_t i8; + int16_t i16; + int32_t i32; + int64_t i64; + float f32; + double f64; +} runtime_config_tests_full_instance_t; + +typedef enum { + RUNTIME_CONFIG_TESTS_FULL_BYTES, + RUNTIME_CONFIG_TESTS_FULL_STRING, + RUNTIME_CONFIG_TESTS_FULL_BOOLEAN, + RUNTIME_CONFIG_TESTS_FULL_U8, + RUNTIME_CONFIG_TESTS_FULL_U16, + RUNTIME_CONFIG_TESTS_FULL_U32, + RUNTIME_CONFIG_TESTS_FULL_U64, + RUNTIME_CONFIG_TESTS_FULL_I8, + RUNTIME_CONFIG_TESTS_FULL_I16, + RUNTIME_CONFIG_TESTS_FULL_I32, + RUNTIME_CONFIG_TESTS_FULL_I64, + RUNTIME_CONFIG_TESTS_FULL_F32, + RUNTIME_CONFIG_TESTS_FULL_F64, +} runtime_config_tests_full_indices_t; + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/tests/unittests/tests-runtime_config/namespace/tests/nested.h b/tests/unittests/tests-runtime_config/namespace/tests/nested.h new file mode 100644 index 000000000000..6750e99716aa --- /dev/null +++ b/tests/unittests/tests-runtime_config/namespace/tests/nested.h @@ -0,0 +1,48 @@ +/* Autogenerated by `make runtime_config generate`, do not edit. */ + +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @brief Runtime configuration "nested" schema representing different + * nesting levels of a configuration schema + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "runtime_config.h" + +/* NESTED */ +extern const runtime_config_parameter_t runtime_config_tests_nested_parameter; +extern const runtime_config_group_t runtime_config_tests_nested_group; +extern const runtime_config_parameter_t runtime_config_tests_nested_group_parameter; +extern runtime_config_schema_t runtime_config_tests_nested; + +typedef struct { + clist_node_t node; + uint8_t parameter; + uint8_t group_parameter; +} runtime_config_tests_nested_instance_t; + +typedef enum { + RUNTIME_CONFIG_TESTS_NESTED_PARAMETER, + RUNTIME_CONFIG_TESTS_NESTED_GROUP, + RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER, +} runtime_config_tests_nested_indices_t; + +#ifdef __cplusplus +} +#endif +/** @} */ diff --git a/tests/unittests/tests-runtime_config/tests-apply.c b/tests/unittests/tests-runtime_config/tests-apply.c new file mode 100644 index 000000000000..e16079b628b5 --- /dev/null +++ b/tests/unittests/tests-runtime_config/tests-apply.c @@ -0,0 +1,210 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup unittests + * @{ + * + * @file + * @brief Unittests for runtime_config_apply + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include "embUnit.h" +#include "fmt.h" +#include "assert.h" +#include "vfs.h" +#include "board.h" +#include "mtd.h" +#include "runtime_config.h" + +#include "tests-runtime_config.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +static bool successful = false; +static runtime_config_group_or_parameter_id_t parameter_id; +static runtime_config_group_or_parameter_id_t group_id; + +static runtime_config_error_t apply_schema_instance_cb( + const runtime_config_schema_instance_t *const schema_instance) +{ + (void)schema_instance; + + successful = true; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static runtime_config_error_t apply_parameter_cb( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id) +{ + (void)schema_instance; + + if (group_or_parameter_id == parameter_id) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t apply_group_cb( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id) +{ + (void)schema_instance; + + if (group_or_parameter_id == group_id) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_schema_instance_t test_nested_instance_parameter_test = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .apply_schema_instance_cb = &apply_schema_instance_cb, + .apply_group_or_parameter_cb = &apply_parameter_cb, +}; + +static runtime_config_schema_instance_t test_nested_instance_group_test = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) + .name = "test-nested-group-test", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .apply_schema_instance_cb = &apply_schema_instance_cb, + .apply_group_or_parameter_cb = &apply_group_cb, +}; + +static runtime_config_schema_instance_t test_nested_instance_instance_test = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) + .name = "test-nested-instance-test", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .apply_schema_instance_cb = &apply_schema_instance_cb, + .apply_group_or_parameter_cb = &apply_group_cb, +}; + +static void test_runtime_config_setup(void) +{ + /* init runtime configuration module */ + runtime_config_init(); + + /* add schema instances */ + runtime_config_add_schema_instance(&runtime_config_tests_nested, &test_nested_instance_parameter_test); + runtime_config_add_schema_instance(&runtime_config_tests_nested, &test_nested_instance_group_test); + runtime_config_add_schema_instance(&runtime_config_tests_nested, &test_nested_instance_instance_test); +} + +static void test_runtime_config_teardown(void) +{ +} + +static void tests_runtime_config_apply_parameter(void) +{ + successful = false; + parameter_id = RUNTIME_CONFIG_TESTS_NESTED_PARAMETER; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_PARAMETER( + &test_nested_instance_parameter_test, + &runtime_config_tests_nested_parameter); + + runtime_config_apply(&node); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_apply_group(void) +{ + successful = false; + group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_GROUP( + &test_nested_instance_group_test, + &runtime_config_tests_nested_group); + + runtime_config_apply(&node); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_apply_instance(void) +{ + successful = false; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_SCHEMA_INSTANCE( + &test_nested_instance_instance_test); + + runtime_config_apply(&node); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_apply_schema(void) +{ + successful = false; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_SCHEMA( + &runtime_config_tests_nested); + + runtime_config_apply(&node); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_apply_namespace(void) +{ + successful = false; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_NAMESPACE( + &runtime_config_tests); + + runtime_config_apply(&node); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_apply_all(void) +{ + successful = false; + runtime_config_apply(NULL); + TEST_ASSERT(successful); +} + +Test *tests_runtime_config_apply_tests(void) +{ + + EMB_UNIT_TESTFIXTURES(fixtures){ + new_TestFixture(tests_runtime_config_apply_parameter), + new_TestFixture(tests_runtime_config_apply_group), + new_TestFixture(tests_runtime_config_apply_instance), + new_TestFixture(tests_runtime_config_apply_schema), + new_TestFixture(tests_runtime_config_apply_namespace), + new_TestFixture(tests_runtime_config_apply_all), + }; + + EMB_UNIT_TESTCALLER(runtime_config_tests, test_runtime_config_setup, test_runtime_config_teardown, fixtures); + + return (Test *)&runtime_config_tests; +} diff --git a/tests/unittests/tests-runtime_config/tests-get-set.c b/tests/unittests/tests-runtime_config/tests-get-set.c new file mode 100644 index 000000000000..d70c618e7d5e --- /dev/null +++ b/tests/unittests/tests-runtime_config/tests-get-set.c @@ -0,0 +1,514 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup unittests + * @{ + * + * @file + * @brief Unittests for runtime_config_get, runtime_config_set + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include "embUnit.h" +#include "fmt.h" +#include "assert.h" +#include "vfs.h" +#include "board.h" +#include "mtd.h" +#include "runtime_config.h" + +#include "tests-runtime_config.h" +#include "namespace/tests.h" +#include "namespace/tests/full.h" + +static runtime_config_error_t apply_schema_instance_cb( + const runtime_config_schema_instance_t *const schema_instance) +{ + (void)schema_instance; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t apply_group_cb( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id) +{ + (void)schema_instance; + (void)group_or_parameter_id; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_tests_full_instance_t test_full_instance_1_data = { + .bytes = { 7 }, + .string = "hello world", + .boolean = true, + .u8 = 9, + .u16 = 17, + .u32 = 33, + .u64 = 65, + .i8 = 8, + .i16 = 16, + .i32 = 32, + .i64 = 64, + .f32 = 3.2, + .f64 = 6.4, +}; + +static runtime_config_schema_instance_t test_full_instance_1 = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) + .name = "test-full-1", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + .data = &test_full_instance_1_data, + .apply_schema_instance_cb = apply_schema_instance_cb, + .apply_group_or_parameter_cb = apply_group_cb, +}; + +static void test_runtime_config_setup(void) +{ + /* init runtime configuration module */ + runtime_config_init(); + + /* add schema instances */ + runtime_config_add_schema_instance(&runtime_config_tests_full, &test_full_instance_1); +} + +static void test_runtime_config_teardown(void) +{ +} + +static void tests_runtime_config_min_values(void) +{ + void *output; + size_t output_len; + + runtime_config_node_t node = { + .type = RUNTIME_CONFIG_NODE_TYPE_PARAMETER, + .as_parameter.schema_instance = &test_full_instance_1, + }; + + /* bytes */ + typedef struct { + uint8_t foo; + } custom_struct_one_field_t; + + custom_struct_one_field_t custom_data_short = { .foo = UINT8_MAX }; + + node.as_parameter.parameter = &runtime_config_tests_full_bytes; + + runtime_config_set(&node, &custom_data_short, sizeof(custom_data_short)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(custom_data_short.foo, + ((custom_struct_one_field_t *)output)->foo); + + /* string */ + const char input_string[] = ""; + node.as_parameter.parameter = &runtime_config_tests_full_string; + + runtime_config_set(&node, input_string, sizeof(input_string)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_STRING("", (char *)output); + + /* bool */ + const bool input_bool = false; + node.as_parameter.parameter = &runtime_config_tests_full_boolean; + + runtime_config_set(&node, &input_bool, sizeof(input_bool)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output); + + /* u8 */ + const uint8_t input_u8 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u8; + + runtime_config_set(&node, &input_u8, sizeof(input_u8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output); + + /* u16 */ + const uint16_t input_u16 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u16; + + runtime_config_set(&node, &input_u16, sizeof(input_u16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output); + + /* u32 */ + const uint32_t input_u32 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u32; + + runtime_config_set(&node, &input_u32, sizeof(input_u32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output); + + /* u64 */ + const uint64_t input_u64 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u64; + + runtime_config_set(&node, &input_u64, sizeof(input_u64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output); + + /* i8 */ + const int8_t input_i8 = INT8_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_i8; + + runtime_config_set(&node, &input_i8, sizeof(input_i8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output); + + /* i16 */ + const int16_t input_i16 = INT16_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_i16; + + runtime_config_set(&node, &input_i16, sizeof(input_i16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output); + + /* i32 */ + const int32_t input_i32 = INT32_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_i32; + + runtime_config_set(&node, &input_i32, sizeof(input_i32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output); + + /* i64 */ + const int64_t input_i64 = INT64_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_i64; + + runtime_config_set(&node, &input_i64, sizeof(input_i64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output); + + /* f32 */ + const float input_f32 = FLT_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_f32; + + runtime_config_set(&node, &input_f32, sizeof(input_f32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f32 == *(float *)output); + + /* f64 */ + const double input_f64 = DBL_MIN; + node.as_parameter.parameter = &runtime_config_tests_full_f64; + + runtime_config_set(&node, &input_f64, sizeof(input_f64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f64 == *(double *)output); +} + +static void tests_runtime_config_zero_values(void) +{ + void *output; + size_t output_len; + + runtime_config_node_t node = { + .type = RUNTIME_CONFIG_NODE_TYPE_PARAMETER, + .as_parameter.schema_instance = &test_full_instance_1, + }; + + /* bytes */ + typedef struct { + uint8_t foo; + } custom_struct_one_field_t; + + custom_struct_one_field_t custom_data_short = { .foo = 0 }; + + node.as_parameter.parameter = &runtime_config_tests_full_bytes; + + runtime_config_set(&node, &custom_data_short, sizeof(custom_data_short)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(0, ((custom_struct_one_field_t *)output)->foo); + + /* string */ + const char input_string[] = ""; + node.as_parameter.parameter = &runtime_config_tests_full_string; + + runtime_config_set(&node, input_string, sizeof(input_string)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_STRING("", (char *)output); + + /* bool */ + const bool input_bool = 0; + node.as_parameter.parameter = &runtime_config_tests_full_boolean; + + runtime_config_set(&node, &input_bool, sizeof(input_bool)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output); + + /* u8 */ + const uint8_t input_u8 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u8; + + runtime_config_set(&node, &input_u8, sizeof(input_u8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output); + + /* u16 */ + const uint16_t input_u16 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u16; + + runtime_config_set(&node, &input_u16, sizeof(input_u16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output); + + /* u32 */ + const uint32_t input_u32 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u32; + + runtime_config_set(&node, &input_u32, sizeof(input_u32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output); + + /* u64 */ + const uint64_t input_u64 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_u64; + + runtime_config_set(&node, &input_u64, sizeof(input_u64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output); + + /* i8 */ + const int8_t input_i8 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_i8; + + runtime_config_set(&node, &input_i8, sizeof(input_i8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output); + + /* i16 */ + const int16_t input_i16 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_i16; + + runtime_config_set(&node, &input_i16, sizeof(input_i16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output); + + /* i32 */ + const int32_t input_i32 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_i32; + + runtime_config_set(&node, &input_i32, sizeof(input_i32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output); + + /* i64 */ + const int64_t input_i64 = 0; + node.as_parameter.parameter = &runtime_config_tests_full_i64; + + runtime_config_set(&node, &input_i64, sizeof(input_i64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output); + + /* f32 */ + const float input_f32 = 0.0; + node.as_parameter.parameter = &runtime_config_tests_full_f32; + + runtime_config_set(&node, &input_f32, sizeof(input_f32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f32 == *(float *)output); + + /* f64 */ + const double input_f64 = 0.0; + node.as_parameter.parameter = &runtime_config_tests_full_f64; + + runtime_config_set(&node, &input_f64, sizeof(input_f64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f64 == *(double *)output); +} + +static void tests_runtime_config_ensure_that_max_values_can_be_set_and_get(void) +{ + void *output; + size_t output_len; + + runtime_config_node_t node = { + .type = RUNTIME_CONFIG_NODE_TYPE_PARAMETER, + .as_parameter.schema_instance = &test_full_instance_1, + }; + + /* bytes */ + typedef struct { + uint8_t foo; + } custom_struct_one_field_t; + + custom_struct_one_field_t custom_data_short = { .foo = UINT8_MAX }; + + node.as_parameter.parameter = &runtime_config_tests_full_bytes; + + runtime_config_set(&node, &custom_data_short, sizeof(custom_data_short)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(UINT8_MAX, + ((custom_struct_one_field_t *)output)->foo); + + /* string */ + char input_string[sizeof(test_full_instance_1_data.string)] = { 0 }; + + for (size_t i = 0; i < sizeof(input_string) - 1; i++) { + input_string[i] = 'a'; + } + + node.as_parameter.parameter = &runtime_config_tests_full_string; + + runtime_config_set(&node, input_string, sizeof(input_string)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_STRING(input_string, (char *)output); + + /* bool */ + const bool input_bool = true; + node.as_parameter.parameter = &runtime_config_tests_full_boolean; + + runtime_config_set(&node, &input_bool, sizeof(input_bool)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output); + + /* u8 */ + const uint8_t input_u8 = UINT8_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_u8; + + runtime_config_set(&node, &input_u8, sizeof(input_u8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output); + + /* u16 */ + const uint16_t input_u16 = UINT16_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_u16; + + runtime_config_set(&node, &input_u16, sizeof(input_u16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output); + + /* u32 */ + const uint32_t input_u32 = UINT32_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_u32; + + runtime_config_set(&node, &input_u32, sizeof(input_u32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output); + + /* u64 */ + const uint64_t input_u64 = UINT64_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_u64; + + runtime_config_set(&node, &input_u64, sizeof(input_u64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output); + + /* i8 */ + const int8_t input_i8 = INT8_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_i8; + + runtime_config_set(&node, &input_i8, sizeof(input_i8)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output); + + /* i16 */ + const int16_t input_i16 = INT16_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_i16; + + runtime_config_set(&node, &input_i16, sizeof(input_i16)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output); + + /* i32 */ + const int32_t input_i32 = INT32_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_i32; + + runtime_config_set(&node, &input_i32, sizeof(input_i32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output); + + /* i64 */ + const int64_t input_i64 = INT64_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_i64; + + runtime_config_set(&node, &input_i64, sizeof(input_i64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output); + + /* f32 */ + const float input_f32 = FLT_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_f32; + + runtime_config_set(&node, &input_f32, sizeof(input_f32)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f32 == *(float *)output); + + /* f64 */ + const double input_f64 = DBL_MAX; + node.as_parameter.parameter = &runtime_config_tests_full_f64; + + runtime_config_set(&node, &input_f64, sizeof(input_f64)); + runtime_config_get(&node, &output, &output_len); + + TEST_ASSERT(input_f64 == *(double *)output); +} + +Test *tests_runtime_config_get_set_tests(void) +{ + (void)tests_runtime_config_min_values; + (void)tests_runtime_config_zero_values; + (void)tests_runtime_config_ensure_that_max_values_can_be_set_and_get; + + EMB_UNIT_TESTFIXTURES(fixtures){ + new_TestFixture(tests_runtime_config_min_values), + new_TestFixture(tests_runtime_config_zero_values), + new_TestFixture(tests_runtime_config_ensure_that_max_values_can_be_set_and_get), + }; + + EMB_UNIT_TESTCALLER(runtime_config_tests, test_runtime_config_setup, test_runtime_config_teardown, fixtures); + + return (Test *)&runtime_config_tests; +} diff --git a/tests/unittests/tests-runtime_config/tests-runtime_config.c b/tests/unittests/tests-runtime_config/tests-runtime_config.c new file mode 100644 index 000000000000..4df0315f62ad --- /dev/null +++ b/tests/unittests/tests-runtime_config/tests-runtime_config.c @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup unittests + * @{ + * + * @file + * @brief Unittest entry point for the runtime configuration test group + * + * @author Lasse Rosenow + * + * @} + */ + +#include "embUnit/embUnit.h" + +#include "tests-runtime_config.h" + +Test *tests_runtime_config_get_set_tests(void); +Test *tests_runtime_config_apply_tests(void); +Test *tests_runtime_config_traverse_config_tree_tests(void); + +void tests_runtime_config(void) +{ + TESTS_RUN(tests_runtime_config_get_set_tests()); + TESTS_RUN(tests_runtime_config_apply_tests()); + TESTS_RUN(tests_runtime_config_traverse_config_tree_tests()); +} diff --git a/tests/unittests/tests-runtime_config/tests-runtime_config.h b/tests/unittests/tests-runtime_config/tests-runtime_config.h new file mode 100644 index 000000000000..9309dbc019e6 --- /dev/null +++ b/tests/unittests/tests-runtime_config/tests-runtime_config.h @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup unittests + * @brief Unittests for the runtime_config module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_runtime_config(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/tests/unittests/tests-runtime_config/tests-traverse_config_tree.c b/tests/unittests/tests-runtime_config/tests-traverse_config_tree.c new file mode 100644 index 000000000000..82bff67d1395 --- /dev/null +++ b/tests/unittests/tests-runtime_config/tests-traverse_config_tree.c @@ -0,0 +1,396 @@ +/* + * SPDX-FileCopyrightText: 2023-2026 Lasse Rosenow + * SPDX-FileCopyrightText: 2023-2026 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup unittests + * @{ + * + * @file + * @brief Unittests for runtime_config_traverse_config_tree + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include "embUnit.h" +#include "fmt.h" +#include "assert.h" +#include "vfs.h" +#include "board.h" +#include "mtd.h" +#include "runtime_config.h" + +#include "tests-runtime_config.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +static runtime_config_error_t apply_schema_instance_cb( + const runtime_config_schema_instance_t *const schema_instance) +{ + (void)schema_instance; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t apply_group_cb( + const runtime_config_schema_instance_t *const schema_instance, + const runtime_config_group_or_parameter_id_t group_or_parameter_id) +{ + (void)schema_instance; + (void)group_or_parameter_id; + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static bool successful = false; + +static runtime_config_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static runtime_config_schema_instance_t test_nested_instance_1 = { +#if IS_ACTIVE(CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_RUNTIME_CONFIG_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .apply_schema_instance_cb = apply_schema_instance_cb, + .apply_group_or_parameter_cb = apply_group_cb, +}; + +static runtime_config_error_t export_parameter_cb(const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == RUNTIME_CONFIG_NODE_TYPE_PARAMETER && node->as_parameter.parameter != NULL && + node->as_parameter.parameter->id == *(runtime_config_parameter_id_t *)context && + node->as_parameter.schema_instance == &test_nested_instance_1) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t export_group_cb(const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == RUNTIME_CONFIG_NODE_TYPE_GROUP && node->as_group.group != NULL && + node->as_group.group->id == *(runtime_config_group_id_t *)context) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t export_instance_cb(const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == RUNTIME_CONFIG_NODE_TYPE_SCHEMA_INSTANCE && node->as_schema_instance == &test_nested_instance_1) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t export_schema_cb(const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == RUNTIME_CONFIG_NODE_TYPE_SCHEMA && node->as_schema != NULL && + node->as_schema == &runtime_config_tests_nested) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static runtime_config_error_t export_namespace_cb(const runtime_config_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == RUNTIME_CONFIG_NODE_TYPE_NAMESPACE && node->as_namespace != NULL && + node->as_namespace == &runtime_config_tests) { + successful = true; + } + + return RUNTIME_CONFIG_ERROR_NONE; +} + +static void test_runtime_config_setup(void) +{ + /* init runtime configuration module */ + runtime_config_init(); + + /* add schema instances */ + runtime_config_add_schema_instance(&runtime_config_tests_nested, &test_nested_instance_1); +} + +static void test_runtime_config_teardown(void) +{ +} + +static void tests_runtime_config_traverse_parameter(void) +{ + const runtime_config_parameter_id_t parameter_id = RUNTIME_CONFIG_TESTS_NESTED_PARAMETER; + + successful = false; + + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_PARAMETER( + &test_nested_instance_1, + &runtime_config_tests_nested_parameter); + + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, ¶meter_id); + + TEST_ASSERT(successful); +} + +static void tests_runtime_config_traverse_group(void) +{ + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_GROUP( + &test_nested_instance_1, + &runtime_config_tests_nested_group); + + /* check if group gets exported */ + const runtime_config_group_id_t group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + successful = false; + runtime_config_traverse_config_tree(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + /* check that siblings get NOT exported */ + const runtime_config_parameter_id_t sibling_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_PARAMETER; + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, &sibling_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* check if children get exported */ + const runtime_config_parameter_id_t child_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER; + + /* tree_traversal_depth 0 => infinite => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* tree_traversal_depth 1 => only group => parameter gets NOT exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 1, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 2 => group + 1 level more => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 2, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_runtime_config_traverse_instance(void) +{ + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_SCHEMA_INSTANCE( + &test_nested_instance_1); + + /* check if instance gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if group gets exported */ + const runtime_config_group_id_t group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + successful = false; + runtime_config_traverse_config_tree(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + /* check if parameter get exported */ + const runtime_config_parameter_id_t child_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER; + + /* tree_traversal_depth 0 => infinite => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* tree_traversal_depth 2 => only instance and group => parameter gets NOT exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 2, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 3 => instance, group and parameter => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 3, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_runtime_config_traverse_schema(void) +{ + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_SCHEMA( + &runtime_config_tests_nested); + + /* check if schema gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if group gets exported */ + const runtime_config_group_id_t group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + successful = false; + runtime_config_traverse_config_tree(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + /* check if parameter get exported */ + const runtime_config_parameter_id_t child_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER; + + /* tree_traversal_depth 0 => infinite => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* tree_traversal_depth 3 => only schema, instance and group => parameter gets NOT exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 3, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 4 => schema, instance, group and parameter => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 4, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_runtime_config_traverse_namespace(void) +{ + const runtime_config_node_t node = RUNTIME_CONFIG_NODE_NAMESPACE( + &runtime_config_tests); + + /* check if namespace gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_namespace_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if schema gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if group gets exported */ + const runtime_config_group_id_t group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + successful = false; + runtime_config_traverse_config_tree(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + /* check if parameter get exported */ + const runtime_config_parameter_id_t child_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER; + + /* tree_traversal_depth 0 => infinite => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* tree_traversal_depth 4 => only namespace, schema, instance and group => parameter gets NOT exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 4, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 5 => namespace, schema, instance, group and parameter => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(&node, &export_parameter_cb, 5, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_runtime_config_traverse_all(void) +{ + /* check if namespace gets exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_namespace_cb, 0, NULL); + TEST_ASSERT(successful); + + /* tree_traversal_depth 1 => nothing gets exported because node == NULL */ + /* => no namespace was specified and 1 means only export the exact match (NULL) */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_namespace_cb, 1, NULL); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* check if schema gets exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if group gets exported */ + const runtime_config_group_id_t group_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP; + + successful = false; + runtime_config_traverse_config_tree(NULL, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + /* check if parameter get exported */ + const runtime_config_parameter_id_t child_parameter_id = RUNTIME_CONFIG_TESTS_NESTED_GROUP_PARAMETER; + + /* tree_traversal_depth 0 => infinite => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* tree_traversal_depth 1 => nothing gets exported because node == NULL */ + /* => not even a namespace was specified and 1 means only export the exact match (NULL) */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_parameter_cb, 1, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 5 => root, namespace, schema, instance, group */ + /* => parameter gets NOT exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_parameter_cb, 5, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* tree_traversal_depth 6 => root, namespace, schema, instance, group and parameter */ + /* => parameter gets exported */ + successful = false; + runtime_config_traverse_config_tree(NULL, &export_parameter_cb, 6, &child_parameter_id); + TEST_ASSERT(successful); +} + +Test *tests_runtime_config_traverse_config_tree_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures){ + new_TestFixture(tests_runtime_config_traverse_parameter), + new_TestFixture(tests_runtime_config_traverse_group), + new_TestFixture(tests_runtime_config_traverse_instance), + new_TestFixture(tests_runtime_config_traverse_schema), + new_TestFixture(tests_runtime_config_traverse_namespace), + new_TestFixture(tests_runtime_config_traverse_all), + }; + + EMB_UNIT_TESTCALLER(runtime_config_tests, test_runtime_config_setup, test_runtime_config_teardown, fixtures); + + return (Test *)&runtime_config_tests; +}