diff --git a/examples/registry_cli/Makefile b/examples/registry_cli/Makefile new file mode 100644 index 000000000000..54e60d403134 --- /dev/null +++ b/examples/registry_cli/Makefile @@ -0,0 +1,38 @@ +# name of the application +APPLICATION = registry_example_cli + +# 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 the shell to execute the registry CLI +USEMODULE += shell +USEMODULE += shell_cmds_default + +# enable littlefs2 for the persistent storage of the registry +USEMODULE += littlefs2 +# enable mtd to use it as the storage location +USEMODULE += mtd + +# enable the string_path extension of the registry to +# identify configuration parameters using strings instead of pointers +USEMODULE += registry_string_path +# enable the vfs storage implementation to persistently store configuration +# parameters to a storage device such as the mtd +USEMODULE += registry_storage_vfs +# enable the rgb_led schema (sys/rgb_led) to demonstrate how to configure +# RGB LEDs as an example for the registry CLI +USEMODULE += registry_namespace_sys_rgb_led + +# 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/registry_cli/README.md b/examples/registry_cli/README.md new file mode 100644 index 000000000000..cb6f2f1430b2 --- /dev/null +++ b/examples/registry_cli/README.md @@ -0,0 +1,25 @@ +examples/registry_cli +================ + +This application demonstrates the most basic usage of the RIOT Registry. + +Usage +===== + +Simply build and flash the application for your target board: + +``` +BOARD=YOUR_BOARD_NAME_HERE make flash term +``` + +Now you should have access to the RIOT shell on your board. For interacting +with the RIOT Registry, use the `registry` shell command, e.g.: + +``` +registry export <- prints the IDs of the whole configuration tree +registry get 0/0/0/0 <- get the value of the red parameter of the instance 0 of the RGB LED Schema inside of the SYS namespace +registry set 0/0/0/0 56 <- set the value of the red parameter to 56 +registry commit 0/0/0/0 <- apply new value for the 0/0/0/0 path +registry save <- save configurations to persistent storage +registry load <- load configurations from persistent storage +``` diff --git a/examples/registry_cli/main.c b/examples/registry_cli/main.c new file mode 100644 index 000000000000..0a76f2af4706 --- /dev/null +++ b/examples/registry_cli/main.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief RIOT registry CLI example application to demonstrate how + * to use the RIOT registry using the CLI. + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include + +#include "msg.h" +#include "shell.h" +#include "board.h" +#include "mtd.h" +#include "vfs.h" +#include "fs/littlefs2_fs.h" +#include "registry.h" +#include "registry/namespace/sys.h" +#include "registry/namespace/sys/rgb_led.h" +#include "registry/storage.h" +#include "registry/string_path.h" + +/* this callback is usually implemented drivers such as an RGB LED driver */ +static registry_error_t shared_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context) +{ + /* since this function is shared by multiple commit_cb functions, we path the instance in the context */ + const registry_instance_t *instance = context; + const registry_sys_rgb_led_instance_t *data = instance->data; + + printf("RGB instance commit_cb was executed on "); + + /* check how much of the registry instance was committed and print the new values */ + switch (scope) + { + case REGISTRY_COMMIT_INSTANCE: + /* in this case the whole instance and all parameters inside of it was committed */ + printf("the whole instance: ID: %d\n", instance->id); + printf("\tParameter ID: 0, VALUE: %d\n", data->red); + printf("\tParameter ID: 1, VALUE: %d\n", data->green); + printf("\tParameter ID: 2, VALUE: %d\n", data->blue); + break; + case REGISTRY_COMMIT_GROUP: + /* in this case a group and all parameters inside of it was committed */ + printf("a group: %d\n", *group_or_parameter_id); + /* this instance does not have groups, so no need to print anything here */ + break; + case REGISTRY_COMMIT_PARAMETER: + /* in this case only a single parameter was committed */ + printf("a single parameter: ID: %d,", *group_or_parameter_id); + switch (*group_or_parameter_id) + { + case 0: printf(" VALUE: %d\n", data->red); break; + case 1: printf(" VALUE: %d\n", data->green); break; + case 2: printf(" VALUE: %d\n", data->blue); break; + } + } + + return REGISTRY_ERROR_NONE; +} + +/* create instances of a configuration schema, to expose configuration parameters to the registry */ +static registry_error_t rgb_led_instance_0_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context); +static registry_sys_rgb_led_instance_t rgb_led_instance_0_data = { + .red = 0, + .green = 255, + .blue = 70, +}; +static registry_instance_t rgb_led_instance_0 = { + .name = "rgb-0", + .data = &rgb_led_instance_0_data, + .commit_cb = &rgb_led_instance_0_commit_cb, +}; +static registry_error_t rgb_led_instance_0_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context) +{ + (void)context; + return shared_commit_cb(scope, group_or_parameter_id, &rgb_led_instance_0); +} + +/* create an instance for a second RGB LED */ +static registry_error_t rgb_led_instance_1_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context); +static registry_sys_rgb_led_instance_t rgb_led_instance_1_data = { + .red = 90, + .green = 4, + .blue = 0, +}; +static registry_instance_t rgb_led_instance_1 = { + .name = "rgb-1", + .data = &rgb_led_instance_1_data, + .commit_cb = &rgb_led_instance_1_commit_cb, +}; +static registry_error_t rgb_led_instance_1_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context) +{ + (void)context; + return shared_commit_cb(scope, group_or_parameter_id, &rgb_led_instance_1); +} + +/* configure the registry storage to use littlefs2 */ +static littlefs2_desc_t fs_desc = { + .lock = MUTEX_INIT, +}; + +/* set the mount point for the registry storage to /sda for this example */ +static vfs_mount_t _vfs_mount = { + .fs = &littlefs2_file_system, + .mount_point = "/sda", + .private_data = &fs_desc, +}; + +/* create a storage instance to register the storage at the RIOT registry */ +static registry_storage_instance_t vfs_instance = { + .storage = ®istry_storage_vfs, + .data = &_vfs_mount, +}; + +int main(void) +{ + /* initialize the riot registry storage for persistent configuration parameters */ + #if IS_USED(MODULE_LITTLEFS2) + fs_desc.dev = MTD_0; + #endif + + /* initialize the riot registry */ + registry_init(); + + /* set storage instances so that the CLI can find them */ + const registry_storage_instance_t* storage_instances[] = {&vfs_instance}; + registry_storage_set_instances(storage_instances); + + /* add configuration schemas to the registry */ + registry_add_schema_instance(®istry_sys_rgb_led, &rgb_led_instance_0); + registry_add_schema_instance(®istry_sys_rgb_led, &rgb_led_instance_1); + + /* initialize and run the RIOT shell */ + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(NULL, line_buf, sizeof(line_buf)); + return 0; +} diff --git a/examples/registry_core/Makefile b/examples/registry_core/Makefile new file mode 100644 index 000000000000..d3c76e6394c3 --- /dev/null +++ b/examples/registry_core/Makefile @@ -0,0 +1,24 @@ +# name of the application +APPLICATION = registry_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 riot registry +USEMODULE += registry_namespace_sys_board_led +# enable the ztimer to turn the led on and off every second +USEMODULE += ztimer_msec + +# 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/registry_core/README.md b/examples/registry_core/README.md new file mode 100644 index 000000000000..0317fb298292 --- /dev/null +++ b/examples/registry_core/README.md @@ -0,0 +1,16 @@ +examples/registry_core +================ + +This application demonstrates the most basic usage of the RIOT Registry. +It implements a RGB LED schema and continuously changes its value every second. + +Usage +===== + +Simply build and flash the application for your target board: + +``` +BOARD=YOUR_BOARD_NAME_HERE make flash term +``` + +Now you should see the terminal continuously print out different LED states. diff --git a/examples/registry_core/main.c b/examples/registry_core/main.c new file mode 100644 index 000000000000..3840e7dedc19 --- /dev/null +++ b/examples/registry_core/main.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief RIOT registry core minimal example application to demonstrate + * how to use the riot registry without any of its extensions. + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include + +#include "periph_cpu.h" +#include "led.h" +#include "board.h" +#include "registry.h" +#include "registry/namespace/sys.h" +#include "registry/namespace/sys/board_led.h" +#include "ztimer.h" + +registry_error_t board_led_instance_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context); + +/* This belongs into the BOARD or Driver for example */ +registry_sys_board_led_instance_t board_led_instance_0_data = { + .enabled = 0, +}; + +registry_instance_t board_led_instance = { + .data = &board_led_instance_0_data, + .commit_cb = &board_led_instance_commit_cb, +}; + +/* this callback is usually implemented drivers such as an RGB LED driver */ +registry_error_t board_led_instance_commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context) +{ + (void)scope; + (void)context; + + /* Either commit 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 == NULL) || + (*group_or_parameter_id == REGISTRY_SYS_BOARD_LED_ENABLED)) { + /* The Driver owns the board_led data instance, so we can just get our value from there */ + bool led_state = board_led_instance_0_data.enabled; + /* Turn the LED on or off depending on the led_state */ + if (led_state == true) { + /* This is the commit_cb function of instance 0, so we toggle LED 0 as well */ + LED_ON(0); + } else { + LED_OFF(0); + } + } + + return 0; +} + +/* This belongs into our main application */ +int main(void) +{ + registry_init(); + + /* init schemas */ + registry_add_schema_instance(®istry_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 registry_node_t for the board_led_parameter */ + const registry_node_t parameter_node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &board_led_instance, + .parameter = ®istry_sys_board_led_enabled, + }, + }; + + /* Set new registry value */ + registry_set(¶meter_node, &board_led_enabled, sizeof(board_led_enabled)); + + /* Apply the registry value to change the LED state (in this case calls the commit_cb function: "board_led_instance_commit_cb")*/ + registry_commit(¶meter_node); + + /* Sleep for 1 second and then do it again*/ + ztimer_sleep(ZTIMER_MSEC, 1000); + } + + return 0; +} diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 986a1c11ca4e..636141943362 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -406,6 +406,11 @@ PSEUDOMODULES += fortuna_reseed ## will be removed after 2023.07 release. PSEUDOMODULES += random_cmd ## @} +PSEUDOMODULES += registry_% +NO_PSEUDOMODULES += registry_namespace_sys +NO_PSEUDOMODULES += registry_namespace_tests +NO_PSEUDOMODULES += registry_storage + PSEUDOMODULES += riotboot_% PSEUDOMODULES += rtt_cmd PSEUDOMODULES += saul_adc @@ -497,6 +502,7 @@ PSEUDOMODULES += shell_cmd_openwsn PSEUDOMODULES += shell_cmd_pm PSEUDOMODULES += shell_cmd_ps PSEUDOMODULES += shell_cmd_random +PSEUDOMODULES += shell_cmd_registry PSEUDOMODULES += shell_cmd_rtc PSEUDOMODULES += shell_cmd_rtt PSEUDOMODULES += shell_cmd_saul_reg diff --git a/sys/Kconfig b/sys/Kconfig index 97ed70068a16..d9540de18c57 100644 --- a/sys/Kconfig +++ b/sys/Kconfig @@ -95,6 +95,7 @@ rsource "progress_bar/Kconfig" rsource "ps/Kconfig" rsource "psa_crypto/Kconfig" rsource "random/Kconfig" +rsource "registry/Kconfig" rsource "rtc_utils/Kconfig" rsource "rust_riotmodules/Kconfig" rsource "saul_reg/Kconfig" diff --git a/sys/Makefile b/sys/Makefile index 835383e8b9a8..041dfe8c1de6 100644 --- a/sys/Makefile +++ b/sys/Makefile @@ -128,6 +128,9 @@ endif ifneq (,$(filter netutils,$(USEMODULE))) DIRS += net/netutils endif +ifneq (,$(filter registry,$(USEMODULE))) + DIRS += registry +endif ifneq (,$(filter sema,$(USEMODULE))) DIRS += sema endif diff --git a/sys/Makefile.dep b/sys/Makefile.dep index 8a7771accc16..53f6162b582a 100644 --- a/sys/Makefile.dep +++ b/sys/Makefile.dep @@ -706,4 +706,8 @@ ifneq (,$(filter wifi_scan_list,$(USEMODULE))) USEMODULE += l2scan_list endif +ifneq (,$(filter registry%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/Makefile.dep +endif + include $(RIOTBASE)/sys/test_utils/Makefile.dep diff --git a/sys/Makefile.include b/sys/Makefile.include index 450b3fe40180..5c6430bf4cf9 100644 --- a/sys/Makefile.include +++ b/sys/Makefile.include @@ -196,3 +196,7 @@ endif ifneq (,$(filter nanocoap,$(USEMODULE))) include $(RIOTBASE)/sys/net/application_layer/nanocoap/Makefile.include endif + +ifneq (,$(filter registry%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/Makefile.include +endif diff --git a/sys/include/registry.h b/sys/include/registry.h new file mode 100644 index 000000000000..db09a9ae7861 --- /dev/null +++ b/sys/include/registry.h @@ -0,0 +1,389 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry RIOT Registry + * @ingroup sys + * @brief RIOT Registry module for handling runtime configurations + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_H +#define REGISTRY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "clist.h" +#include "xfa.h" +#include "modules.h" +#include "registry/error.h" + +/** + * @brief Identifier of a configuration namespace. + * It is unique within the scope of the RIOT registry itself. + */ +typedef uint8_t registry_namespace_id_t; + +/** + * @brief Identifier of a configuration schema. + * It is unique within the scope of its parent configuration namespace. + */ +typedef uint32_t registry_schema_id_t; + +/** + * @brief Identifier of a schema instance. + * It is unique within the scope of its parent configuration schema. + */ +typedef uint16_t registry_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 registry_group_or_parameter_id_t; + +/** + * @brief Identifier of a configuration group. + * It is unique within the scope of its parent schema instance. + */ +typedef registry_group_or_parameter_id_t registry_group_id_t; + +/** + * @brief Identifier of a configuration parameter. + * It is unique within the scope of its parent schema instance. + */ +typedef registry_group_or_parameter_id_t registry_parameter_id_t; + + +/** + * @brief Data structure of a configuration namespace see @p _registry_namespace_t. + */ +typedef struct _registry_namespace_t registry_namespace_t; + +/** + * @brief Data structure of a configuration schema see @p _registry_schema_t. + */ +typedef struct _registry_schema_t registry_schema_t; + +/** + * @brief Instance of a schema containing its configuration parameters values see @p _registry_instance_t. + */ +typedef struct _registry_instance_t registry_instance_t; + +/** + * @brief Data structure of a configuration group see @p _registry_group_t. + */ +typedef struct _registry_group_t registry_group_t; + +/** + * @brief Data structure of a configuration parameter see @p _registry_parameter_t. + */ +typedef struct _registry_parameter_t registry_parameter_t; + + +/** + * @brief Data types of the registry. + */ +typedef enum { + REGISTRY_TYPE_NONE = 0, /**< No type specified */ + + REGISTRY_TYPE_OPAQUE, /**< OPAQUE. */ + REGISTRY_TYPE_STRING, /**< String. */ + REGISTRY_TYPE_BOOL, /**< Boolean. */ + + REGISTRY_TYPE_UINT8, /**< 8-bits unsigned integer. */ + REGISTRY_TYPE_UINT16, /**< 16-bits unsigned integer. */ + REGISTRY_TYPE_UINT32, /**< 32-bits unsigned integer. */ + REGISTRY_TYPE_UINT64, /**< 64-bits unsigned integer. */ + + REGISTRY_TYPE_INT8, /**< 8-bits signed integer. */ + REGISTRY_TYPE_INT16, /**< 16-bits signed integer. */ + REGISTRY_TYPE_INT32, /**< 32-bits signed integer. */ + REGISTRY_TYPE_INT64, /**< 64-bits signed integer. */ + + REGISTRY_TYPE_FLOAT32, /**< 32-bits float. */ + REGISTRY_TYPE_FLOAT64, /**< 64-bits float. */ +} registry_type_t; + +/** + * @brief The type of a registry node. + * A registry node points to a namespace, schema, instance, group or parameter. + */ +typedef enum { + REGISTRY_NODE_NAMESPACE = 0, + REGISTRY_NODE_SCHEMA, + REGISTRY_NODE_INSTANCE, + REGISTRY_NODE_GROUP, + REGISTRY_NODE_PARAMETER, +} registry_node_type_t; + +/** + * @brief A registry node represents a specific location within the registry configuration tree. + * It can point to a namespace, 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. + */ +typedef struct { + registry_node_type_t type; /**< The type of the node */ + union { + const registry_namespace_t *namespace; /**< Pointer to the configuration namespace */ + const registry_schema_t *schema; /**< Pointer to a configuration schema */ + const registry_instance_t *instance; /**< Pointer to a schema instance */ + struct { + const registry_instance_t *instance; /**< Pointer to a schema instance */ + const registry_group_t *group; /**< Pointer to a configuration group */ + } group; /**< A configuration group depends on an instance */ + struct { + const registry_instance_t *instance; /**< Pointer to a schema instance */ + const registry_parameter_t *parameter; /**< Pointer to a configuration parameter */ + } parameter; /**< A configuration parameter depends on an instance */ + } value; /**< The location inside of the configuration tree */ +} registry_node_t; + +/** + * @brief Basic representation of a configuration parameter value. + */ +typedef struct { + registry_type_t type; /**< The type of the configuration parameter value. */ + const void *buf; /**< Pointer to the buffer containing the value of the configuration parameter. */ + size_t buf_len; /**< Length of the buffer. */ +} registry_value_t; + +/** + * @brief The different scopes of a registry @p commit_cb function. + */ +typedef const enum { + REGISTRY_COMMIT_INSTANCE = 0, + REGISTRY_COMMIT_GROUP, + REGISTRY_COMMIT_PARAMETER, +} registry_commit_cb_scope_t; + +/** + * @brief The callback must be implemented by modules that integrate a configuration schema. + * This callback is called when the registry notifies the module, that a configuration + * parameter has changed. + * + * @param[in] scope Scope of what will be committed (a parameter, a group or the whole instance). + * @param[in] id ID of the group or parameter to commit changes to, commits the whole instance on NULL. + * @param[in] context Context of the instance. + * + * @return 0 on success, non-zero on failure. + */ +typedef registry_error_t (*registry_commit_cb_t)(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t * + group_or_parameter_id, const void *context); + +/** + * @brief Instance of a schema containing its configuration parameters values. + * The instance of a configuration schema is created by the modules that need to expose runtime configurations. + * The modules also implement the @p commit_cb function to react to configuration changes. + */ +struct _registry_instance_t { + clist_node_t node; /**< Linked list node pointing to the next schema instance. */ + const registry_instance_id_t id; /**< Integer representing the path id of the schema instance. */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + const char * const name; /**< String describing the instance. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + const void * const data; /**< Struct containing all configuration parameters of the schema. */ + const registry_schema_t *schema; /**< Configuration Schema that the Schema Instance belongs to. */ + registry_commit_cb_t commit_cb; /**< Will be called after @ref registry_commit() was called on this instance. */ + void *context; /**< Optional context used by the instance */ +}; + +/** + * @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 _registry_group_t { + const registry_group_id_t id; /**< Integer representing the ID of the configuration group. */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + const char * const name; /**< String describing the configuration group. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + const char * const description; /**< String describing the configuration group with more details. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + const registry_schema_t * const schema; /**< Configuration Schema that the configuration group belongs to. */ + const registry_group_t ** const groups; /**< Array of pointers to all the configuration groups that belong to this group. */ + const size_t groups_len; /**< Size of groups array. */ + const registry_parameter_t ** const parameters; /**< Array of pointers to all the configuration parameters that belong to this group. */ + const size_t parameters_len; /**< Size of parameters array. */ +}; + +/** + * @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 _registry_parameter_t { + const registry_parameter_id_t id; /**< Integer representing the ID of the configuration parameter. */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + const char * const name; /**< String describing the configuration parameter. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + const char * const description; /**< String describing the configuration parameter with more details. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + const registry_schema_t * const schema; /**< Configuration Schema that the configuration parameter belongs to. */ + const registry_type_t type; /**< Type of the configuration parameter. */ +}; + +/** + * @brief Data structure of a configuration schema. + * A configuration schema contains configuration configuration groups and/or configuration parameters. + * Besides that it also contains a list of instances that hold the actual configuration parameter values. + * It has an ID that is unique within the scope of its parent configuration namespace. + */ +struct _registry_schema_t { + const registry_schema_id_t id; /**< Integer representing the ID of the schema. */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + const char * const name; /**< String describing the schema. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + const char * const description; /**< String describing the schema with more details. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + const registry_namespace_t * const namespace; /**< Configuration Namespace that the Configuration Schema belongs to. */ + clist_node_t instances; /**< Linked list of schema instances @ref registry_instance_t. */ + const registry_group_t ** const groups; /**< Array of pointers to all the configuration groups that belong to this schema. */ + const size_t groups_len; /**< Size of groups array. */ + const registry_parameter_t ** const parameters; /**< Array of pointers to all the configuration parameters that belong to this schema. */ + const size_t parameters_len; /**< Size of parameters array. */ + + /** + * @brief Mapping to connect configuration parameter IDs with the address in the storage. + * + * @param[in] parameter_id ID of the parameter that contains the value. + * @param[in] instance Pointer to the instance of the schema, 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. + */ + void(*const mapping)(const registry_parameter_id_t parameter_id, + const registry_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 RIOT registry itself. + */ +struct _registry_namespace_t { + const registry_namespace_id_t id; /**< Integer representing the ID of the namespace. */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + const char * const name; /**< String describing the configuration namespace. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + const char * const description; /**< String describing the configuration namespace with more details. */ +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + const registry_schema_t ** const schemas; /**< Array of pointers to all the configuration schemas that belong to this namespace. */ + const size_t schemas_len; /**< Size of schemas array. */ +}; + +#define REGISTRY_ADD_NAMESPACE(_name, _namespace) \ + XFA_USE_CONST(registry_namespace_t *, _registry_namespaces_xfa); \ + XFA_ADD_PTR(_registry_namespaces_xfa, _name, _name, &_namespace) + +/** + * @brief Initializes the RIOT Registry. + */ +void registry_init(void); + +/** + * @brief Adds a new instance to a schema. + * + * @param[in] schema Pointer to the schema. + * @param[in] instance Pointer to the new instance. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_add_schema_instance(const registry_schema_t *schema, + const registry_instance_t *instance); + +/** + * @brief Gets the current value of a parameter that belongs to an instance + * of a configuration schema. + * + * @param[in] node A location within the registry configuration tree. + * @param[out] value Pointer to a uninitialized @ref registry_value_t struct. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_get(const registry_node_t *node, registry_value_t *value); + +/** + * @brief Sets the value of a configuration parameter that belongs to an instance + * of a configuration schema. + * + * @param[in] node A location within the registry configuration tree. + * @param[in] buf Pointer to the new value for the configuration parameter. + * @param[in] buf_len Length of the buffer. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_set(const registry_node_t *node, const void *buf, const size_t buf_len); + +/** + * @brief Commits every configuration parameter within the given configuration location (@p node) of the registry configuration tree. + * + * @param[in] node A location within the registry configuration tree. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_commit(const registry_node_t *node); + +/** + * @brief Callback definition of the @p registry_export function. + * This callback will be called for each location inside of the configuration tree that is + * within the scope of the registry node passed on to the @p registry_export function. + * + * @param[in] node A location within the registry configuration tree. + * @param[in] context Context that is passed by the @p registry_export function. + * + * @return 0 on success, non-zero on failure. + */ +typedef registry_error_t (*registry_export_cb_t)(const registry_node_t *node, const void *context); + +#define REGISTRY_EXPORT_ALL 0 +#define REGISTRY_EXPORT_SELF 1 +#define REGISTRY_EXPORT_WITH_N_LEVELS_OF_CHILDREN(_n) (_n + 1) + +/** + * @brief Exports every configuration parameter within the given configuration location (@p node) of the registry configuration tree. + * + * @param[in] node A location within the registry configuration tree. + * @param[in] export_cb Exporting callback function will be called for each namespace, + * schema, instance, group and parameter that are in scope. + * @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, n > 1 + * to show the exact match plus n - 1 levels of children). + * @param[in] context Context that will be passed to @p export_cb. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_export(const registry_node_t *node, const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, const void *context); + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_H */ +/** @} */ diff --git a/sys/include/registry/error.h b/sys/include/registry/error.h new file mode 100644 index 000000000000..d67e94eac1cc --- /dev/null +++ b/sys/include/registry/error.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_error RIOT Registry Error + * @ingroup sys + * @brief RIOT Registry module providing all possible registry specific error codes + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_ERROR_H +#define REGISTRY_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Registry specific error codes. + */ +typedef enum { + REGISTRY_ERROR_NONE = 0, + REGISTRY_ERROR_INVALID_ARGUMENT, + REGISTRY_ERROR_NAMESPACE_NOT_FOUND, + REGISTRY_ERROR_SCHEMA_NOT_FOUND, + REGISTRY_ERROR_INSTANCE_NOT_FOUND, + REGISTRY_ERROR_GROUP_NOT_FOUND, + REGISTRY_ERROR_PARAMETER_NOT_FOUND, + REGISTRY_ERROR_NODE_INVALID, + REGISTRY_ERROR_BUF_LEN_TOO_SMALL, + REGISTRY_ERROR_BUF_LEN_TOO_LARGE, + REGISTRY_ERROR_SCHEMA_HAS_NO_INSTANCE, +} registry_error_t; + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_ERROR_H */ +/** @} */ diff --git a/sys/include/registry/find.h b/sys/include/registry/find.h new file mode 100644 index 000000000000..90e4ba8b0d6d --- /dev/null +++ b/sys/include/registry/find.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_find RIOT Registry utility to find registry nodes + * @ingroup sys + * @brief RIOT Registry Find module for finding specific nodes within the configuration tree + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#ifndef REGISTRY_TREE_TRAVERSAL_H +#define REGISTRY_TREE_TRAVERSAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" +#include "registry/error.h" + +/** + * @brief The different return types of @p registry_find_comparator_t callback function. + */ +typedef enum { + REGISTRY_FIND_NO_MATCH = -1, + REGISTRY_FIND_EXACT_MATCH = 0, + REGISTRY_FIND_PARTIAL_MATCH = 1, +} registry_find_result_type; + +/** + * @brief This callback is called by the @p registry_find function passing in a @p context, + * to compare a @p node with it. + * + * @param[in] node A location within the registry configuration tree. + * @param[in] context Context of the callback (contains the data to compare each @p node to). + * + * @return 0 on exact match, 1 on partial match and -1 on no match. + */ +typedef registry_find_result_type (*registry_find_comparator_t)(const registry_node_t *node, + const void *context); + +/** + * @brief Finds a specific node within the registry configuration tree. + * The comparison of nodes is handled by the @p compare callback function. + * The context to compare to is passed to it using the @p context parameter. + * + * @param[in] compare Callback function that is invoked on the nodes of the configuration tree. + * @param[in] context Optional context used by the @p compare callback function. + * @param[out] node A location within the registry configuration tree. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_find(const registry_find_comparator_t compare, + const void *context, registry_node_t *node); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* REGISTRY_TREE_TRAVERSAL_H */ diff --git a/sys/include/registry/int_path.h b/sys/include/registry/int_path.h new file mode 100644 index 000000000000..28d5061f484c --- /dev/null +++ b/sys/include/registry/int_path.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_int_path RIOT Registry integer path + * @ingroup sys + * @brief RIOT Registry integer path module + * @{ + * + * This module provides functions to convert between @ref registry_node_t and + * @ref registry_int_path_t. + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_INT_PATH_H +#define REGISTRY_INT_PATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" +#include "registry/error.h" + +/** + * @brief Maximum length of a configuration path. + * + * Path: namespace_id / schema_id / instance_id / (group_id | parameter_id). + */ +#define REGISTRY_INT_PATH_MAX_LEN 4 + +/** + * @brief Maximum length of a configuration path as a string of numbers. + * + * An int path consists of the following elements: + * namespace_id: 8 bits corresponds to a max of 3 characters + * schema_id: 32 bits corresponds to a max of 10 characters + * instance_id: 16 bits corresponds to a max of 5 characters + * group__or_parameter_id: 8 bits corresponds to a max of 3 characters + * We also need to include the separator. One additional char between each number. + */ +#define REGISTRY_INT_PATH_STRING_MAX_LEN (3 + 10 + 5 + 3 + (REGISTRY_INT_PATH_MAX_LEN - 1)) + +/** + * @brief Integer path representation for a namespace. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ +} registry_namespace_int_path_t; + +/** + * @brief Integer path representation for a configuration schema. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ + registry_schema_id_t schema_id; /**< The ID of the schema. */ +} registry_schema_int_path_t; + +/** + * @brief Integer path representation for a configuration schema instance. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ + registry_schema_id_t schema_id; /**< The ID of the schema. */ + registry_instance_id_t instance_id; /**< The ID of the instance. */ +} registry_instance_int_path_t; + +/** + * @brief Integer path representation for a configuration group. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ + registry_schema_id_t schema_id; /**< The ID of the schema. */ + registry_instance_id_t instance_id; /**< The ID of the instance. */ + registry_group_id_t group_id; /**< The ID of the group. */ +} registry_group_int_path_t; + +/** + * @brief Integer path representation for a configuration parameter. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ + registry_schema_id_t schema_id; /**< The ID of the schema. */ + registry_instance_id_t instance_id; /**< The ID of the instance. */ + registry_parameter_id_t parameter_id; /**< The ID of the parameter. */ +} registry_parameter_int_path_t; + +/** + * @brief Integer path representation for a configuration group or parameter. + */ +typedef struct { + registry_namespace_id_t namespace_id; /**< The ID of the namespace. */ + registry_schema_id_t schema_id; /**< The ID of the schema. */ + registry_instance_id_t instance_id; /**< The ID of the instance. */ + registry_group_or_parameter_id_t group_or_parameter_id; /**< The ID of the group or parameter. */ +} registry_group_or_parameter_int_path_t; + +/** + * @brief Enumeration of the different types of integer paths. + */ +typedef enum { + REGISTRY_INT_PATH_TYPE_NAMESPACE, /**< The path represents a namespace. */ + REGISTRY_INT_PATH_TYPE_SCHEMA, /**< The path represents a schema. */ + REGISTRY_INT_PATH_TYPE_INSTANCE, /**< The path represents an instance. */ + REGISTRY_INT_PATH_TYPE_GROUP, /**< The path represents a group. */ + REGISTRY_INT_PATH_TYPE_PARAMETER, /**< The path represents a parameter. */ + REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER, /**< The path represents a group or parameter. */ +} registry_int_path_type_t; + +/** + * @brief Union of the different types of integer paths. + */ +typedef struct { + registry_int_path_type_t type; + union { + registry_namespace_int_path_t namespace_path; /**< The path represents a namespace. */ + registry_schema_int_path_t schema_path; /**< The path represents a schema. */ + registry_instance_int_path_t instance_path; /**< The path represents an instance. */ + registry_group_int_path_t group_path; /**< The path represents a group. */ + registry_parameter_int_path_t parameter_path; /**< The path represents a parameter. */ + registry_group_or_parameter_int_path_t group_or_parameter_path; /**< The path represents a group or parameter. */ + } value; +} registry_int_path_t; + +/** + * @brief Converts a registry namespace to an integer path. + * + * @param[in] node A location within the registry configuration tree. + * @param[out] path The integer path. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_node_to_int_path(const registry_node_t *node, registry_int_path_t *path); + +/** + * @brief Converts an integer path to a registry namespace. + * + * @param[in] path The integer path to convert. + * @param[out] node A location within the registry configuration tree. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_node_from_int_path(const registry_int_path_t *path, + registry_node_t *node); + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_INT_PATH_H */ +/** @} */ diff --git a/sys/include/registry/namespace/sys.h b/sys/include/registry/namespace/sys.h new file mode 100644 index 000000000000..02208e984f51 --- /dev/null +++ b/sys/include/registry/namespace/sys.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_sys RIOT Registry Sys Namespace + * @ingroup sys + * @brief RIOT Registry Namespace Sys module + * @{ + * + * This module provides common sys configuration schemas for the RIOT Registry sys module + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_SYS_H +#define REGISTRY_NAMESPACE_SYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +extern registry_namespace_t registry_sys; + +/** + * @brief This ENUM defines the IDs of configuration schemas in the "sys" namespace. + * The IDs are needed by the int_path module to identify schemas using IDs instead + * of pointers. + */ +typedef enum { + REGISTRY_SYS_BOARD_LED = 0, + REGISTRY_SYS_RGB_LED = 1, +} registry_sys_indices_t; + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_NAMESPACE_SYS_H */ +/** @} */ diff --git a/sys/include/registry/namespace/sys/board_led.h b/sys/include/registry/namespace/sys/board_led.h new file mode 100644 index 000000000000..ff24fa5c6d4a --- /dev/null +++ b/sys/include/registry/namespace/sys/board_led.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_sys_board_led RIOT Registry Schema: BOARD_LED + * @ingroup sys + * @brief RIOT Registry BOARD_LED Schema representing the basic structure of a BOARD LED + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_SYS_BOARD_LED_H +#define REGISTRY_NAMESPACE_SYS_BOARD_LED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/* BOARD-LED */ +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_BOARD_LED) || IS_ACTIVE(DOXYGEN) + +extern const registry_parameter_t registry_sys_board_led_enabled; +extern registry_schema_t registry_sys_board_led; + +typedef struct { + clist_node_t node; + bool enabled; +} registry_sys_board_led_instance_t; + +typedef const enum { + REGISTRY_SYS_BOARD_LED_ENABLED, +} registry_sys_board_led_indices_t; + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_NAMESPACE_SYS_BOARD_LED_H */ +/** @} */ diff --git a/sys/include/registry/namespace/sys/rgb_led.h b/sys/include/registry/namespace/sys/rgb_led.h new file mode 100644 index 000000000000..413b20bd7fc0 --- /dev/null +++ b/sys/include/registry/namespace/sys/rgb_led.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_sys_rgb_led RIOT Registry Schema: RGB_LED + * @ingroup sys + * @brief RIOT Registry RGB_LED Schema representing the basic structure of an RGB LED + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_SYS_RGB_LED_H +#define REGISTRY_NAMESPACE_SYS_RGB_LED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/* RGB-LED */ +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_RGB_LED) || IS_ACTIVE(DOXYGEN) + +extern const registry_parameter_t registry_sys_rgb_led_red; +extern const registry_parameter_t registry_sys_rgb_led_green; +extern const registry_parameter_t registry_sys_rgb_led_blue; +extern registry_schema_t registry_sys_rgb_led; + +typedef struct { + clist_node_t node; + uint8_t red; + uint8_t green; + uint8_t blue; +} registry_sys_rgb_led_instance_t; + +typedef const enum { + REGISTRY_SYS_RGB_LED_RED, + REGISTRY_SYS_RGB_LED_GREEN, + REGISTRY_SYS_RGB_LED_BLUE, +} registry_sys_rgb_led_indices_t; + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_NAMESPACE_SYS_RGB_LED_H */ +/** @} */ diff --git a/sys/include/registry/storage.h b/sys/include/registry/storage.h new file mode 100644 index 000000000000..ff1531ab9c46 --- /dev/null +++ b/sys/include/registry/storage.h @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_storage RIOT Registry Storage + * @ingroup sys + * @brief RIOT Registry Storage module allowing to store configuration parameters to non-volatile storage + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_STORAGE_H +#define REGISTRY_STORAGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/** + * @brief Prototype of a callback function for the load action of a storage interface. + * + * @param[in] node A location within the registry configuration tree. (In this case it must be a configuration parameter) + * @param[in] buf The saved value of the configuration parameter. + * @param[in] buf_len The size of @p buf. + * + * @return 0 on success, non-zero on failure. + */ +typedef registry_error_t (*load_cb_t)(const registry_node_t *node, const void *buf, + const size_t buf_len); + +typedef struct _registry_storage_t registry_storage_t; + +/** + * @brief The Storage instance references the @p registry_storage_t and holds configuration data such as mount points. + */ +typedef struct { + registry_storage_t *storage; /**< interface of the storage. */ + void *data; /**< Struct containing all config data for the storage (specific to storage implementation). */ +} registry_storage_instance_t; + +/** + * @brief Storage interface. All storages should, at least, implement the load and save actions. + */ +struct _registry_storage_t { + /** + * @brief Loads all saved parameters and calls the @p load_cb callback function. + * + * @param[in] storage_instance Storage instance to load the configurations from. + * @param[in] load_cb Callback function to call for every saved parameter. + * + * @return 0 on success, non-zero on failure. + */ + registry_error_t (*load)(const registry_storage_instance_t *storage, + const load_cb_t load_cb); + + /** + * @brief If implemented, it is used for any preparation the storage may + * need before starting a saving process. + * NULL if not implemented + * + * @param[in] storage_instance Storage instance to save the configurations to. + * + * @return 0 on success, non-zero on failure. + */ + registry_error_t (*save_start)(const registry_storage_instance_t *storage); + + /** + * @brief Saves a parameter into storage. + * + * @param[in] storage_instance Storage instance to save the configurations to. + * @param[in] node A location within the registry configuration tree. + * @param[in] value Configuration parameter value. + * + * @return 0 on success, non-zero on failure. + */ + registry_error_t (*save)(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value); + + /** + * @brief If implemented, it is used for any tear-down the storage may need + * after a saving process. + * NULL if not implemented + * + * @param[in] storage_instance Storage instance to save the configurations to. + * + * @return 0 on success, non-zero on failure. + */ + registry_error_t (*save_end)(const registry_storage_instance_t *storage); +}; + +/** + * @brief Load all configuration parameters from the storages that are registered + * using @p REGISTRY_ADD_STORAGE_SOURCE. + * + * @param[in] storage_instance Storage instance to load the configurations from. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_storage_load(const registry_storage_instance_t *storage_instance); + +/** + * @brief Save all configuration parameters that are within + * the scope of the to the @p node. to the storage device, that was registered + * using @p REGISTRY_SET_STORAGE_DESTINATION. + * + * @param[in] storage_instance Storage instance to save the configurations to. + * @param[in] node A location within the registry configuration tree. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_storage_save(const registry_storage_instance_t *storage_instance, + const registry_node_t *node); + +/** + * @brief Set storage instances to expose them to configuration managers such as + * the RIOT CLI. + * + * @param[in] storage_instances An array of pointers to storage instances. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_storage_set_instances( + const registry_storage_instance_t **storage_instances); + +/** + * @brief Get exposed storage instances to use them in a configuration manager + * such as the RIOT CLI. + * + * @param[out] storage_instances An array of pointers to storage instances. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_storage_get_instances( + const registry_storage_instance_t ***storage_instances); + +/* heap */ +#if IS_USED(MODULE_REGISTRY_STORAGE_HEAP) || IS_ACTIVE(DOXYGEN) +extern registry_storage_t registry_storage_heap; +#endif + +/* vfs */ +#if IS_USED(MODULE_REGISTRY_STORAGE_VFS) || IS_ACTIVE(DOXYGEN) +extern registry_storage_t registry_storage_vfs; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_STORAGE_H */ +/** @} */ diff --git a/sys/include/registry/string_path.h b/sys/include/registry/string_path.h new file mode 100644 index 000000000000..40635ffa9fa5 --- /dev/null +++ b/sys/include/registry/string_path.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_string_path RIOT Registry String Path + * @ingroup sys + * @brief RIOT Registry String Path module + * @{ + * + * This module provides functions to convert between @ref registry_node_t and + * a string path representation of it. + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_STRING_PATH_H +#define REGISTRY_STRING_PATH_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" +#include "registry/error.h" + +/** + * @brief Converts a registry namespace to its string path representation. + * + * @param[in] node A location within the registry configuration tree. + * @param[out] path The buffer to store the resulting string path. + * @param[out] path_len Length of the path. + * + * @return Length of string path on success, non-zero on failure. + */ +int registry_node_to_string_path(const registry_node_t *node, char *path); + +/** + * @brief Converts a string path to a registry namespace. + * + * @param[in] path The string path array to convert. + * @param[in] path_len Length of the path. + * @param[out] node A location within the registry configuration tree. + * + * @return 0 on success, non-zero on failure. + */ +registry_error_t registry_node_from_string_path(const char **path, const size_t path_len, + registry_node_t *node); + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_STRING_PATH_H */ +/** @} */ diff --git a/sys/include/registry/util.h b/sys/include/registry/util.h new file mode 100644 index 000000000000..92b2b42b5b10 --- /dev/null +++ b/sys/include/registry/util.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_util RIOT Registry utilities + * @ingroup sys + * @brief RIOT Registry Util module providing utility functions + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#ifndef REGISTRY_UTIL_H +#define REGISTRY_UTIL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/** + * @brief Convenience function to parse a configuration parameter value from + * a string. + * + * @param[in] src Pointer of the input value. + * @param[out] dest Pointer to the output buffer. + * @param[in] dest_len Length of @p dest. + * @param[in] dest_type Type of the output value. + * + * @return 0 on success, non-zero on failure. + */ +int registry_util_convert_str_to_value(const char *src, void *dest, const size_t dest_len, + const registry_type_t dest_type); + +/** + * @brief Convenience function to transform a configuration parameter value into + * its representation as a string. + * + * @param[in] src Pointer to the value to be converted. + * @param[out] dest Buffer to store the output string. + * @param[in] dest_len Length of @p buf. + * + * @return Length of string value on success, non-zero on failure. + */ +int registry_util_convert_value_to_str(const registry_value_t *src, char *dest, + const size_t dest_len); + +#ifdef __cplusplus +} +#endif + +/** @} */ +#endif /* REGISTRY_UTIL_H */ diff --git a/sys/registry/Kconfig b/sys/registry/Kconfig new file mode 100644 index 000000000000..c0061e8ad5f7 --- /dev/null +++ b/sys/registry/Kconfig @@ -0,0 +1,30 @@ +# Copyright (c) 2023 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_REGISTRY + bool "REGISTRY" + help + RIOT Registry module for handling runtime configurations. + +if MODULE_REGISTRY + +config REGISTRY_ENABLE_META_NAME + bool "Include name string in schemas" + +config REGISTRY_ENABLE_META_DESCRIPTION + bool "Include description string in schemas" + +config MODULE_REGISTRY_INT_PATH + bool "Access configuration parameters via an integer path" + +config MODULE_REGISTRY_STRING_PATH + bool "Access configuration parameters via a string path" + +rsource "namespace/Kconfig" +rsource "storage/Kconfig" + +endif # MODULE_REGISTRY diff --git a/sys/registry/Makefile b/sys/registry/Makefile new file mode 100644 index 000000000000..ee04422a8c26 --- /dev/null +++ b/sys/registry/Makefile @@ -0,0 +1,13 @@ +SRC := registry.c init.c + +SUBMODULES := 1 + +ifneq (,$(filter registry_namespace_%,$(USEMODULE))) + DIRS += namespace +endif + +ifneq (,$(filter registry_storage%,$(USEMODULE))) + DIRS += storage +endif + +include $(RIOTBASE)/Makefile.base diff --git a/sys/registry/Makefile.dep b/sys/registry/Makefile.dep new file mode 100644 index 000000000000..9a7fd7d7ecfd --- /dev/null +++ b/sys/registry/Makefile.dep @@ -0,0 +1,23 @@ +ifneq (,$(filter registry_namespace%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/namespace/Makefile.dep +endif + +ifneq (,$(filter registry_storage%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/storage/Makefile.dep +endif + +ifneq (,$(filter registry_int_path%,$(USEMODULE))) + USEMODULE += registry_find +endif + +ifneq (,$(filter registry_string_path%,$(USEMODULE))) + USEMODULE += registry_find +endif + +ifneq (,$(filter registry_util%,$(USEMODULE))) + USEMODULE += base64 +endif + +ifneq (,$(filter registry_%,$(USEMODULE))) + USEMODULE += registry +endif diff --git a/sys/registry/Makefile.include b/sys/registry/Makefile.include new file mode 100644 index 000000000000..d43c8233d207 --- /dev/null +++ b/sys/registry/Makefile.include @@ -0,0 +1,3 @@ +ifneq (,$(filter registry_string_path%,$(USEMODULE))) + CFLAGS += -DCONFIG_REGISTRY_ENABLE_META_NAME +endif diff --git a/sys/registry/find.c b/sys/registry/find.c new file mode 100644 index 000000000000..3eed6f2421e9 --- /dev/null +++ b/sys/registry/find.c @@ -0,0 +1,287 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_find RIOT Registry utility to find registry nodes + * @ingroup sys + * @brief RIOT Registry Find module for finding specific nodes within the configuration tree + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" + +#include "registry.h" +#include "registry/error.h" +#include "registry/find.h" + +XFA_USE_CONST(registry_namespace_t *, _registry_namespaces_xfa); + +static registry_find_result_type _find_group(const registry_group_t **groups, + const size_t groups_len, + const registry_find_comparator_t compare, + const void *context, const registry_group_t **group) +{ + assert(groups != NULL); + assert(groups_len > 0); + assert(compare != NULL); + + registry_find_result_type res; + registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + }; + + for (size_t i = 0; i < groups_len; i++) { + node.value.group.group = groups[i]; + res = compare(&node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + *group = groups[i]; + return res; + } + + /* if a partial match is found, we need to keep searching for its children, but can break the loop */ + if (res == REGISTRY_FIND_PARTIAL_MATCH) { + return _find_group(groups[i]->groups, groups[i]->groups_len, compare, context, group); + } + } + + return -REGISTRY_ERROR_GROUP_NOT_FOUND; +} + +static registry_find_result_type _find_parameter_by_group( + const registry_group_t *group, const registry_find_comparator_t compare, + const void *context, const registry_parameter_t **parameter) +{ + assert(group != NULL); + assert(compare != NULL); + + registry_find_result_type res; + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + }; + + /* search parameter at group root */ + for (size_t i = 0; i < group->parameters_len; i++) { + node.value.parameter.parameter = group->parameters[i]; + res = compare(&node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + *parameter = group->parameters[i]; + return res; + } + } + + /* search parameter inside sub-groups */ + for (size_t i = 0; i < group->groups_len; i++) { + res = _find_parameter_by_group(group->groups[i], compare, context, parameter); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + return res; + } + } + + return -REGISTRY_ERROR_PARAMETER_NOT_FOUND; +} + +static registry_find_result_type _find_parameter_by_schema( + const registry_schema_t *schema, const registry_find_comparator_t compare, + const void *context, const registry_parameter_t **parameter) +{ + assert(schema != NULL); + assert(compare != NULL); + + registry_find_result_type res; + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + }; + + /* search parameter at schema root */ + for (size_t i = 0; i < schema->parameters_len; i++) { + node.value.parameter.parameter = schema->parameters[i]; + res = compare(&node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + *parameter = schema->parameters[i]; + return res; + } + } + + /* search parameter inside groups */ + for (size_t i = 0; i < schema->groups_len; i++) { + res = _find_parameter_by_group(schema->groups[i], compare, context, parameter); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + return res; + } + } + + return -REGISTRY_ERROR_PARAMETER_NOT_FOUND; +} + +registry_error_t registry_find(const registry_find_comparator_t compare, + const void *context, registry_node_t *node) +{ + assert(compare != NULL); + assert(context != NULL); + + registry_find_result_type res = REGISTRY_FIND_NO_MATCH; + registry_node_t compare_node; + + /* Namespace */ + const registry_namespace_t *namespace; + for (size_t i = 0; i < XFA_LEN(registry_namespace_t *, _registry_namespaces_xfa); i++) { + namespace = _registry_namespaces_xfa[i]; + compare_node.type = REGISTRY_NODE_NAMESPACE; + compare_node.value.namespace = namespace; + res = compare(&compare_node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_NAMESPACE; + node->value.namespace = namespace; + return 0; + } + + /* if a partial match is found, we need to keep searching for its children, but can break the loop */ + if (res == REGISTRY_FIND_PARTIAL_MATCH) { + break; + } + } + + /* Check if a namespace was found */ + if (res == REGISTRY_FIND_NO_MATCH) { + return -REGISTRY_ERROR_NAMESPACE_NOT_FOUND; + } + + /* Schema */ + const registry_schema_t *schema = NULL; + for (size_t i = 0; i < namespace->schemas_len; i++) { + schema = namespace->schemas[i]; + compare_node.type = REGISTRY_NODE_SCHEMA; + compare_node.value.schema = schema; + res = compare(&compare_node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_SCHEMA; + node->value.schema = schema; + return 0; + } + + /* if a partial match is found, we need to keep searching for its children, but can break the loop */ + if (res == REGISTRY_FIND_PARTIAL_MATCH) { + break; + } + } + + /* Check if a schema was found */ + if (res == REGISTRY_FIND_NO_MATCH) { + return -REGISTRY_ERROR_SCHEMA_NOT_FOUND; + } + + + if (schema != NULL) { + /* Instance */ + const registry_instance_t *instance = NULL; + clist_node_t *instance_node = schema->instances.next; + if (instance_node) { + do { + instance_node = instance_node->next; + instance = container_of(instance_node, registry_instance_t, node); + compare_node.type = REGISTRY_NODE_INSTANCE; + compare_node.value.instance = instance; + + res = compare(&compare_node, context); + + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_INSTANCE; + node->value.instance = instance; + return 0; + } + + /* if a partial match is found, we need to keep searching for its children, but can break the loop */ + if (res == REGISTRY_FIND_PARTIAL_MATCH) { + break; + } + } while (instance_node != schema->instances.next); + } + + /* Check if an instance was found */ + if (res == REGISTRY_FIND_NO_MATCH) { + return -REGISTRY_ERROR_INSTANCE_NOT_FOUND; + } + + if (instance != NULL) { + /* Group */ + const registry_group_t *group; + res = _find_group(schema->groups, schema->groups_len, compare, context, &group); + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_GROUP; + node->value.group.group = group; + node->value.group.instance = instance; + return 0; + } + + /* if a partial match is found, we need to keep searching for its children */ + if (res == REGISTRY_FIND_PARTIAL_MATCH) { + const registry_parameter_t *parameter; + res = _find_parameter_by_group(node->value.group.group, compare, context, + ¶meter); + + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_PARAMETER; + node->value.parameter.parameter = parameter; + node->value.parameter.instance = instance; + return 0; + } + + /* A partial match of a parameter cannot happen as it is a leaf of the configuration tree */ + + return -REGISTRY_ERROR_PARAMETER_NOT_FOUND; + } + + /* Parameter */ + const registry_parameter_t *parameter; + res = _find_parameter_by_schema(schema, compare, context, ¶meter); + /* if an exact match is found, we can set it and return the function */ + if (res == REGISTRY_FIND_EXACT_MATCH) { + node->type = REGISTRY_NODE_PARAMETER; + node->value.parameter.parameter = parameter; + node->value.parameter.instance = instance; + return 0; + } + + /* A partial match of a parameter cannot happen as it is a leaf of the configuration tree */ + } + } + + return -REGISTRY_ERROR_PARAMETER_NOT_FOUND; +} diff --git a/sys/registry/init.c b/sys/registry/init.c new file mode 100644 index 000000000000..12548e8b8b15 --- /dev/null +++ b/sys/registry/init.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_init RIOT Registry initialization + * @ingroup sys + * @brief RIOT Registry Init module providing init functions + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#include "registry.h" + +#include "auto_init_utils.h" +#if IS_USED(MODULE_AUTO_INIT) +#endif + +XFA_USE_CONST(registry_namespace_t *, _registry_namespaces_xfa); + +void registry_init(void) +{ + /* set namespace_id to its index value */ + for (size_t i = 0; i < XFA_LEN(registry_namespace_t *, _registry_namespaces_xfa); i++) { + registry_namespace_t *namespace = _registry_namespaces_xfa[i]; + + *(registry_namespace_id_t *)&namespace->id = i; + } +} + +AUTO_INIT(registry_init, 2000); diff --git a/sys/registry/int_path.c b/sys/registry/int_path.c new file mode 100644 index 000000000000..4c78f37962ab --- /dev/null +++ b/sys/registry/int_path.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_int_path RIOT Registry Int Path + * @ingroup sys + * @brief RIOT Registry integer path module + * @{ + * + * This module provides functions to convert between @ref registry_node_t and + * @ref registry_int_path_t. + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "clist.h" +#include "registry.h" +#include "registry/util.h" +#include "registry/error.h" +#include "registry/find.h" + +#include "registry/int_path.h" + +registry_error_t registry_node_to_int_path(const registry_node_t *node, registry_int_path_t *path) +{ + assert(node != NULL); + assert(path != NULL); + + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + path->type = REGISTRY_INT_PATH_TYPE_NAMESPACE; + path->value.namespace_path.namespace_id = node->value.namespace->id; + break; + + case REGISTRY_NODE_SCHEMA: + path->type = REGISTRY_INT_PATH_TYPE_SCHEMA; + path->value.schema_path.namespace_id = node->value.schema->namespace->id; + path->value.schema_path.schema_id = node->value.schema->id; + break; + + case REGISTRY_NODE_INSTANCE: + path->type = REGISTRY_INT_PATH_TYPE_INSTANCE; + path->value.instance_path.namespace_id = node->value.instance->schema->namespace->id; + path->value.instance_path.schema_id = node->value.instance->schema->id; + path->value.instance_path.instance_id = node->value.instance->id; + break; + + case REGISTRY_NODE_GROUP: + path->type = REGISTRY_INT_PATH_TYPE_GROUP; + path->value.group_path.namespace_id = node->value.group.group->schema->namespace->id; + path->value.group_path.schema_id = node->value.group.group->schema->id; + path->value.group_path.instance_id = node->value.group.instance->id; + path->value.group_path.group_id = node->value.group.group->id; + break; + + case REGISTRY_NODE_PARAMETER: + path->type = REGISTRY_INT_PATH_TYPE_PARAMETER; + path->value.parameter_path.namespace_id = + node->value.parameter.parameter->schema->namespace->id; + path->value.parameter_path.schema_id = node->value.parameter.parameter->schema->id; + path->value.parameter_path.instance_id = node->value.parameter.instance->id; + path->value.parameter_path.parameter_id = node->value.parameter.parameter->id; + break; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_find_result_type _compare_node_by_id(const registry_node_t *node, + const void *context) +{ + const registry_int_path_t *path = context; + + bool id_matches = false; + bool path_type_matches = false; + + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + id_matches = node->value.namespace->id == path->value.namespace_path.namespace_id; + path_type_matches = path->type == REGISTRY_INT_PATH_TYPE_NAMESPACE; + break; + + case REGISTRY_NODE_SCHEMA: + id_matches = node->value.schema->id == path->value.schema_path.schema_id; + path_type_matches = path->type == REGISTRY_INT_PATH_TYPE_SCHEMA; + break; + + case REGISTRY_NODE_INSTANCE: + id_matches = node->value.instance->id == path->value.instance_path.instance_id; + path_type_matches = path->type == REGISTRY_INT_PATH_TYPE_INSTANCE; + break; + + case REGISTRY_NODE_GROUP: + id_matches = node->value.group.group->id == path->value.group_path.group_id; + path_type_matches = path->type == REGISTRY_INT_PATH_TYPE_GROUP || + path->type == REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER; + break; + + case REGISTRY_NODE_PARAMETER: + id_matches = node->value.parameter.parameter->id == path->value.parameter_path.parameter_id; + path_type_matches = path->type == REGISTRY_INT_PATH_TYPE_PARAMETER || + path->type == REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER; + break; + } + + if (id_matches) { + if (path_type_matches) { + return REGISTRY_FIND_EXACT_MATCH; + } + + return REGISTRY_FIND_PARTIAL_MATCH; + } + + return REGISTRY_FIND_NO_MATCH; +} + +registry_error_t registry_node_from_int_path(const registry_int_path_t *path, registry_node_t *node) +{ + assert(path != NULL); + assert(node != NULL); + + return registry_find(_compare_node_by_id, path, node); +} diff --git a/sys/registry/namespace/Kconfig b/sys/registry/namespace/Kconfig new file mode 100644 index 000000000000..4d7a2a12f47f --- /dev/null +++ b/sys/registry/namespace/Kconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2023 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_REGISTRY_NAMESPACE + bool "REGISTRY_NAMESPACE" + depends on MODULE_REGISTRY + help + Namespace module containing default namespaces for the RIOT Registry sys module. + +if MODULE_REGISTRY_NAMESPACE + +rsource "sys/Kconfig" +rsource "tests/Kconfig" + +endif # MODULE_REGISTRY_NAMESPACE \ No newline at end of file diff --git a/sys/registry/namespace/Makefile b/sys/registry/namespace/Makefile new file mode 100644 index 000000000000..9cd1d452108d --- /dev/null +++ b/sys/registry/namespace/Makefile @@ -0,0 +1,9 @@ +ifneq (,$(filter registry_namespace_sys%,$(USEMODULE))) + DIRS += sys +endif + +ifneq (,$(filter registry_namespace_tests%,$(USEMODULE))) + DIRS += tests +endif + +include $(RIOTBASE)/Makefile.base \ No newline at end of file diff --git a/sys/registry/namespace/Makefile.dep b/sys/registry/namespace/Makefile.dep new file mode 100644 index 000000000000..ff87a2711e5f --- /dev/null +++ b/sys/registry/namespace/Makefile.dep @@ -0,0 +1,7 @@ +ifneq (,$(filter registry_namespace_sys%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/namespace/sys/Makefile.dep +endif + +ifneq (,$(filter registry_namespace_tests%,$(USEMODULE))) + include $(RIOTBASE)/sys/registry/namespace/tests/Makefile.dep +endif \ No newline at end of file diff --git a/sys/registry/namespace/sys/Kconfig b/sys/registry/namespace/sys/Kconfig new file mode 100644 index 000000000000..beae57dfe2f7 --- /dev/null +++ b/sys/registry/namespace/sys/Kconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2023 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_REGISTRY_NAMESPACE_SYS + bool "REGISTRY_NAMESPACE_SYS" + depends on MODULE_REGISTRY_NAMESPACE + help + Namespace Sys module providing common sys configuration schemas for the RIOT Registry sys module. + +if MODULE_REGISTRY_NAMESPACE_SYS + +config MODULE_REGISTRY_NAMESPACE_SYS_RGB_LED + bool "RGB LED schema" + +endif # MODULE_REGISTRY_NAMESPACE_SYS \ No newline at end of file diff --git a/sys/registry/namespace/sys/Makefile b/sys/registry/namespace/sys/Makefile new file mode 100644 index 000000000000..1389ac65a12d --- /dev/null +++ b/sys/registry/namespace/sys/Makefile @@ -0,0 +1,9 @@ +MODULE := registry_namespace_sys + +BASE_MODULE := registry + +SRC := namespace_sys.c + +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/sys/registry/namespace/sys/Makefile.dep b/sys/registry/namespace/sys/Makefile.dep new file mode 100644 index 000000000000..b475297bcfe9 --- /dev/null +++ b/sys/registry/namespace/sys/Makefile.dep @@ -0,0 +1,4 @@ +# Enable "registry_namespace_sys" module for all schemas +ifneq (,$(filter registry_namespace_sys_%,$(USEMODULE))) + USEMODULE += registry_namespace_sys +endif diff --git a/sys/registry/namespace/sys/definitions/0000_board_led.yaml b/sys/registry/namespace/sys/definitions/0000_board_led.yaml new file mode 100644 index 000000000000..ee651b62ad6b --- /dev/null +++ b/sys/registry/namespace/sys/definitions/0000_board_led.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=../../../../../dist/tools/registry_gen/schema.json +id: 0 +name: board_led +description: Representation of a board LED. +items: + - id: 0 + name: enabled + description: State of the board LED. + type: bool diff --git a/sys/registry/namespace/sys/definitions/0001_rgb_led.yaml b/sys/registry/namespace/sys/definitions/0001_rgb_led.yaml new file mode 100644 index 000000000000..8d42ddf04002 --- /dev/null +++ b/sys/registry/namespace/sys/definitions/0001_rgb_led.yaml @@ -0,0 +1,19 @@ +# yaml-language-server: $schema=../../../../../dist/tools/registry_gen/schema.json +id: 1 +name: rgb +description: Representation of an rgb color. +items: + - id: 0 + name: red + description: Intensity of the red color of the rgb lamp. + type: uint8 + + - id: 1 + name: green + description: Intensity of the green color of the rgb lamp. + type: uint8 + + - id: 2 + name: blue + description: Intensity of the blue color of the rgb lamp. + type: uint8 diff --git a/sys/registry/namespace/sys/namespace_sys.c b/sys/registry/namespace/sys/namespace_sys.c new file mode 100644 index 000000000000..302775e6a982 --- /dev/null +++ b/sys/registry/namespace/sys/namespace_sys.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_schema RIOT Registry Schema + * @ingroup sys + * @brief RIOT Registry Schema module providing common sys configuration schemas for the RIOT Registry sys module + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" + +#include "registry/namespace/sys.h" +#include "registry/namespace/sys/rgb_led.h" +#include "registry/namespace/sys/board_led.h" + +static const registry_schema_t *_schemas[] = { +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_RGB_LED) + ®istry_sys_rgb_led, +#endif +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_BOARD_LED) + ®istry_sys_board_led, +#endif +}; + +registry_namespace_t registry_sys = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "sys", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Sys namespace", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schemas = _schemas, + .schemas_len = ARRAY_SIZE(_schemas), +}; + +REGISTRY_ADD_NAMESPACE(sys, registry_sys); diff --git a/sys/registry/namespace/sys/namespace_sys_board_led.c b/sys/registry/namespace/sys/namespace_sys_board_led.c new file mode 100644 index 000000000000..3602b4338b85 --- /dev/null +++ b/sys/registry/namespace/sys/namespace_sys_board_led.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_sys_board_led RIOT Registry Schema: BOARD_LED + * @ingroup sys + * @brief RIOT Registry BOARD_LED Schema representing the basic structure of a BOARD LED + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" +#include "registry/namespace/sys.h" + +#include "registry/namespace/sys/board_led.h" + +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_BOARD_LED) || IS_ACTIVE(DOXYGEN) + +/* Mapping */ +static void mapping(const registry_parameter_id_t parameter_id, const registry_instance_t *instance, + void **val, size_t *val_len) +{ + registry_sys_board_led_instance_t *_instance = + (registry_sys_board_led_instance_t *)instance->data; + + switch (parameter_id) { + case REGISTRY_SYS_BOARD_LED_ENABLED: + *val = &_instance->enabled; + *val_len = sizeof(_instance->enabled); + break; + } +} + +/* Schema parameters */ +const registry_parameter_t registry_sys_board_led_enabled = { + .id = REGISTRY_SYS_BOARD_LED_ENABLED, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "enabled", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "State of the board LED.", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_sys_board_led, + .type = REGISTRY_TYPE_BOOL, +}; + +/* Schema */ +registry_schema_t registry_sys_board_led = { + .id = REGISTRY_SYS_BOARD_LED, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "board_led", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Representation of a board LED.", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .namespace = ®istry_sys, + .mapping = mapping, + .groups = NULL, + .groups_len = 0, + .parameters = (const registry_parameter_t *[]) { + ®istry_sys_board_led_enabled, + }, + .parameters_len = 1, +}; + +#endif diff --git a/sys/registry/namespace/sys/namespace_sys_rgb_led.c b/sys/registry/namespace/sys/namespace_sys_rgb_led.c new file mode 100644 index 000000000000..e33bbadb6fca --- /dev/null +++ b/sys/registry/namespace/sys/namespace_sys_rgb_led.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_sys_rgb_led RIOT Registry Schema: RGB_LED + * @ingroup sys + * @brief RIOT Registry RGB_LED Schema representing the basic structure of an RGB LED + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" +#include "registry/namespace/sys.h" + +#include "registry/namespace/sys/rgb_led.h" + +#if IS_USED(MODULE_REGISTRY_NAMESPACE_SYS_RGB_LED) || IS_ACTIVE(DOXYGEN) + +/* Mapping */ +static void mapping(const registry_parameter_id_t parameter_id, const registry_instance_t *instance, + void **val, size_t *val_len) +{ + registry_sys_rgb_led_instance_t *_instance = + (registry_sys_rgb_led_instance_t *)instance->data; + + switch (parameter_id) { + case REGISTRY_SYS_RGB_LED_RED: + *val = &_instance->red; + *val_len = sizeof(_instance->red); + break; + + case REGISTRY_SYS_RGB_LED_GREEN: + *val = &_instance->green; + *val_len = sizeof(_instance->green); + break; + + case REGISTRY_SYS_RGB_LED_BLUE: + *val = &_instance->blue; + *val_len = sizeof(_instance->blue); + break; + } +} + +/* Schema parameters */ +const registry_parameter_t registry_sys_rgb_led_red = { + .id = REGISTRY_SYS_RGB_LED_RED, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "red", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_sys_rgb_led, + .type = REGISTRY_TYPE_UINT8, +}; + +const registry_parameter_t registry_sys_rgb_led_green = { + .id = REGISTRY_SYS_RGB_LED_GREEN, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "green", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_sys_rgb_led, + .type = REGISTRY_TYPE_UINT8, +}; + +const registry_parameter_t registry_sys_rgb_led_blue = { + .id = REGISTRY_SYS_RGB_LED_BLUE, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "blue", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_sys_rgb_led, + .type = REGISTRY_TYPE_UINT8, +}; + +/* Schema */ +registry_schema_t registry_sys_rgb_led = { + .id = REGISTRY_SYS_RGB_LED, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "rgb_led", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .namespace = ®istry_sys, + .mapping = mapping, + .groups = NULL, + .groups_len = 0, + .parameters = (const registry_parameter_t *[]) { + ®istry_sys_rgb_led_red, + ®istry_sys_rgb_led_green, + ®istry_sys_rgb_led_blue, + }, + .parameters_len = 3, +}; + +#endif diff --git a/sys/registry/registry.c b/sys/registry/registry.c new file mode 100644 index 000000000000..4dbe52a07209 --- /dev/null +++ b/sys/registry/registry.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry RIOT Registry + * @ingroup module_registry + * @brief RIOT Registry for runtime configuration of modules + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "clist.h" + +#include "registry.h" +#include "registry/util.h" + +/* Implementation of the module */ + +XFA_INIT_CONST(registry_namespace_t *, _registry_namespaces_xfa); + +registry_error_t registry_add_schema_instance(const registry_schema_t *schema, + const registry_instance_t *instance) +{ + assert(schema != NULL); + assert(instance != NULL); + + /* add schema to instance */ + (*((registry_instance_t *)instance)).schema = schema; + + /* get instances length to determine the id of the new instance */ + size_t count = clist_count((clist_node_t *)&schema->instances); + + /* set id of new instance to the instance count */ + *(registry_instance_id_t *)&instance->id = count; + + /* add instance to schema */ + clist_rpush((clist_node_t *)&schema->instances, (clist_node_t *)&instance->node); + + return REGISTRY_ERROR_NONE; +} + +registry_error_t registry_get(const registry_node_t *node, registry_value_t *value) +{ + assert(node != NULL); + assert(value != NULL); + + if (node->type != REGISTRY_NODE_PARAMETER) { + return -REGISTRY_ERROR_NODE_INVALID; + } + + /* call handler to get pointer to registry internal value buffer and length */ + void *intern_val = NULL; + size_t intern_val_len; + + const registry_parameter_t *parameter = node->value.parameter.parameter; + + parameter->schema->mapping(parameter->id, node->value.parameter.instance, &intern_val, + &intern_val_len); + + /* update buf pointer in registry_value_t to point to the value inside the registry and set buf_len */ + value->type = parameter->type; + value->buf = intern_val; + value->buf_len = intern_val_len; + + return REGISTRY_ERROR_NONE; +} + +registry_error_t registry_set(const registry_node_t *node, const void *buf, const size_t buf_len) +{ + assert(node != NULL); + assert(buf != NULL); + + if (node->type != REGISTRY_NODE_PARAMETER) { + return -REGISTRY_ERROR_NODE_INVALID; + } + + /* get pointer to registry internal value buffer and length */ + void *intern_val = NULL; + size_t intern_val_len; + + const registry_parameter_t *parameter = node->value.parameter.parameter; + + parameter->schema->mapping(parameter->id, node->value.parameter.instance, &intern_val, + &intern_val_len); + + if (buf_len > intern_val_len) { + return -REGISTRY_ERROR_BUF_LEN_TOO_LARGE; + } + + /* call handler to apply the new value to the correct parameter in the instance of the schema */ + memcpy(intern_val, buf, buf_len); + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t _commit_export_cb(const registry_node_t *node, const void *context) +{ + (void)context; + + const registry_instance_t *instance; + + switch (node->type) { + /* The commit function is only called for instance and below */ + case REGISTRY_NODE_NAMESPACE: + case REGISTRY_NODE_SCHEMA: + return REGISTRY_ERROR_NONE; + + case REGISTRY_NODE_INSTANCE: + instance = node->value.instance; + return instance->commit_cb(REGISTRY_COMMIT_INSTANCE, NULL, instance->context); + + case REGISTRY_NODE_GROUP: + instance = node->value.group.instance; + return instance->commit_cb(REGISTRY_COMMIT_GROUP, &node->value.group.group->id, + instance->context); + + case REGISTRY_NODE_PARAMETER: + instance = node->value.parameter.instance; + return instance->commit_cb(REGISTRY_COMMIT_PARAMETER, &node->value.parameter.parameter->id, + instance->context); + } + + return REGISTRY_ERROR_NONE; +} + +registry_error_t registry_commit(const registry_node_t *node) +{ + uint8_t tree_traversal_depth = REGISTRY_EXPORT_WITH_N_LEVELS_OF_CHILDREN(3); + + if (node != NULL) { + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + tree_traversal_depth = REGISTRY_EXPORT_WITH_N_LEVELS_OF_CHILDREN(2); + break; + + case REGISTRY_NODE_SCHEMA: + tree_traversal_depth = REGISTRY_EXPORT_WITH_N_LEVELS_OF_CHILDREN(1); + break; + + case REGISTRY_NODE_INSTANCE: + case REGISTRY_NODE_GROUP: + case REGISTRY_NODE_PARAMETER: + tree_traversal_depth = REGISTRY_EXPORT_SELF; + break; + } + } + + return registry_export(node, _commit_export_cb, tree_traversal_depth, NULL); +} + +static registry_error_t _registry_export_parameter(const registry_instance_t *instance, + const registry_parameter_t *parameter, + const registry_export_cb_t export_cb, + const void *context) +{ + assert(parameter != NULL); + + const registry_node_t export_node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = instance, + .parameter = parameter, + }, + }; + + return export_cb(&export_node, context); +} + +static registry_error_t _registry_export_group(const registry_instance_t *instance, + const registry_group_t *group, + const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(group != NULL); + + /* export the given configuration group */ + const registry_node_t export_node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = instance, + .group = group, + }, + }; + registry_error_t rc = export_cb(&export_node, context); + + /* export all children of the given configuration group if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == 1) { + return REGISTRY_ERROR_NONE; + } + else { + uint8_t new_tree_traversal_depth = tree_traversal_depth; + if (tree_traversal_depth > 1) { + new_tree_traversal_depth--; + } + + /* group */ + for (size_t i = 0; i < group->groups_len; i++) { + rc = _registry_export_group(instance, group->groups[i], export_cb, + new_tree_traversal_depth, + context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + + /* parameter */ + for (size_t i = 0; i < group->parameters_len; i++) { + rc = _registry_export_parameter(instance, group->parameters[i], export_cb, context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + } + + return rc; +} + +static registry_error_t _registry_export_instance( + const registry_instance_t *instance, + const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(instance != NULL); + + /* export the given configuration schema instance */ + const registry_node_t export_node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = instance, + }; + registry_error_t rc = export_cb(&export_node, context); + + /* export all groups or parameters of the given configuration schema instance if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == 1) { + return REGISTRY_ERROR_NONE; + } + else { + uint8_t new_tree_traversal_depth = tree_traversal_depth; + if (tree_traversal_depth > 1) { + new_tree_traversal_depth--; + } + + /* groups */ + for (size_t i = 0; i < instance->schema->groups_len; i++) { + rc = _registry_export_group(instance, instance->schema->groups[i], export_cb, + new_tree_traversal_depth, context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + + /* parameters */ + for (size_t i = 0; i < instance->schema->parameters_len; i++) { + rc = _registry_export_parameter(instance, instance->schema->parameters[i], export_cb, + context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + } + + return rc; +} + +static registry_error_t _registry_export_schema( + const registry_schema_t *schema, + const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, const void *context) +{ + assert(schema != NULL); + + /* export the given configuration schema */ + const registry_node_t export_node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = schema, + }; + registry_error_t rc = export_cb(&export_node, context); + + /* export all instances of the given configuration schema if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == 1) { + return REGISTRY_ERROR_NONE; + } + else { + uint8_t new_tree_traversal_depth = tree_traversal_depth; + if (tree_traversal_depth > 1) { + new_tree_traversal_depth--; + } + + clist_node_t *node = schema->instances.next; + + if (!node) { + return REGISTRY_ERROR_NONE; + } + + do { + node = node->next; + registry_instance_t *instance = container_of(node, registry_instance_t, node); + + if (!instance) { + return -REGISTRY_ERROR_SCHEMA_HAS_NO_INSTANCE; + } + + rc = _registry_export_instance(instance, export_cb, new_tree_traversal_depth, context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } while (node != schema->instances.next); + } + + return rc; +} + +static registry_error_t _registry_export_namespace(const registry_namespace_t *namespace, + const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, + const void *context) +{ + assert(namespace != NULL); + + /* export the given namespace */ + const registry_node_t export_node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = namespace, + }; + registry_error_t rc = export_cb(&export_node, context); + + /* export all configuration schemas of the given namespace if available and within tree_traversal_depth bounds */ + if (tree_traversal_depth == 1) { + return REGISTRY_ERROR_NONE; + } + else { + uint8_t new_tree_traversal_depth = tree_traversal_depth; + if (tree_traversal_depth > 1) { + new_tree_traversal_depth--; + } + + for (size_t i = 0; i < namespace->schemas_len; i++) { + const registry_schema_t *child = namespace->schemas[i]; + + rc = _registry_export_schema(child, export_cb, new_tree_traversal_depth, context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + } + + return rc; +} + +registry_error_t registry_export(const registry_node_t *node, const registry_export_cb_t export_cb, + const uint8_t tree_traversal_depth, const void *context) +{ + registry_error_t rc = REGISTRY_ERROR_NONE; + + if (node == NULL) { + /* export all namespaces */ + for (size_t i = 0; i < XFA_LEN(registry_namespace_t *, _registry_namespaces_xfa); i++) { + registry_namespace_t *namespace = _registry_namespaces_xfa[i]; + + rc = _registry_export_namespace(namespace, export_cb, tree_traversal_depth, context); + + if (!rc == REGISTRY_ERROR_NONE) { + return rc; + } + } + } + else { + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + rc = _registry_export_namespace(node->value.namespace, export_cb, tree_traversal_depth, + context); + break; + case REGISTRY_NODE_SCHEMA: + rc = _registry_export_schema(node->value.schema, export_cb, tree_traversal_depth, + context); + break; + case REGISTRY_NODE_INSTANCE: + rc = _registry_export_instance(node->value.instance, export_cb, tree_traversal_depth, + context); + break; + case REGISTRY_NODE_GROUP: + rc = _registry_export_group(node->value.group.instance, node->value.group.group, + export_cb, tree_traversal_depth, context); + break; + case REGISTRY_NODE_PARAMETER: + rc = _registry_export_parameter(node->value.parameter.instance, + node->value.parameter.parameter, export_cb, context); + break; + } + } + + return rc; +} diff --git a/sys/registry/storage/Kconfig b/sys/registry/storage/Kconfig new file mode 100644 index 000000000000..7ef846a45b2b --- /dev/null +++ b/sys/registry/storage/Kconfig @@ -0,0 +1,22 @@ +# Copyright (c) 2023 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_REGISTRY_STORAGE + bool "MODULE_REGISTRY_STORAGE" + depends on MODULE_REGISTRY + help + Storage module allowing to store configuration parameters to non-volatile storage. + +if MODULE_REGISTRY_STORAGE + +config MODULE_REGISTRY_STORAGE_HEAP + bool "Heap storage" + +config MODULE_REGISTRY_STORAGE_VFS + bool "VFS storage" + +endif # MODULE_REGISTRY_STORAGE \ No newline at end of file diff --git a/sys/registry/storage/Makefile b/sys/registry/storage/Makefile new file mode 100644 index 000000000000..6d464a7919c1 --- /dev/null +++ b/sys/registry/storage/Makefile @@ -0,0 +1,9 @@ +MODULE := registry_storage + +BASE_MODULE := registry + +SRC := storage.c + +SUBMODULES := 1 + +include $(RIOTBASE)/Makefile.base diff --git a/sys/registry/storage/Makefile.dep b/sys/registry/storage/Makefile.dep new file mode 100644 index 000000000000..93e079bfbf1c --- /dev/null +++ b/sys/registry/storage/Makefile.dep @@ -0,0 +1,9 @@ +# Enable "registry_storage" module for all storage modules +ifneq (,$(filter registry_storage_%,$(USEMODULE))) + USEMODULE += registry_storage +endif + +# Enable "registry_int_path" module for some storage modules +ifneq (,$(filter registry_storage_vfs,$(USEMODULE))) + USEMODULE += registry_int_path +endif \ No newline at end of file diff --git a/sys/registry/storage/storage.c b/sys/registry/storage/storage.c new file mode 100644 index 000000000000..c4a6ee3f5676 --- /dev/null +++ b/sys/registry/storage/storage.c @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_storage RIOT Registry Storage + * @ingroup sys + * @brief RIOT Registry Storage module allowing to store configuration parameters to non-volatile storage + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "clist.h" + +#include "registry/storage.h" +#include "registry/error.h" + +static const registry_storage_instance_t **_storage_instances; + +/* registry_load */ +static registry_error_t _registry_load_cb(const registry_node_t *node, const void *buf, + const size_t buf_len) +{ + assert(node->type == REGISTRY_NODE_PARAMETER); + assert(node->value.parameter.parameter != NULL); + assert(node->value.parameter.instance != NULL); + + return registry_set(node, buf, buf_len); +} + +registry_error_t registry_storage_load(const registry_storage_instance_t *storage_instance) +{ + return storage_instance->storage->load(storage_instance, _registry_load_cb); +} + +/* registry_save */ +static registry_error_t _registry_save_export_cb(const registry_node_t *node, const void *context) +{ + const registry_storage_instance_t *storage_instance = context; + + /* the registry also exports just the namespace or just a schema, but the storage is only interested in configuration parameter values */ + if (node->type != REGISTRY_NODE_PARAMETER) { + return REGISTRY_ERROR_NONE; + } + + /* get value of configuration parameter */ + registry_value_t value; + registry_get(node, &value); + + /* save parameter value via the save function of the provided storage instance */ + return storage_instance->storage->save(storage_instance, node, &value); +} + +registry_error_t registry_storage_save(const registry_storage_instance_t *storage_instance, + const registry_node_t *node) +{ + assert(storage_instance != NULL); + + if (storage_instance->storage->save_start) { + storage_instance->storage->save_start(storage_instance); + } + + registry_error_t res = registry_export(node, _registry_save_export_cb, REGISTRY_EXPORT_ALL, + storage_instance); + + if (storage_instance->storage->save_end) { + storage_instance->storage->save_end(storage_instance); + } + + return res; +} + +registry_error_t registry_storage_set_instances( + const registry_storage_instance_t **storage_instances) +{ + assert(storage_instances != NULL); + + _storage_instances = storage_instances; + + return REGISTRY_ERROR_NONE; +} + +registry_error_t registry_storage_get_instances( + const registry_storage_instance_t ***storage_instances) +{ + assert(storage_instances != NULL); + + *storage_instances = _storage_instances; + + return REGISTRY_ERROR_NONE; +} diff --git a/sys/registry/storage/storage_heap.c b/sys/registry/storage/storage_heap.c new file mode 100644 index 000000000000..71d175348d5f --- /dev/null +++ b/sys/registry/storage/storage_heap.c @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_storage_heap RIOT Registry Storage: Heap + * @ingroup sys + * @brief RIOT Registry Storage Heap, only uses the heap for testing. + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" + +#include "registry/storage.h" + +#ifndef REGISTRY_STORAGE_HEAP_CAPACITY + #define REGISTRY_STORAGE_HEAP_CAPACITY 100 +#endif + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb); +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value); + +typedef struct { + registry_node_t node; + void *buf; + size_t buf_len; +} heap_storage_t; + +/* This is the "storage device" containing all the data */ +static heap_storage_t heap_storage[REGISTRY_STORAGE_HEAP_CAPACITY]; +static size_t heap_storage_len = 0; + +/* Storage interface descriptor to be registered in the RIOT Registry */ +registry_storage_t registry_storage_heap = { + .load = load, + .save = save, +}; + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb) +{ + (void)storage; + + for (size_t i = 0; i < heap_storage_len; i++) { + load_cb(&heap_storage[i].node, heap_storage[i].buf, heap_storage[i].buf_len); + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value) +{ + assert(node->type == REGISTRY_NODE_PARAMETER); + assert(node->value.parameter.parameter != NULL); + assert(node->value.parameter.instance != NULL); + + (void)storage; + + /* Search value in storage */ + for (size_t i = 0; i < heap_storage_len; i++) { + if (heap_storage[i].node.value.parameter.instance == node->value.parameter.instance && + heap_storage[i].node.value.parameter.parameter == node->value.parameter.parameter) { + memcpy(heap_storage[i].buf, value->buf, value->buf_len); + return REGISTRY_ERROR_NONE; + } + } + + /* Value not found in storage => Append it at the end */ + heap_storage[heap_storage_len] = (heap_storage_t) { + .node = *node, + .buf = malloc(value->buf_len), + .buf_len = value->buf_len, + }; + memcpy(heap_storage[heap_storage_len].buf, value->buf, value->buf_len); + + heap_storage_len++; + return REGISTRY_ERROR_NONE; +} diff --git a/sys/registry/storage/storage_vfs.c b/sys/registry/storage/storage_vfs.c new file mode 100644 index 000000000000..bc1568fa78bf --- /dev/null +++ b/sys/registry/storage/storage_vfs.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_int_path_storage_vfs RIOT Registry Path Storage: VFS + * @ingroup sys + * @brief RIOT Registry Path Storage VFS, allows using the RIOT VFS module as a RIOT Registry data storage. + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "vfs.h" +#include "ps.h" +#include "registry/int_path.h" + +#include "registry/storage.h" + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb); +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value); + +registry_storage_t registry_storage_vfs = { + .load = load, + .save = save, +}; + +static void _string_path_append_item(char *dest, uint32_t number) +{ + int size = snprintf(NULL, 0, "/%d", number); + + /* Allocate size + string termination */ + char buf[size + 1]; + + sprintf(buf, "/%d", number); + + strcat(dest, buf); +} + +static int _format(vfs_mount_t *mount) +{ + DEBUG("formatting %s....\t", mount->mount_point); + if (vfs_format(mount) < 0) { + DEBUG("[Failed]\n"); + return 1; + } + else { + DEBUG("[OK]\n"); + } + + return 0; +} + +static int _mount(vfs_mount_t *mount) +{ + int res = vfs_mount(mount); + + if (res < 0) { + DEBUG("Error while mounting %s ... (%d) ... try format\n", mount->mount_point, res); + + /* format to fix mount */ + _format(mount); + + /* try to mount again */ + res = vfs_mount(mount); + if (res != 0) { + return -1; + } + } + + return 0; +} + +static int _umount(vfs_mount_t *mount) +{ + int res = vfs_umount(mount, false); + + if (res < 0) { + DEBUG("Error while unmounting %s...\n", mount->mount_point); + return 1; + } + + return 0; +} + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb) +{ + vfs_mount_t *mount = storage->data; + + /* mount */ + _mount(mount); + + /* create dir path */ + char string_path[REGISTRY_INT_PATH_STRING_MAX_LEN]; + + sprintf(string_path, "%s", mount->mount_point); + + /* read dirs */ + vfs_DIR dirp; + + if (vfs_opendir(&dirp, string_path) != 0) { + DEBUG("[registry storage_vfs] load: Can not open dir\n"); + } + else { + struct stat _stat; + vfs_dirent_t dir_entry; + + size_t i = 0; + int last_dir_entry_positions[REGISTRY_INT_PATH_MAX_LEN] = { -1 }; + size_t last_dir_string_path_lens[REGISTRY_INT_PATH_MAX_LEN] = { 0 }; + int res = 0; + bool exit_folder_iteration = false; + + while (exit_folder_iteration == false) { + int dir_entry_position = -1; + do { + res = vfs_readdir(&dirp, &dir_entry); + dir_entry_position++; + + if (dir_entry_position > last_dir_entry_positions[i]) { + last_dir_entry_positions[i] = dir_entry_position; + for (size_t j = i + 1; j < REGISTRY_INT_PATH_MAX_LEN; j++) { + last_dir_entry_positions[j] = -1; + } + + if (res == 1) { + if (strcmp(dir_entry.d_name, + ".") != 0 && strcmp(dir_entry.d_name, "..") != 0) { + /* save string_path length to restore it later */ + last_dir_string_path_lens[i] = strlen(string_path); + + /* add new directory to string_path */ + strcat(string_path, "/"); + strcat(string_path, dir_entry.d_name); + + vfs_stat(string_path, &_stat); + + if (S_ISDIR(_stat.st_mode)) { + /* close old directory */ + if (vfs_closedir(&dirp) != 0) { + DEBUG( + "[registry storage_vfs] load: Can not close dir\n"); + } + + /* open new directory */ + if (vfs_opendir(&dirp, string_path) != 0) { + DEBUG("[registry storage_vfs] load: Can not open dir\n"); + } + + /* move on to next sub path */ + i++; + + /* reset position within current dir, because the dir changed */ + dir_entry_position = -1; + } + else { + /* open file */ + int fd = vfs_open(string_path, O_RDONLY, 0); + + if (fd <= 0) { + DEBUG( + "[registry storage_vfs] load: Can not open file: %d\n", + fd); + } + + /* convert string path of integers to native registry int path format (remove mount point and first '/' character) */ + char *ptr = (char *)string_path + mount->mount_point_len + 1; + registry_namespace_id_t namespace_id = strtol(ptr, &ptr, 10); + ptr++; + registry_schema_id_t schema_id = strtol(ptr, &ptr, 10); + ptr++; + registry_instance_id_t instance_id = strtol(ptr, &ptr, 10); + ptr++; + registry_parameter_id_t parameter_id = strtol(ptr, &ptr, 10); + + const registry_int_path_t int_path = { + .type = REGISTRY_INT_PATH_TYPE_PARAMETER, + .value.parameter_path = { + .namespace_id = namespace_id, + .schema_id = schema_id, + .instance_id = instance_id, + .parameter_id = parameter_id, + } + }; + + /* get pointer to registry internal configuration parameter */ + registry_node_t node; + registry_node_from_int_path(&int_path, &node); + + /* get value from registry to know its size (buf_len) */ + registry_value_t value; + registry_get(&node, &value); + + /* read value from file */ + uint8_t new_value_buf[value.buf_len]; + if (vfs_read(fd, new_value_buf, value.buf_len) < 0) { + DEBUG( + "[registry storage_vfs] load: Can not read from file\n"); + } + else { + /* call callback with value and path */ + load_cb(&node, new_value_buf, value.buf_len); + } + + /* close file */ + if (vfs_close(fd) != 0) { + DEBUG( + "[registry storage_vfs] load: Can not close file: %d\n", + fd); + } + + /* restore old string_path */ + string_path[last_dir_string_path_lens[i]] = '\0'; + } + } + } + else { + /* if i == 0 it can't be further decreased => exit */ + if (i == 0) { + exit_folder_iteration = true; + } + else { + /* move up one path back to the parent */ + i--; + + /* restore old string_path */ + string_path[last_dir_string_path_lens[i]] = '\0'; + + /* close old directory */ + if (vfs_closedir(&dirp) != 0) { + DEBUG("[registry storage_vfs] load: Can not close dir\n"); + } + + /* open new directory */ + if (vfs_opendir(&dirp, string_path) != 0) { + DEBUG("[registry storage_vfs] load: Can not open dir\n"); + } + } + } + } + } while (res == 1); + } + + if (vfs_closedir(&dirp) != 0) { + DEBUG("[registry storage_vfs] load: Can not close dir\n"); + } + } + + /* umount */ + _umount(mount); + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value) +{ + assert(node->type == REGISTRY_NODE_PARAMETER); + assert(node->value.parameter.parameter != NULL); + assert(node->value.parameter.instance != NULL); + + vfs_mount_t *mount = storage->data; + + /* mount */ + _mount(mount); + + /* create dir path */ + registry_int_path_t path; + registry_error_t res = registry_node_to_int_path(node, &path); + + char string_path[REGISTRY_INT_PATH_STRING_MAX_LEN]; + + sprintf(string_path, "%s", mount->mount_point); + + _string_path_append_item(string_path, path.value.parameter_path.namespace_id); + res = vfs_mkdir(string_path, 0); + + if (res < 0 && res != -EEXIST) { + DEBUG("[registry storage_vfs] save: Can not make dir: %s\n", string_path); + } + + _string_path_append_item(string_path, path.value.parameter_path.schema_id); + res = vfs_mkdir(string_path, 0); + + if (res < 0 && res != -EEXIST) { + DEBUG("[registry storage_vfs] save: Can not make dir: %s\n", string_path); + } + + _string_path_append_item(string_path, path.value.parameter_path.instance_id); + res = vfs_mkdir(string_path, 0); + + if (res < 0 && res != -EEXIST) { + DEBUG("[registry storage_vfs] save: Can not make dir: %s\n", string_path); + } + + /* open file */ + _string_path_append_item(string_path, path.value.parameter_path.parameter_id); + + int fd = vfs_open(string_path, O_CREAT | O_RDWR, 0); + + if (fd <= 0) { + DEBUG("[registry storage_vfs] save: Can not open file: %d\n", fd); + } + + /* write to file */ + if (vfs_write(fd, value->buf, value->buf_len) < 0) { + DEBUG("[registry storage_vfs] save: Can not write to file: %d\n", fd); + } + + if (vfs_close(fd) != 0) { + DEBUG("[registry storage_vfs] save: Can not close file: %d\n", fd); + } + + /* umount */ + _umount(mount); + + return REGISTRY_ERROR_NONE; +} diff --git a/sys/registry/string_path.c b/sys/registry/string_path.c new file mode 100644 index 000000000000..3500d464a22f --- /dev/null +++ b/sys/registry/string_path.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_string_path RIOT Registry String Path + * @ingroup sys + * @brief RIOT Registry String Path module + * @{ + * + * This module provides functions to convert between @ref registry_node_t and + * a string path representation of it. + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "clist.h" +#include "registry.h" +#include "registry/util.h" +#include "registry/error.h" +#include "registry/find.h" + +#include "registry/string_path.h" + + +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + +typedef struct { + const char **path; + size_t path_len; + size_t position; +} _registry_string_comparator_context_t; + +static int _internal_registry_to_group_string_path(const registry_group_t *current_group, + const registry_group_t *group, char *path) +{ + int size = snprintf(NULL, 0, "/%s", current_group->name); + + if (path != NULL) { + snprintf(path, size + 1, "/%s", current_group->name); + } + + if (group == current_group) { + return size; + } + + for (size_t i = 0; i < current_group->groups_len; i++) { + int res = _internal_registry_to_group_string_path(current_group->groups[i], group, + path != NULL ? path + size : NULL); + if (res >= 0) { + return size + res; + } + } + + return -EINVAL; +} + +static int _internal_registry_to_parameter_string_path(const registry_group_t *current_group, + const registry_parameter_t *parameter, + char *path) +{ + int size = snprintf(NULL, 0, "/%s", current_group->name); + + if (path != NULL) { + snprintf(path, size + 1, "/%s", current_group->name); + } + + /* check if the parameter is a child of this group */ + for (size_t i = 0; i < current_group->parameters_len; i++) { + if (current_group->parameters[i] == parameter) { + int sub_size = snprintf(NULL, 0, "/%s", current_group->parameters[i]->name); + + if (path != NULL) { + snprintf(path + size, sub_size + 1, "/%s", current_group->parameters[i]->name); + } + + return size + sub_size; + } + } + + /* check if the parameter is the child of a subgroup */ + for (size_t i = 0; i < current_group->groups_len; i++) { + int res = _internal_registry_to_parameter_string_path(current_group->groups[i], parameter, + path != NULL ? path + size : NULL); + if (res >= 0) { + return size + res; + } + } + + return -EINVAL; +} + +int registry_node_to_string_path(const registry_node_t *node, char *path) +{ + assert(node != NULL); + + int size = 0; + + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + size = snprintf(NULL, 0, "/%s", node->value.namespace->name); + + if (path != NULL) { + return snprintf(path, size + 1, "/%s", node->value.namespace->name); + } + break; + + case REGISTRY_NODE_SCHEMA: + size = snprintf(NULL, 0, "/%s/%s", node->value.schema->namespace->name, + node->value.schema->name); + + if (path != NULL) { + return snprintf(path, size + 1, "/%s/%s", node->value.schema->namespace->name, + node->value.schema->name); + } + break; + + case REGISTRY_NODE_INSTANCE: + size = snprintf(NULL, 0, "/%s/%s/%s", node->value.instance->schema->namespace->name, + node->value.instance->schema->name, node->value.instance->name); + + if (path != NULL) { + return snprintf(path, size + 1, "/%s/%s/%s", + node->value.instance->schema->namespace->name, + node->value.instance->schema->name, node->value.instance->name); + } + break; + + case REGISTRY_NODE_GROUP: + size = snprintf(NULL, 0, "/%s/%s/%s", node->value.group.instance->schema->namespace->name, + node->value.group.instance->schema->name, node->value.group.instance->name); + + if (path != NULL) { + snprintf(path, size + 1, "/%s/%s/%s", + node->value.group.instance->schema->namespace->name, + node->value.group.instance->schema->name, node->value.group.instance->name); + } + + for (size_t i = 0; i < node->value.group.instance->schema->groups_len; i++) { + int res = + _internal_registry_to_group_string_path( + node->value.group.instance->schema->groups[i], node->value.group.group, + path != NULL ? path + size : NULL); + if (res >= 0) { + return size += res; + } + } + + return -EINVAL; + break; + + case REGISTRY_NODE_PARAMETER: + size = snprintf(NULL, 0, "/%s/%s/%s", + node->value.parameter.instance->schema->namespace->name, + node->value.parameter.instance->schema->name, + node->value.parameter.instance->name); + + if (path != NULL) { + snprintf(path, size + 1, "/%s/%s/%s", + node->value.parameter.instance->schema->namespace->name, + node->value.parameter.instance->schema->name, + node->value.parameter.instance->name); + } + + /* check if the parameter is a child of this schema */ + for (size_t i = 0; i < node->value.parameter.instance->schema->parameters_len; i++) { + if (node->value.parameter.instance->schema->parameters[i] == + node->value.parameter.parameter) { + int sub_size = snprintf(NULL, 0, "/%s", + node->value.parameter.instance->schema->parameters[i]->name); + + if (path != NULL) { + snprintf(path + size, sub_size + 1, "/%s", + node->value.parameter.instance->schema->parameters[i]->name); + } + + return size + sub_size; + } + } + + /* check if the parameter is the child of a group */ + for (size_t i = 0; i < node->value.parameter.instance->schema->groups_len; i++) { + int res = + _internal_registry_to_parameter_string_path( + node->value.parameter.instance->schema->groups[i], + node->value.parameter.parameter, + path != + NULL ? path + size : NULL); + if (res >= 0) { + return size += res; + } + } + + return -EINVAL; + break; + } + + return size; +} + +static registry_find_result_type _compare_node_by_string(const registry_node_t *node, + const void *context) +{ + _registry_string_comparator_context_t *data = (void *)context; + + const char *name = NULL; + + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + name = node->value.namespace->name; + break; + + case REGISTRY_NODE_SCHEMA: + name = node->value.schema->name; + break; + + case REGISTRY_NODE_INSTANCE: + name = node->value.instance->name; + break; + + case REGISTRY_NODE_GROUP: + name = node->value.group.group->name; + break; + + + case REGISTRY_NODE_PARAMETER: + name = node->value.parameter.parameter->name; + break; + } + + if (strncmp(data->path[data->position], name, strlen(name)) == 0) { + if (data->path_len == data->position + 1) { + return REGISTRY_FIND_EXACT_MATCH; + } + + data->position++; + return REGISTRY_FIND_PARTIAL_MATCH; + } + + return REGISTRY_FIND_NO_MATCH; +} + +registry_error_t registry_node_from_string_path(const char **path, const size_t path_len, + registry_node_t *node) +{ + _registry_string_comparator_context_t context = { + .path = path, + .path_len = path_len, + .position = 0, + }; + + return registry_find(_compare_node_by_string, &context, node); +} + +#endif diff --git a/sys/registry/util.c b/sys/registry/util.c new file mode 100644 index 000000000000..d313878405b7 --- /dev/null +++ b/sys/registry/util.c @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_util RIOT Registry utilities + * @ingroup sys + * @brief RIOT Registry Util module providing utility functions + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "base64.h" +#include "kernel_defines.h" + +#include "registry.h" +#include "registry/util.h" +#include "fmt.h" + +int registry_util_convert_str_to_value(const char *src, void *dest, const size_t dest_len, + const registry_type_t dest_type) +{ + assert(src != NULL); + assert(dest != NULL); + + char *eptr = '\0'; + + if (!src) { + return -EINVAL; + } + + switch (dest_type) { + case REGISTRY_TYPE_NONE: { + return -EINVAL; + } + + case REGISTRY_TYPE_OPAQUE: { + size_t base64_decode_len; + if (base64_decode(src, strlen(src), dest, &base64_decode_len) != + BASE64_SUCCESS && base64_decode_len <= dest_len) { + return -EINVAL; + } + break; + } + + case REGISTRY_TYPE_STRING: { + if (strlen(src) + 1 > dest_len) { + return -EINVAL; + } + strcpy((char *)dest, src); + break; + } + + case REGISTRY_TYPE_BOOL: { + *(bool *)dest = strtol(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_UINT8: { + *(uint8_t *)dest = strtoul(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_UINT16: { + *(uint16_t *)dest = strtoul(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_UINT32: { + *(uint32_t *)dest = strtoul(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_UINT64: { + *(uint64_t *)dest = strtoull(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_INT8: { + *(int8_t *)dest = strtol(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_INT16: { + *(int16_t *)dest = strtol(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_INT32: { + *(int32_t *)dest = strtol(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_INT64: { + *(int64_t *)dest = strtoll(src, &eptr, 0); + break; + } + + case REGISTRY_TYPE_FLOAT32: { + *(float *)dest = strtof(src, &eptr); + break; + } + + case REGISTRY_TYPE_FLOAT64: { + *(double *)dest = strtod(src, &eptr); + break; + } + } + + if (*eptr != '\0') { + return -EINVAL; + } + + return 0; +} + +int registry_util_convert_value_to_str(const registry_value_t *src, char *dest, + const size_t dest_len) +{ + assert(src != NULL); + + size_t str_len; + + switch (src->type) { + case REGISTRY_TYPE_NONE: { + return -EINVAL; + } + + case REGISTRY_TYPE_OPAQUE: { + if (base64_encode(src->buf, src->buf_len, dest, &str_len) != BASE64_SUCCESS + || str_len > dest_len - 1) { + /* If dest is NULL, the length is returned */ + if (dest != NULL) { + return -EINVAL; + } + } + else { + dest[str_len] = '\0'; + } + break; + } + + case REGISTRY_TYPE_STRING: { + char *str_val = (char *)src->buf; + + str_len = strlen(str_val); + + if (str_len > dest_len - 1) { + /* If dest is NULL, the length is returned */ + if (dest != NULL) { + return -EINVAL; + } + } + else { + strcpy(dest, str_val); + } + break; + } + + case REGISTRY_TYPE_BOOL: { + str_len = snprintf(dest, dest_len, " %" PRId8, *(bool *)src->buf); + break; + } + + case REGISTRY_TYPE_UINT8: { + str_len = snprintf(dest, dest_len, " %" PRIu8, *(uint8_t *)src->buf); + break; + } + + case REGISTRY_TYPE_UINT16: { + str_len = snprintf(dest, dest_len, " %" PRIu16, *(uint16_t *)src->buf); + break; + } + + case REGISTRY_TYPE_UINT32: { + str_len = snprintf(dest, dest_len, " %" PRIu32, *(uint32_t *)src->buf); + break; + } + + case REGISTRY_TYPE_UINT64: { + str_len = snprintf(dest, dest_len, " %" PRIu64, *(uint64_t *)src->buf); + break; + } + + case REGISTRY_TYPE_INT8: { + str_len = snprintf(dest, dest_len, " %" PRId8, *(int8_t *)src->buf); + break; + } + + case REGISTRY_TYPE_INT16: { + str_len = snprintf(dest, dest_len, " %" PRId16, *(int16_t *)src->buf); + break; + } + + case REGISTRY_TYPE_INT32: { + str_len = snprintf(dest, dest_len, " %" PRId32, *(int32_t *)src->buf); + break; + } + + case REGISTRY_TYPE_INT64: { + str_len = snprintf(dest, dest_len, " %" PRId64, *(int64_t *)src->buf); + break; + } + + case REGISTRY_TYPE_FLOAT32: { + str_len = sprintf(dest, " %f", *(float *)src->buf); + if (str_len > dest_len - 1) { + /* If dest is NULL, the length is returned */ + if (dest != NULL) { + return -EINVAL; + } + } + break; + } + + case REGISTRY_TYPE_FLOAT64: { + str_len = sprintf(dest, " %f", *(double *)src->buf); + if (str_len > dest_len - 1) { + /* If dest is NULL, the length is returned */ + if (dest != NULL) { + return -EINVAL; + } + } + break; + } + } + + return str_len; +} diff --git a/sys/shell/Makefile.dep b/sys/shell/Makefile.dep index 7f9ec9d21b9b..91936f54a8b6 100644 --- a/sys/shell/Makefile.dep +++ b/sys/shell/Makefile.dep @@ -111,6 +111,10 @@ ifneq (,$(filter shell_cmds_default,$(USEMODULE))) ifneq (,$(filter openwsn,$(USEPKG))) USEMODULE += shell_cmd_openwsn endif + ifneq (,$(filter registry,$(USEMODULE))) + USEMODULE += shell_cmd_registry + USEMODULE += registry_util + endif ifneq (,$(filter rtt_rtc periph_rtc,$(USEMODULE))) USEMODULE += shell_cmd_rtc endif @@ -258,6 +262,10 @@ endif ifneq (,$(filter shell_cmd_random_cmd,$(USEMODULE))) USEMODULE += random endif +ifneq (,$(filter shell_cmd_registry,$(USEMODULE))) + USEMODULE += registry + USEMODULE += registry_int_path +endif ifneq (,$(filter shell_cmd_rtc,$(USEMODULE))) FEATURES_REQUIRED_ANY += periph_rtc|periph_rtt # beware: this is a bit more tricky than it looks. Before the diff --git a/sys/shell/cmds/Kconfig b/sys/shell/cmds/Kconfig index 1a663693f602..4ebe4d31ba6f 100644 --- a/sys/shell/cmds/Kconfig +++ b/sys/shell/cmds/Kconfig @@ -255,6 +255,13 @@ config MODULE_SHELL_CMD_RANDOM depends on MODULE_SHELL_CMDS depends on MODULE_RANDOM +config MODULE_SHELL_CMD_REGISTRY + bool "Command to get, set, commit, export, save and load registry values" + default y if MODULE_SHELL_CMDS_DEFAULT + depends on MODULE_SHELL_CMDS + depends on MODULE_REGISTRY + depends on MODULE_REGISTRY_INT_PATH + config MODULE_SHELL_CMD_RTC bool "Command to control the peripheral real time clock" default y if MODULE_SHELL_CMDS_DEFAULT && MODULE_PERIPH_RTC diff --git a/sys/shell/cmds/registry.c b/sys/shell/cmds/registry.c new file mode 100644 index 000000000000..3ad0812f9acc --- /dev/null +++ b/sys/shell/cmds/registry.c @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + * + */ + +/** + * @ingroup sys_shell_commands + * @{ + * + * @file + * @brief Registry shell commands + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "registry.h" +#include "registry/util.h" +#include "registry/int_path.h" +#include "registry/storage.h" +#include "shell.h" + +/** + * @brief Separator character to define hierarchy in configurations names. + */ +#define REGISTRY_CLI_PATH_SEPARATOR '/' + +static int _parse_string_path(const char *string_path, + registry_int_path_t *registry_path) +{ + char *ptr = (char *)string_path; + + if (*ptr == '\0') { + return -EINVAL; + } + + /* get namespace_id */ + registry_namespace_id_t namespace_id = strtol(ptr, &ptr, 10); + + if (*ptr == '\0') { + registry_path->type = REGISTRY_INT_PATH_TYPE_NAMESPACE; + registry_path->value.namespace_path = (registry_namespace_int_path_t) { + .namespace_id = namespace_id, + }; + return 0; + } + + /* get schema_id */ + ptr++; /* skip the '/' character */ + registry_schema_id_t schema_id = strtol(ptr, &ptr, 10); + + if (*ptr == '\0') { + registry_path->type = REGISTRY_INT_PATH_TYPE_SCHEMA; + registry_path->value.schema_path = (registry_schema_int_path_t) { + .namespace_id = namespace_id, + .schema_id = schema_id, + }; + return 0; + } + + /* get instance_id */ + ptr++; /* skip the '/' character */ + registry_instance_id_t instance_id = strtol(ptr, &ptr, 10); + + if (*ptr == '\0') { + registry_path->type = REGISTRY_INT_PATH_TYPE_INSTANCE; + registry_path->value.instance_path = (registry_instance_int_path_t) { + .namespace_id = namespace_id, + .schema_id = schema_id, + .instance_id = instance_id, + }; + return 0; + } + + /* get group_or_parameter_id */ + ptr++; /* skip the '/' character */ + registry_group_or_parameter_id_t group_or_parameter_id = strtol(ptr, &ptr, 10); + + if (*ptr == '\0') { + registry_path->type = REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER; + registry_path->value.group_or_parameter_path = (registry_group_or_parameter_int_path_t) { + .namespace_id = namespace_id, + .schema_id = schema_id, + .instance_id = instance_id, + .group_or_parameter_id = group_or_parameter_id, + }; + return 0; + } + + return 0; +} + +static registry_error_t _export_cb(const registry_node_t *node, const void *context) +{ + (void)context; + + /* calculate the indentation based on the the exported data type */ + /* fallthrough switch is intentional */ + /* the more nesting we have, the more indentation we need. => highest nesting level first */ + size_t indentation = 0; + + switch (node->type) { + case REGISTRY_NODE_PARAMETER: + case REGISTRY_NODE_GROUP: + indentation++; + __attribute__ ((fallthrough)); + + case REGISTRY_NODE_INSTANCE: + indentation++; + __attribute__ ((fallthrough)); + + case REGISTRY_NODE_SCHEMA: + indentation++; + __attribute__ ((fallthrough)); + + case REGISTRY_NODE_NAMESPACE: + indentation++; + } + + printf("%*c\b", ((indentation - 1) * 2) + 1, ' '); + + /* print the path element, that is currently being exported */ + switch (node->type) { + case REGISTRY_NODE_NAMESPACE: + printf("%d %s\n", node->value.namespace->id, node->value.namespace->name); + break; + + case REGISTRY_NODE_SCHEMA: + printf("%d %s\n", node->value.schema->id, node->value.schema->name); + break; + + case REGISTRY_NODE_INSTANCE: + printf("%d %s\n", node->value.instance->id, node->value.instance->name); + break; + + case REGISTRY_NODE_GROUP: + printf("%d %s (group)\n", node->value.group.group->id, node->value.group.group->name); + break; + + case REGISTRY_NODE_PARAMETER: + printf("%d %s\n", node->value.parameter.parameter->id, node->value.parameter.parameter->name); + break; + } + + return 0; +} + +static int _registry(int argc, char **argv) +{ + registry_int_path_t path; + registry_node_t node; + registry_value_t value; + + if (argc == 1) { + /* show help for main commands */ + goto help_error; + } + + if (strcmp(argv[1], "get") == 0) { + if (_parse_string_path(argv[2], &path) < 0) { + printf("usage: %s %s \n", argv[0], argv[1]); + return 1; + } + + if (path.type != REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER) { + return -EINVAL; + } + + /* get instance and parameter of the path */ + int res = registry_node_from_int_path(&path, &node); + + if (node.type != REGISTRY_NODE_PARAMETER) { + return -EINVAL; + } + + if (res == 0) { + res = registry_get(&node, &value); + + if (res == 0) { + /* get the length of the value as a string */ + size_t str_len = registry_util_convert_value_to_str(&value, NULL, 0); + + /* convert the value to a string */ + char str[str_len + 1]; + registry_util_convert_value_to_str(&value, str, str_len + 1); + + /* print the string */ + printf("%s\n", str); + return 0; + } + } + + printf("error: %d\n", res); + return 1; + } + else if (strcmp(argv[1], "set") == 0) { + if (_parse_string_path(argv[2], &path) < 0) { + printf("usage: %s %s \n", argv[0], argv[1]); + return 1; + } + + if (path.type != REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER) { + return -EINVAL; + } + + /* get instance and parameter of the path */ + int res = registry_node_from_int_path(&path, &node); + + if (node.type != REGISTRY_NODE_PARAMETER) { + return -EINVAL; + } + + if (res == 0) { + /* get value from the registry, to know its correct type and size */ + res = registry_get(&node, &value); + + if (res == 0) { + /* convert the string into the correct value type */ + uint8_t new_value_buf[value.buf_len]; + registry_util_convert_str_to_value(argv[3], new_value_buf, value.buf_len, + value.type); + + /* let the registry_value_t object point to the buffer of the new value */ + value.buf = new_value_buf; + + /* set the new value in the registry */ + registry_set(&node, value.buf, value.buf_len); + return 0; + } + } + + printf("error: %d\n", res); + return 1; + } + else if (strcmp(argv[1], "commit") == 0) { + if (_parse_string_path(argv[2], &path) < 0) { + printf("usage: %s %s \n", argv[0], argv[1]); + return 1; + } + + int res = registry_node_from_int_path(&path, &node); + + if (res == 0) { + res = registry_commit(&node); + } + + if (res != 0) { + printf("error: %d\n", res); + return 1; + } + + return 0; + } + else if (strcmp(argv[1], "export") == 0) { + /* if the path is invalid, it can also just be non existent, so other arguments like -r need to be checked */ + bool invalid_path = false; + if (_parse_string_path(argv[2], &path) < 0) { + invalid_path = true; + } + if (invalid_path && strcmp(argv[2], "-r") != 0) { + printf("usage: %s %s [-r ]\n", argv[0], argv[1]); + return 1; + } + + /* the argv index of -r varies depending on if a path was specified or not */ + int tree_traversal_level = 0; + if (invalid_path && argc > 3 && strcmp(argv[2], "-r") == 0) { + tree_traversal_level = atoi(argv[3]); + } + else if (argc > 4 && strcmp(argv[3], "-r") == 0) { + tree_traversal_level = atoi(argv[4]); + } + + int res = registry_node_from_int_path(&path, &node); + + if (res == 0) { + res = registry_export(&node, _export_cb, tree_traversal_level, NULL); + } + + if (res != 0) { + printf("error: %d\n", res); + return 1; + } + + return 0; + } +#if IS_USED(MODULE_REGISTRY_STORAGE) || IS_ACTIVE(DOXYGEN) + else if (strcmp(argv[1], "load") == 0) { + // TODO implement storage selector + const registry_storage_instance_t **storage_instances; + registry_storage_get_instances(&storage_instances); + + if (argc > 2) { + printf("usage: %s %s\n", argv[0], argv[1]); + return 1; + } + + registry_storage_load(storage_instances[0]); + + return 0; + } + else if (strcmp(argv[1], "save") == 0) { + int res = 0; + // TODO implement storage selector + const registry_storage_instance_t **storage_instances; + registry_storage_get_instances(&storage_instances); + + if (argc > 2) { + if (_parse_string_path(argv[2], &path) < 0) { + printf("usage: %s %s [path]\n", argv[0], argv[1]); + return 1; + } + + res = registry_node_from_int_path(&path, &node); + if (res == 0) { + res = registry_storage_save(storage_instances[0], &node); + } + } + else { + // save everything + res = registry_storage_save(storage_instances[0], NULL); + } + + if (res != 0) { + printf("error: %d\n", res); + return 1; + } + + return 0; + } +#endif + +help_error: + printf("usage: %s {get|set|commit|export|load|save}\n", argv[0]); + + return 1; +} + +SHELL_COMMAND(registry, "Registry cli", _registry); diff --git a/tests/unittests/tests-registry/Makefile b/tests/unittests/tests-registry/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry/Makefile.include b/tests/unittests/tests-registry/Makefile.include new file mode 100644 index 000000000000..5c37ee79b853 --- /dev/null +++ b/tests/unittests/tests-registry/Makefile.include @@ -0,0 +1,2 @@ +USEMODULE += registry +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry/namespace/Makefile b/tests/unittests/tests-registry/namespace/Makefile new file mode 100644 index 000000000000..dc31b2f88174 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/Makefile @@ -0,0 +1,3 @@ +MODULE = tests-registry-namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry/namespace/definitions/0001_full.yaml b/tests/unittests/tests-registry/namespace/definitions/0001_full.yaml new file mode 100644 index 000000000000..63132f58bff7 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/definitions/0001_full.yaml @@ -0,0 +1,71 @@ +# yaml-language-server: $schema=../../../../../dist/tools/registry_gen/schema.json +id: 1 +name: rgb +description: Representation of an rgb color. +items: + - id: 0 + name: opaque + description: Opaque data type property. + type: opaque + 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-registry/namespace/namespace_tests.c b/tests/unittests/tests-registry/namespace/namespace_tests.c new file mode 100644 index 000000000000..dfffb170c54d --- /dev/null +++ b/tests/unittests/tests-registry/namespace/namespace_tests.c @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests RIOT Registry Tests Namespace + * @ingroup tests + * @brief RIOT Registry Namespace Tests module providing common tests configuration schemas for the RIOT Registry sys module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" + +#include "tests.h" +#include "tests/full.h" +#include "tests/nested.h" + +static const registry_schema_t *_schemas[] = { + ®istry_tests_full, + ®istry_tests_nested, +}; + +registry_namespace_t registry_tests = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "tests", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "Tests namespace", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schemas = _schemas, + .schemas_len = ARRAY_SIZE(_schemas), +}; + +REGISTRY_ADD_NAMESPACE(tests, registry_tests); diff --git a/tests/unittests/tests-registry/namespace/namespace_tests_full.c b/tests/unittests/tests-registry/namespace/namespace_tests_full.c new file mode 100644 index 000000000000..06ccb0fe7dd6 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/namespace_tests_full.c @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests_full RIOT Registry Schema: Full + * @ingroup sys + * @brief RIOT Registry Full Schema using all possible data types of the riot registry + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" +#include "tests.h" +#include "tests/full.h" + +/* Mapping */ +static void mapping(const registry_parameter_id_t parameter_id, const registry_instance_t *instance, + void **val, size_t *val_len) +{ + registry_tests_full_instance_t *_instance = + (registry_tests_full_instance_t *)instance->data; + + switch (parameter_id) { + case REGISTRY_TESTS_FULL_OPAQUE: + *val = &_instance->opaque; + *val_len = sizeof(_instance->opaque); + break; + + case REGISTRY_TESTS_FULL_STRING: + *val = &_instance->string; + *val_len = sizeof(_instance->string); + break; + + case REGISTRY_TESTS_FULL_BOOLEAN: + *val = &_instance->boolean; + *val_len = sizeof(_instance->boolean); + break; + + case REGISTRY_TESTS_FULL_U8: + *val = &_instance->u8; + *val_len = sizeof(_instance->u8); + break; + + case REGISTRY_TESTS_FULL_U16: + *val = &_instance->u16; + *val_len = sizeof(_instance->u16); + break; + + case REGISTRY_TESTS_FULL_U32: + *val = &_instance->u32; + *val_len = sizeof(_instance->u32); + break; + + case REGISTRY_TESTS_FULL_U64: + *val = &_instance->u64; + *val_len = sizeof(_instance->u64); + break; + + case REGISTRY_TESTS_FULL_I8: + *val = &_instance->i8; + *val_len = sizeof(_instance->i8); + break; + + case REGISTRY_TESTS_FULL_I16: + *val = &_instance->i16; + *val_len = sizeof(_instance->i16); + break; + + case REGISTRY_TESTS_FULL_I32: + *val = &_instance->i32; + *val_len = sizeof(_instance->i32); + break; + + case REGISTRY_TESTS_FULL_I64: + *val = &_instance->i64; + *val_len = sizeof(_instance->i64); + break; + + case REGISTRY_TESTS_FULL_F32: + *val = &_instance->f32; + *val_len = sizeof(_instance->f32); + break; + + case REGISTRY_TESTS_FULL_F64: + *val = &_instance->f64; + *val_len = sizeof(_instance->f64); + break; + } +} + +/* Schema parameters */ +const registry_parameter_t registry_tests_full_opaque = { + .id = REGISTRY_TESTS_FULL_OPAQUE, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "opaque", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_OPAQUE, +}; + +const registry_parameter_t registry_tests_full_string = { + .id = REGISTRY_TESTS_FULL_STRING, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "string", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_STRING, +}; + +const registry_parameter_t registry_tests_full_boolean = { + .id = REGISTRY_TESTS_FULL_BOOLEAN, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "boolean", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_BOOL, +}; + +const registry_parameter_t registry_tests_full_u8 = { + .id = REGISTRY_TESTS_FULL_U8, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u8", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_UINT8, +}; + +const registry_parameter_t registry_tests_full_u16 = { + .id = REGISTRY_TESTS_FULL_U16, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u16", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_UINT16, +}; + +const registry_parameter_t registry_tests_full_u32 = { + .id = REGISTRY_TESTS_FULL_U32, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u32", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_UINT32, +}; + +const registry_parameter_t registry_tests_full_u64 = { + .id = REGISTRY_TESTS_FULL_U64, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "u64", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_UINT64, +}; + +const registry_parameter_t registry_tests_full_i8 = { + .id = REGISTRY_TESTS_FULL_I8, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i8", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_INT8, +}; + +const registry_parameter_t registry_tests_full_i16 = { + .id = REGISTRY_TESTS_FULL_I16, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i16", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_INT16, +}; + +const registry_parameter_t registry_tests_full_i32 = { + .id = REGISTRY_TESTS_FULL_I32, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i32", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_INT32, +}; + +const registry_parameter_t registry_tests_full_i64 = { + .id = REGISTRY_TESTS_FULL_I64, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "i64", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_INT64, +}; + +const registry_parameter_t registry_tests_full_f32 = { + .id = REGISTRY_TESTS_FULL_F32, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "f32", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_FLOAT32, +}; + +const registry_parameter_t registry_tests_full_f64 = { + .id = REGISTRY_TESTS_FULL_F64, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "f64", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_full, + .type = REGISTRY_TYPE_FLOAT64, +}; + +/* Schema */ +registry_schema_t registry_tests_full = { + .id = REGISTRY_TESTS_FULL, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "full", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .namespace = ®istry_tests, + .mapping = mapping, + .groups = NULL, + .groups_len = 0, + .parameters = (const registry_parameter_t *[]) { + ®istry_tests_full_opaque, + ®istry_tests_full_string, + ®istry_tests_full_boolean, + ®istry_tests_full_u8, + ®istry_tests_full_u16, + ®istry_tests_full_u32, + ®istry_tests_full_u64, + ®istry_tests_full_i8, + ®istry_tests_full_i16, + ®istry_tests_full_i32, + ®istry_tests_full_i64, + ®istry_tests_full_f32, + ®istry_tests_full_f64, + }, + .parameters_len = 13, +}; diff --git a/tests/unittests/tests-registry/namespace/namespace_tests_nested.c b/tests/unittests/tests-registry/namespace/namespace_tests_nested.c new file mode 100644 index 000000000000..75c73ce79fa8 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/namespace_tests_nested.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests_nested RIOT Registry Schema: Nested + * @ingroup sys + * @brief RIOT Registry Nested Schema representing different nesting levels of a configuration schema + * @{ + * + * @file + * + * @author Lasse Rosenow + * + * @} + */ + +#include +#include +#include +#include + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "kernel_defines.h" +#include "registry.h" +#include "tests.h" +#include "tests/nested.h" + +/* Mapping */ +static void mapping(const registry_parameter_id_t parameter_id, const registry_instance_t *instance, + void **val, size_t *val_len) +{ + registry_tests_nested_instance_t *_instance = + (registry_tests_nested_instance_t *)instance->data; + + switch (parameter_id) { + case REGISTRY_TESTS_NESTED_PARAMETER: + *val = &_instance->parameter; + *val_len = sizeof(_instance->parameter); + break; + + case REGISTRY_TESTS_NESTED_GROUP_PARAMETER: + *val = &_instance->group_parameter; + *val_len = sizeof(_instance->group_parameter); + break; + } +} + +/* Schema parameters */ +const registry_parameter_t registry_tests_nested_parameter = { + .id = REGISTRY_TESTS_NESTED_PARAMETER, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "parameter", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_nested, + .type = REGISTRY_TYPE_UINT8, +}; + +const registry_parameter_t registry_tests_nested_group_parameter = { + .id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "parameter", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_nested, + .type = REGISTRY_TYPE_UINT8, +}; + +/* Schema groups */ +const registry_group_t registry_tests_nested_group = { + .id = REGISTRY_TESTS_NESTED_GROUP, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "group", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .schema = ®istry_tests_nested, + .groups = NULL, + .groups_len = 0, + .parameters = (const registry_parameter_t *[]) { + ®istry_tests_nested_group_parameter, + }, + .parameters_len = 1, +}; + +/* Schema */ +registry_schema_t registry_tests_nested = { + .id = REGISTRY_TESTS_NESTED, +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + .name = "nested", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_DESCRIPTION) || IS_ACTIVE(DOXYGEN) + .description = "", +#endif /* CONFIG_REGISTRY_ENABLE_META_DESCRIPTION */ + .namespace = ®istry_tests, + .mapping = mapping, + .groups = (const registry_group_t *[]) { + ®istry_tests_nested_group, + }, + .groups_len = 1, + .parameters = (const registry_parameter_t *[]) { + ®istry_tests_nested_parameter, + }, + .parameters_len = 1, +}; diff --git a/tests/unittests/tests-registry/namespace/tests.h b/tests/unittests/tests-registry/namespace/tests.h new file mode 100644 index 000000000000..203b544715d7 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/tests.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests RIOT Registry Tests Namespace + * @ingroup tests + * @brief RIOT Registry Namespace Tests module + * @{ + * + * This module provides common test schemas for the RIOT Registry sys module + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_TESTS_H +#define REGISTRY_NAMESPACE_TESTS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +extern registry_namespace_t registry_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 { + REGISTRY_TESTS_FULL, + REGISTRY_TESTS_NESTED, +} registry_tests_indices_t; + +#ifdef __cplusplus +} +#endif + +#endif /* REGISTRY_NAMESPACE_TESTS_H */ +/** @} */ diff --git a/tests/unittests/tests-registry/namespace/tests/full.h b/tests/unittests/tests-registry/namespace/tests/full.h new file mode 100644 index 000000000000..b4e627a8c73b --- /dev/null +++ b/tests/unittests/tests-registry/namespace/tests/full.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests_full RIOT Registry Schema: Full + * @ingroup sys + * @brief RIOT Registry Full Schema using all possible data types of the riot registry + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_TESTS_FULL_H +#define REGISTRY_NAMESPACE_TESTS_FULL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/* FULL */ +extern const registry_parameter_t registry_tests_full_opaque; +extern const registry_parameter_t registry_tests_full_string; +extern const registry_parameter_t registry_tests_full_boolean; +extern const registry_parameter_t registry_tests_full_u8; +extern const registry_parameter_t registry_tests_full_u16; +extern const registry_parameter_t registry_tests_full_u32; +extern const registry_parameter_t registry_tests_full_u64; +extern const registry_parameter_t registry_tests_full_i8; +extern const registry_parameter_t registry_tests_full_i16; +extern const registry_parameter_t registry_tests_full_i32; +extern const registry_parameter_t registry_tests_full_i64; +extern const registry_parameter_t registry_tests_full_f32; +extern const registry_parameter_t registry_tests_full_f64; +extern registry_schema_t registry_tests_full; + +typedef struct { + uint8_t value; +} registry_tests_full_instance_opaque_t; + +typedef struct { + clist_node_t node; + registry_tests_full_instance_opaque_t opaque; + 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; +} registry_tests_full_instance_t; + +typedef enum { + REGISTRY_TESTS_FULL_OPAQUE, + REGISTRY_TESTS_FULL_STRING, + REGISTRY_TESTS_FULL_BOOLEAN, + REGISTRY_TESTS_FULL_U8, + REGISTRY_TESTS_FULL_U16, + REGISTRY_TESTS_FULL_U32, + REGISTRY_TESTS_FULL_U64, + REGISTRY_TESTS_FULL_I8, + REGISTRY_TESTS_FULL_I16, + REGISTRY_TESTS_FULL_I32, + REGISTRY_TESTS_FULL_I64, + REGISTRY_TESTS_FULL_F32, + REGISTRY_TESTS_FULL_F64, +} registry_tests_full_indices_t; + +#ifdef __cplusplus +} +#endif + +#endif /* MODULE_REGISTRY_NAMESPACE_TESTS_FULL */ +/** @} */ diff --git a/tests/unittests/tests-registry/namespace/tests/nested.h b/tests/unittests/tests-registry/namespace/tests/nested.h new file mode 100644 index 000000000000..0b58a97ad2d8 --- /dev/null +++ b/tests/unittests/tests-registry/namespace/tests/nested.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup sys_registry_namespace_tests_nested RIOT Registry Schema: Nested + * @ingroup sys + * @brief RIOT Registry Nested Schema representing different nesting levels of a configuration schema + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef REGISTRY_NAMESPACE_TESTS_NESTED_H +#define REGISTRY_NAMESPACE_TESTS_NESTED_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "registry.h" + +/* NESTED */ +extern const registry_parameter_t registry_tests_nested_parameter; +extern const registry_group_t registry_tests_nested_group; +extern const registry_parameter_t registry_tests_nested_group_parameter; +extern registry_schema_t registry_tests_nested; + +typedef struct { + clist_node_t node; + uint8_t parameter; + uint8_t group_parameter; +} registry_tests_nested_instance_t; + +typedef enum { + REGISTRY_TESTS_NESTED_PARAMETER, + REGISTRY_TESTS_NESTED_GROUP, + REGISTRY_TESTS_NESTED_GROUP_PARAMETER, +} registry_tests_nested_indices_t; + +#ifdef __cplusplus +} +#endif + +#endif /* MODULE_REGISTRY_NAMESPACE_TESTS_NESTED */ +/** @} */ diff --git a/tests/unittests/tests-registry/tests-commit.c b/tests/unittests/tests-registry/tests-commit.c new file mode 100644 index 000000000000..052e3f6b72ba --- /dev/null +++ b/tests/unittests/tests-registry/tests-commit.c @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Unittests for registry_commit + * + * @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 "registry.h" + +#include "tests-registry.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +static bool successful = false; +static registry_group_or_parameter_id_t parameter_id; +static registry_group_or_parameter_id_t group_id; + +static registry_error_t commit_parameter_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t * + group_or_parameter_id, + const void *context) +{ + (void)context; + + if (scope == REGISTRY_COMMIT_PARAMETER && group_or_parameter_id != NULL && + *group_or_parameter_id == parameter_id) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t commit_group_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t * + group_or_parameter_id, + const void *context) +{ + (void)context; + + if (scope == REGISTRY_COMMIT_GROUP && group_or_parameter_id != NULL && + *group_or_parameter_id == group_id) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t commit_instance_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t * + group_or_parameter_id, + const void *context) +{ + (void)context; + + if (scope == REGISTRY_COMMIT_INSTANCE && group_or_parameter_id == NULL) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_nested_instance_parameter_test = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = &commit_parameter_cb, +}; + +static registry_instance_t test_nested_instance_group_test = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-group-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = &commit_group_cb, +}; + +static registry_instance_t test_nested_instance_instance_test = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-instance-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = &commit_instance_cb, +}; + +static void test_registry_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance_parameter_test); + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance_group_test); + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance_instance_test); +} + +static void test_registry_teardown(void) +{ +} + +static void tests_registry_commit_parameter(void) +{ + successful = false; + parameter_id = REGISTRY_TESTS_NESTED_PARAMETER; + + const registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance_parameter_test, + .parameter = ®istry_tests_nested_parameter, + }, + }; + + registry_commit(&node); + + TEST_ASSERT(successful); +} + +static void tests_registry_commit_group(void) +{ + successful = false; + group_id = REGISTRY_TESTS_NESTED_GROUP; + + const registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = &test_nested_instance_group_test, + .group = ®istry_tests_nested_group, + }, + }; + + registry_commit(&node); + + TEST_ASSERT(successful); +} + +static void tests_registry_commit_instance(void) +{ + successful = false; + + const registry_node_t node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = &test_nested_instance_instance_test, + }; + + registry_commit(&node); + + TEST_ASSERT(successful); +} + +static void tests_registry_commit_schema(void) +{ + successful = false; + + const registry_node_t node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = ®istry_tests_nested, + }; + + registry_commit(&node); + + TEST_ASSERT(successful); +} + +static void tests_registry_commit_namespace(void) +{ + successful = false; + + const registry_node_t node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = ®istry_tests, + }; + + registry_commit(&node); + + TEST_ASSERT(successful); +} + +static void tests_registry_commit_all(void) +{ + successful = false; + registry_commit(NULL); + TEST_ASSERT(successful); +} + +Test *tests_registry_commit_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_registry_commit_parameter), + new_TestFixture(tests_registry_commit_group), + new_TestFixture(tests_registry_commit_instance), + new_TestFixture(tests_registry_commit_schema), + new_TestFixture(tests_registry_commit_namespace), + new_TestFixture(tests_registry_commit_all), + }; + + EMB_UNIT_TESTCALLER(registry_tests, test_registry_setup, test_registry_teardown, fixtures); + + return (Test *)®istry_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-registry/tests-export.c b/tests/unittests/tests-registry/tests-export.c new file mode 100644 index 000000000000..0ff8d0f60761 --- /dev/null +++ b/tests/unittests/tests-registry/tests-export.c @@ -0,0 +1,388 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Unittests for registry_export + * + * @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 "registry.h" + +#include "tests-registry.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +static bool successful = false; + +static registry_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_nested_instance_1 = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = NULL, +}; + +static registry_error_t export_parameter_cb(const registry_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == REGISTRY_NODE_PARAMETER && node->value.parameter.parameter != NULL && + node->value.parameter.parameter->id == *(registry_parameter_id_t *)context && + node->value.parameter.instance == &test_nested_instance_1) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t export_group_cb(const registry_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == REGISTRY_NODE_GROUP && node->value.group.group != NULL && + node->value.group.group->id == *(registry_group_id_t *)context) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t export_instance_cb(const registry_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == REGISTRY_NODE_INSTANCE && node->value.instance == &test_nested_instance_1) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t export_schema_cb(const registry_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == REGISTRY_NODE_SCHEMA && node->value.schema != NULL && + node->value.schema == ®istry_tests_nested) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static registry_error_t export_namespace_cb(const registry_node_t *node, + const void *context) +{ + (void)context; + + if (node->type == REGISTRY_NODE_NAMESPACE && node->value.namespace != NULL && + node->value.namespace == ®istry_tests) { + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static void test_registry_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance_1); +} + +static void test_registry_teardown(void) +{ +} + +static void tests_registry_export_parameter(void) +{ + const registry_parameter_id_t parameter_id = REGISTRY_TESTS_NESTED_PARAMETER; + + successful = false; + + const registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance_1, + .parameter = ®istry_tests_nested_parameter, + }, + }; + + registry_export(&node, &export_parameter_cb, 0, ¶meter_id); + + TEST_ASSERT(successful); +} + +static void tests_registry_export_group(void) +{ + const registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = &test_nested_instance_1, + .group = ®istry_tests_nested_group, + }, + }; + + /* check if group gets exported */ + const registry_group_id_t group_id = REGISTRY_TESTS_NESTED_GROUP; + + successful = false; + registry_export(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + + /* check that siblings get NOT exported */ + const registry_parameter_id_t sibling_parameter_id = REGISTRY_TESTS_NESTED_PARAMETER; + successful = false; + registry_export(&node, &export_parameter_cb, 0, &sibling_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + + /* check if children get exported */ + const registry_parameter_id_t child_parameter_id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER; + + /* recursion_depth 0 => infinite => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* recursion_depth 1 => only group => parameter gets NOT exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 1, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* recursion_depth 2 => group + 1 level more => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 2, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_registry_export_instance(void) +{ + const registry_node_t node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = &test_nested_instance_1, + }; + + /* check if instance gets exported */ + successful = false; + registry_export(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + + /* check if group gets exported */ + const registry_group_id_t group_id = REGISTRY_TESTS_NESTED_GROUP; + + successful = false; + registry_export(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + + /* check if parameter get exported */ + const registry_parameter_id_t child_parameter_id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER; + + /* recursion_depth 0 => infinite => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* recursion_depth 2 => only instance and group => parameter gets NOT exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 2, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* recursion_depth 3 => instance, group and parameter => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 3, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_registry_export_schema(void) +{ + const registry_node_t node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = ®istry_tests_nested, + }; + + /* check if schema gets exported */ + successful = false; + registry_export(&node, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + registry_export(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + + /* check if group gets exported */ + const registry_group_id_t group_id = REGISTRY_TESTS_NESTED_GROUP; + + successful = false; + registry_export(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + + /* check if parameter get exported */ + const registry_parameter_id_t child_parameter_id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER; + + /* recursion_depth 0 => infinite => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* recursion_depth 3 => only schema, instance and group => parameter gets NOT exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 3, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* recursion_depth 4 => schema, instance, group and parameter => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 4, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_registry_export_namespace(void) +{ + const registry_node_t node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = ®istry_tests, + }; + + /* check if namespace gets exported */ + successful = false; + registry_export(&node, &export_namespace_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if schema gets exported */ + successful = false; + registry_export(&node, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + registry_export(&node, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + + /* check if group gets exported */ + const registry_group_id_t group_id = REGISTRY_TESTS_NESTED_GROUP; + + successful = false; + registry_export(&node, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + + /* check if parameter get exported */ + const registry_parameter_id_t child_parameter_id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER; + + /* recursion_depth 0 => infinite => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* recursion_depth 4 => only namespace, schema, instance and group => parameter gets NOT exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 4, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* recursion_depth 5 => namespace, schema, instance, group and parameter => parameter gets exported */ + successful = false; + registry_export(&node, &export_parameter_cb, 5, &child_parameter_id); + TEST_ASSERT(successful); +} + +static void tests_registry_export_all(void) +{ + /* check if namespace gets exported */ + successful = false; + registry_export(NULL, &export_namespace_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if schema gets exported */ + successful = false; + registry_export(NULL, &export_schema_cb, 0, NULL); + TEST_ASSERT(successful); + + /* check if instance gets exported */ + successful = false; + registry_export(NULL, &export_instance_cb, 0, NULL); + TEST_ASSERT(successful); + + + /* check if group gets exported */ + const registry_group_id_t group_id = REGISTRY_TESTS_NESTED_GROUP; + + successful = false; + registry_export(NULL, &export_group_cb, 0, &group_id); + TEST_ASSERT(successful); + + + /* check if parameter get exported */ + const registry_parameter_id_t child_parameter_id = REGISTRY_TESTS_NESTED_GROUP_PARAMETER; + + /* recursion_depth 0 => infinite => parameter gets exported */ + successful = false; + registry_export(NULL, &export_parameter_cb, 0, &child_parameter_id); + TEST_ASSERT(successful); + + /* recursion_depth 4 => only namespace, schema, instance and group => parameter gets NOT exported */ + successful = false; + registry_export(NULL, &export_parameter_cb, 4, &child_parameter_id); + TEST_ASSERT_EQUAL_INT(false, successful); + + /* recursion_depth 5 => namespace, schema, instance, group and parameter => parameter gets exported */ + successful = false; + registry_export(NULL, &export_parameter_cb, 5, &child_parameter_id); + TEST_ASSERT(successful); +} + +Test *tests_registry_export_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_registry_export_parameter), + new_TestFixture(tests_registry_export_group), + new_TestFixture(tests_registry_export_instance), + new_TestFixture(tests_registry_export_schema), + new_TestFixture(tests_registry_export_namespace), + new_TestFixture(tests_registry_export_all), + }; + + EMB_UNIT_TESTCALLER(registry_tests, test_registry_setup, test_registry_teardown, fixtures); + + return (Test *)®istry_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-registry/tests-get-set.c b/tests/unittests/tests-registry/tests-get-set.c new file mode 100644 index 000000000000..52f0352a1fae --- /dev/null +++ b/tests/unittests/tests-registry/tests-get-set.c @@ -0,0 +1,572 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Unittests for registry_get, registry_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 "registry.h" + +#include "tests-registry.h" +#include "namespace/tests.h" +#include "namespace/tests/full.h" + +#define FLOAT_MAX_CHAR_COUNT ((FLT_MAX_10_EXP + 1) + 1 + 1 + 6) // (FLT_MAX_10_EXP + 1) + sign + dot + 6 decimal places +#define DOUBLE_MAX_CHAR_COUNT ((DBL_MAX_10_EXP + 1) + 1 + 1 + 6) // (DBL_MAX_10_EXP + 1) + sign + dot + 6 decimal places + +static registry_error_t commit_cb(const registry_commit_cb_scope_t scope, + const registry_group_or_parameter_id_t *group_or_parameter_id, + const void *context) +{ + (void)scope; + (void)group_or_parameter_id; + (void)context; + + return REGISTRY_ERROR_NONE; +} + +static registry_tests_full_instance_t test_full_instance_1_data = { + .opaque = { + .value = 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 registry_instance_t test_full_instance_1 = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-full-1", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_full_instance_1_data, + .commit_cb = &commit_cb, +}; + + + +static void test_registry_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_full, &test_full_instance_1); +} + +static void test_registry_teardown(void) +{ +} + +static void tests_registry_min_values(void) +{ + registry_value_t output; + + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter.instance = &test_full_instance_1, + }; + + /* opaque */ + const registry_tests_full_instance_opaque_t input_opaque = { + .value = 0, + }; + + node.value.parameter.parameter = ®istry_tests_full_opaque; + + registry_set(&node, &input_opaque, sizeof(input_opaque)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_opaque.value, + ((registry_tests_full_instance_opaque_t *)output.buf)->value); + + /* string */ + const char input_string[] = ""; + node.value.parameter.parameter = ®istry_tests_full_string; + + registry_set(&node, input_string, sizeof(input_string)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_STRING("", (char *)output.buf); + + + /* bool */ + const bool input_bool = false; + node.value.parameter.parameter = ®istry_tests_full_boolean; + + registry_set(&node, &input_bool, sizeof(input_bool)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output.buf); + + + /* u8 */ + const uint8_t input_u8 = 0; + node.value.parameter.parameter = ®istry_tests_full_u8; + + registry_set(&node, &input_u8, sizeof(input_u8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output.buf); + + + /* u16 */ + const uint16_t input_u16 = 0; + node.value.parameter.parameter = ®istry_tests_full_u16; + + registry_set(&node, &input_u16, sizeof(input_u16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output.buf); + + + /* u32 */ + const uint32_t input_u32 = 0; + node.value.parameter.parameter = ®istry_tests_full_u32; + + registry_set(&node, &input_u32, sizeof(input_u32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output.buf); + + + /* u64 */ + const uint64_t input_u64 = 0; + node.value.parameter.parameter = ®istry_tests_full_u64; + + registry_set(&node, &input_u64, sizeof(input_u64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output.buf); + + + /* i8 */ + const int8_t input_i8 = INT8_MIN; + node.value.parameter.parameter = ®istry_tests_full_i8; + + registry_set(&node, &input_i8, sizeof(input_i8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output.buf); + + + /* i16 */ + const int16_t input_i16 = INT16_MIN; + node.value.parameter.parameter = ®istry_tests_full_i16; + + registry_set(&node, &input_i16, sizeof(input_i16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output.buf); + + + /* i32 */ + const int32_t input_i32 = INT32_MIN; + node.value.parameter.parameter = ®istry_tests_full_i32; + + registry_set(&node, &input_i32, sizeof(input_i32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output.buf); + + + /* i64 */ + const int64_t input_i64 = INT64_MIN; + node.value.parameter.parameter = ®istry_tests_full_i64; + + registry_set(&node, &input_i64, sizeof(input_i64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output.buf); + + + /* f32 */ + const float input_f32 = FLT_MIN; + node.value.parameter.parameter = ®istry_tests_full_f32; + + registry_set(&node, &input_f32, sizeof(input_f32)); + registry_get(&node, &output); + + char input_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f32_string, "%f", input_f32); + sprintf(output_f32_string, "%f", *(float *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f32_string, output_f32_string); + + + /* f64 */ + const double input_f64 = DBL_MIN; + node.value.parameter.parameter = ®istry_tests_full_f64; + + registry_set(&node, &input_f64, sizeof(input_f64)); + registry_get(&node, &output); + + char input_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f64_string, "%f", input_f64); + sprintf(output_f64_string, "%f", *(double *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f64_string, output_f64_string); +} + +static void tests_registry_zero_values(void) +{ + registry_value_t output; + + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter.instance = &test_full_instance_1, + }; + + /* opaque */ + const registry_tests_full_instance_opaque_t input_opaque = { + .value = 0, + }; + + node.value.parameter.parameter = ®istry_tests_full_opaque; + + registry_set(&node, &input_opaque, sizeof(input_opaque)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_opaque.value, + ((registry_tests_full_instance_opaque_t *)output.buf)->value); + + + /* string */ + const char input_string[] = ""; + node.value.parameter.parameter = ®istry_tests_full_string; + + registry_set(&node, input_string, sizeof(input_string)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_STRING("", (char *)output.buf); + + + /* bool */ + const bool input_bool = 0; + node.value.parameter.parameter = ®istry_tests_full_boolean; + + registry_set(&node, &input_bool, sizeof(input_bool)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output.buf); + + + /* u8 */ + const uint8_t input_u8 = 0; + node.value.parameter.parameter = ®istry_tests_full_u8; + + registry_set(&node, &input_u8, sizeof(input_u8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output.buf); + + + /* u16 */ + const uint16_t input_u16 = 0; + node.value.parameter.parameter = ®istry_tests_full_u16; + + registry_set(&node, &input_u16, sizeof(input_u16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output.buf); + + + /* u32 */ + const uint32_t input_u32 = 0; + node.value.parameter.parameter = ®istry_tests_full_u32; + + registry_set(&node, &input_u32, sizeof(input_u32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output.buf); + + + /* u64 */ + const uint64_t input_u64 = 0; + node.value.parameter.parameter = ®istry_tests_full_u64; + + registry_set(&node, &input_u64, sizeof(input_u64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output.buf); + + + /* i8 */ + const int8_t input_i8 = 0; + node.value.parameter.parameter = ®istry_tests_full_i8; + + registry_set(&node, &input_i8, sizeof(input_i8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output.buf); + + + /* i16 */ + const int16_t input_i16 = 0; + node.value.parameter.parameter = ®istry_tests_full_i16; + + registry_set(&node, &input_i16, sizeof(input_i16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output.buf); + + + /* i32 */ + const int32_t input_i32 = 0; + node.value.parameter.parameter = ®istry_tests_full_i32; + + registry_set(&node, &input_i32, sizeof(input_i32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output.buf); + + + /* i64 */ + const int64_t input_i64 = 0; + node.value.parameter.parameter = ®istry_tests_full_i64; + + registry_set(&node, &input_i64, sizeof(input_i64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output.buf); + + + /* f32 */ + const float input_f32 = 0.0; + node.value.parameter.parameter = ®istry_tests_full_f32; + + registry_set(&node, &input_f32, sizeof(input_f32)); + registry_get(&node, &output); + + char input_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f32_string, "%f", input_f32); + sprintf(output_f32_string, "%f", *(float *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f32_string, output_f32_string); + + + /* f64 */ + const double input_f64 = 0.0; + node.value.parameter.parameter = ®istry_tests_full_f64; + + registry_set(&node, &input_f64, sizeof(input_f64)); + registry_get(&node, &output); + + char input_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f64_string, "%f", input_f64); + sprintf(output_f64_string, "%f", *(double *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f64_string, output_f64_string); +} + +static void tests_registry_max_values(void) +{ + registry_value_t output; + + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter.instance = &test_full_instance_1, + }; + + /* opaque */ + const registry_tests_full_instance_opaque_t input_opaque = { + .value = UINT8_MAX, + }; + + node.value.parameter.parameter = ®istry_tests_full_opaque; + + registry_set(&node, &input_opaque, sizeof(input_opaque)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_opaque.value, + ((registry_tests_full_instance_opaque_t *)output.buf)->value); + + + /* 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.value.parameter.parameter = ®istry_tests_full_string; + + registry_set(&node, input_string, sizeof(input_string)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_STRING(input_string, (char *)output.buf); + + + /* bool */ + const bool input_bool = true; + node.value.parameter.parameter = ®istry_tests_full_boolean; + + registry_set(&node, &input_bool, sizeof(input_bool)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_bool, *(bool *)output.buf); + + + /* u8 */ + const uint8_t input_u8 = UINT8_MAX; + node.value.parameter.parameter = ®istry_tests_full_u8; + + registry_set(&node, &input_u8, sizeof(input_u8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u8, *(uint8_t *)output.buf); + + + /* u16 */ + const uint16_t input_u16 = UINT16_MAX; + node.value.parameter.parameter = ®istry_tests_full_u16; + + registry_set(&node, &input_u16, sizeof(input_u16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u16, *(uint16_t *)output.buf); + + + /* u32 */ + const uint32_t input_u32 = UINT32_MAX; + node.value.parameter.parameter = ®istry_tests_full_u32; + + registry_set(&node, &input_u32, sizeof(input_u32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u32, *(uint32_t *)output.buf); + + + /* u64 */ + const uint64_t input_u64 = UINT64_MAX; + node.value.parameter.parameter = ®istry_tests_full_u64; + + registry_set(&node, &input_u64, sizeof(input_u64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_u64, *(uint64_t *)output.buf); + + + /* i8 */ + const int8_t input_i8 = INT8_MAX; + node.value.parameter.parameter = ®istry_tests_full_i8; + + registry_set(&node, &input_i8, sizeof(input_i8)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i8, *(int8_t *)output.buf); + + + /* i16 */ + const int16_t input_i16 = INT16_MAX; + node.value.parameter.parameter = ®istry_tests_full_i16; + + registry_set(&node, &input_i16, sizeof(input_i16)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i16, *(int16_t *)output.buf); + + + /* i32 */ + const int32_t input_i32 = INT32_MAX; + node.value.parameter.parameter = ®istry_tests_full_i32; + + registry_set(&node, &input_i32, sizeof(input_i32)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i32, *(int32_t *)output.buf); + + + /* i64 */ + const int64_t input_i64 = INT64_MAX; + node.value.parameter.parameter = ®istry_tests_full_i64; + + registry_set(&node, &input_i64, sizeof(input_i64)); + registry_get(&node, &output); + + TEST_ASSERT_EQUAL_INT(input_i64, *(int64_t *)output.buf); + + + /* f32 */ + const float input_f32 = FLT_MAX; + node.value.parameter.parameter = ®istry_tests_full_f32; + + registry_set(&node, &input_f32, sizeof(input_f32)); + registry_get(&node, &output); + + char input_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f32_string[FLOAT_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f32_string, "%f", input_f32); + sprintf(output_f32_string, "%f", *(float *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f32_string, output_f32_string); + + + /* f64 */ + const double input_f64 = DBL_MAX; + node.value.parameter.parameter = ®istry_tests_full_f64; + + registry_set(&node, &input_f64, sizeof(input_f64)); + registry_get(&node, &output); + + char input_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + char output_f64_string[DOUBLE_MAX_CHAR_COUNT + 1] = { 0 }; + sprintf(input_f64_string, "%f", input_f64); + sprintf(output_f64_string, "%f", *(double *)output.buf); + + TEST_ASSERT_EQUAL_STRING(input_f64_string, output_f64_string); +} + +Test *tests_registry_get_set_tests(void) +{ + + (void)tests_registry_min_values; + (void)tests_registry_zero_values; + (void)tests_registry_max_values; + + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_registry_min_values), + new_TestFixture(tests_registry_zero_values), + new_TestFixture(tests_registry_max_values), + }; + + EMB_UNIT_TESTCALLER(registry_tests, test_registry_setup, test_registry_teardown, fixtures); + + return (Test *)®istry_tests; +} + +/** @} */ diff --git a/tests/unittests/tests-registry/tests-registry.c b/tests/unittests/tests-registry/tests-registry.c new file mode 100644 index 000000000000..b763a3a4aa65 --- /dev/null +++ b/tests/unittests/tests-registry/tests-registry.c @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @{ + * + * @file + * @brief Unittest entry point for the Registry test group + * + * @author Lasse Rosenow + */ + +#include "embUnit/embUnit.h" + +#include "tests-registry.h" + +Test *tests_registry_get_set_tests(void); +Test *tests_registry_commit_tests(void); +Test *tests_registry_export_tests(void); + +void tests_registry(void) +{ + TESTS_RUN(tests_registry_get_set_tests()); + TESTS_RUN(tests_registry_commit_tests()); + TESTS_RUN(tests_registry_export_tests()); +} +/** @} */ diff --git a/tests/unittests/tests-registry/tests-registry.h b/tests/unittests/tests-registry/tests-registry.h new file mode 100644 index 000000000000..1b22419139a7 --- /dev/null +++ b/tests/unittests/tests-registry/tests-registry.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ +#ifndef TESTS_REGISTRY_H +#define TESTS_REGISTRY_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_H */ +/** @} */ diff --git a/tests/unittests/tests-registry_int_path/Makefile b/tests/unittests/tests-registry_int_path/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry_int_path/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry_int_path/Makefile.include b/tests/unittests/tests-registry_int_path/Makefile.include new file mode 100644 index 000000000000..c0906ef1ed59 --- /dev/null +++ b/tests/unittests/tests-registry_int_path/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += registry_int_path + +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry_int_path/namespace b/tests/unittests/tests-registry_int_path/namespace new file mode 120000 index 000000000000..6a0bcd8e750e --- /dev/null +++ b/tests/unittests/tests-registry_int_path/namespace @@ -0,0 +1 @@ +../tests-registry/namespace \ No newline at end of file diff --git a/tests/unittests/tests-registry_int_path/tests-registry_int_path.c b/tests/unittests/tests-registry_int_path/tests-registry_int_path.c new file mode 100644 index 000000000000..d13fde3b6cef --- /dev/null +++ b/tests/unittests/tests-registry_int_path/tests-registry_int_path.c @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_int_path`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#include "embUnit/embUnit.h" +#include "tests-registry_int_path.h" + +#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 "registry.h" +#include "registry/int_path.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +static registry_tests_nested_instance_t test_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_instance = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_instance_data, + .commit_cb = NULL, +}; + +static void test_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_instance); +} + +/* to int_path */ +static void tests_registry_to_parameter_int_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_instance, + .parameter = ®istry_tests_nested_parameter, + }, + }; + registry_int_path_t path; + int res = registry_node_to_int_path(&node, &path); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(registry_tests.id, path.value.parameter_path.namespace_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested.id, path.value.parameter_path.schema_id); + TEST_ASSERT_EQUAL_INT(test_instance.id, path.value.parameter_path.instance_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested_parameter.id, + path.value.parameter_path.parameter_id); +} + +static void tests_registry_to_group_int_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = &test_instance, + .group = ®istry_tests_nested_group, + }, + }; + registry_int_path_t path; + int res = registry_node_to_int_path(&node, &path); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(registry_tests.id, path.value.group_path.namespace_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested.id, path.value.group_path.schema_id); + TEST_ASSERT_EQUAL_INT(test_instance.id, path.value.group_path.instance_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested_group.id, path.value.group_path.group_id); +} + +static void tests_registry_to_instance_int_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = &test_instance, + }; + registry_int_path_t path; + int res = registry_node_to_int_path(&node, &path); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(registry_tests.id, path.value.instance_path.namespace_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested.id, path.value.instance_path.schema_id); + TEST_ASSERT_EQUAL_INT(test_instance.id, path.value.instance_path.instance_id); +} + +static void tests_registry_to_schema_int_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = ®istry_tests_nested, + }; + registry_int_path_t path; + int res = registry_node_to_int_path(&node, &path); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(registry_tests.id, path.value.schema_path.namespace_id); + TEST_ASSERT_EQUAL_INT(registry_tests_nested.id, path.value.schema_path.schema_id); +} + +static void tests_registry_to_namespace_int_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = ®istry_tests, + }; + registry_int_path_t path; + int res = registry_node_to_int_path(&node, &path); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(registry_tests.id, path.value.namespace_path.namespace_id); +} + +/* from int_path */ +static void tests_registry_from_group_or_parameter_int_path(void) +{ + registry_node_t node; + + /* parameter */ + const registry_int_path_t parameter_path = { + .type = REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER, + .value.group_or_parameter_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + .instance_id = test_instance.id, + .group_or_parameter_id = registry_tests_nested_group_parameter.id, + }, + }; + + int res = registry_node_from_int_path(¶meter_path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_PARAMETER, node.type); + TEST_ASSERT_EQUAL_INT((int)&test_instance, (int)node.value.parameter.instance); + TEST_ASSERT_EQUAL_INT((int)®istry_tests_nested_group_parameter, + (int)node.value.parameter.parameter); + + + /* group */ + const registry_int_path_t group_path = { + .type = REGISTRY_INT_PATH_TYPE_GROUP_OR_PARAMETER, + .value.group_or_parameter_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + .instance_id = test_instance.id, + .group_or_parameter_id = registry_tests_nested_group.id, + }, + }; + + res = registry_node_from_int_path(&group_path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_GROUP, node.type); + TEST_ASSERT_EQUAL_INT((int)&test_instance, (int)node.value.group.instance); + TEST_ASSERT_EQUAL_INT((int)®istry_tests_nested_group, (int)node.value.group.group); +} + +static void tests_registry_from_parameter_int_path(void) +{ + registry_node_t node; + + const registry_int_path_t path = { + .type = REGISTRY_INT_PATH_TYPE_PARAMETER, + .value.parameter_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + .instance_id = test_instance.id, + .parameter_id = registry_tests_nested_group_parameter.id, + }, + }; + + int res = registry_node_from_int_path(&path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_PARAMETER, node.type); + TEST_ASSERT_EQUAL_INT((int)&test_instance, (int)node.value.parameter.instance); + TEST_ASSERT_EQUAL_INT((int)®istry_tests_nested_group_parameter, + (int)node.value.parameter.parameter); +} + +static void tests_registry_from_group_int_path(void) +{ + registry_node_t node; + + const registry_int_path_t path = { + .type = REGISTRY_INT_PATH_TYPE_GROUP, + .value.group_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + .instance_id = test_instance.id, + .group_id = registry_tests_nested_group.id, + }, + }; + + int res = registry_node_from_int_path(&path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_GROUP, node.type); + TEST_ASSERT_EQUAL_INT((int)&test_instance, (int)node.value.group.instance); + TEST_ASSERT_EQUAL_INT((int)®istry_tests_nested_group, (int)node.value.group.group); +} + +static void tests_registry_from_instance_int_path(void) +{ + registry_node_t node; + + const registry_int_path_t path = { + .type = REGISTRY_INT_PATH_TYPE_INSTANCE, + .value.instance_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + .instance_id = test_instance.id, + }, + }; + + int res = registry_node_from_int_path(&path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_INSTANCE, node.type); + TEST_ASSERT_EQUAL_INT((int)&test_instance, (int)node.value.instance); +} + +static void tests_registry_from_schema_int_path(void) +{ + registry_node_t node; + + const registry_int_path_t path = { + .type = REGISTRY_INT_PATH_TYPE_SCHEMA, + .value.schema_path = { + .namespace_id = registry_tests.id, + .schema_id = registry_tests_nested.id, + }, + }; + + int res = registry_node_from_int_path(&path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_SCHEMA, node.type); + TEST_ASSERT_EQUAL_INT((int)®istry_tests_nested, (int)node.value.schema); +} + +static void tests_registry_from_namespace_int_path(void) +{ + registry_node_t node; + + const registry_int_path_t path = { + .type = REGISTRY_INT_PATH_TYPE_NAMESPACE, + .value.namespace_path = { + .namespace_id = registry_tests.id, + }, + }; + + int res = registry_node_from_int_path(&path, &node); + + TEST_ASSERT_EQUAL_INT(0, res); + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_NAMESPACE, node.type); + TEST_ASSERT_EQUAL_INT((int)®istry_tests, (int)node.value.namespace); +} + +Test *tests_registry_int_path_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + /* to int_path */ + new_TestFixture(tests_registry_to_parameter_int_path), + new_TestFixture(tests_registry_to_group_int_path), + new_TestFixture(tests_registry_to_instance_int_path), + new_TestFixture(tests_registry_to_schema_int_path), + new_TestFixture(tests_registry_to_namespace_int_path), + /* from int_path */ + new_TestFixture(tests_registry_from_group_or_parameter_int_path), + new_TestFixture(tests_registry_from_parameter_int_path), + new_TestFixture(tests_registry_from_group_int_path), + new_TestFixture(tests_registry_from_instance_int_path), + new_TestFixture(tests_registry_from_schema_int_path), + new_TestFixture(tests_registry_from_namespace_int_path), + }; + + EMB_UNIT_TESTCALLER(registry_int_path_tests, test_setup, NULL, fixtures); + + return (Test *)®istry_int_path_tests; +} + +void tests_registry_int_path(void) +{ + TESTS_RUN(tests_registry_int_path_tests()); +} + +/** @} */ diff --git a/tests/unittests/tests-registry_int_path/tests-registry_int_path.h b/tests/unittests/tests-registry_int_path/tests-registry_int_path.h new file mode 100644 index 000000000000..bfaf4157c808 --- /dev/null +++ b/tests/unittests/tests-registry_int_path/tests-registry_int_path.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_int_path`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef TESTS_REGISTRY_INT_PATH_H +#define TESTS_REGISTRY_INT_PATH_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry_int_path(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_INT_PATH_H */ +/** @} */ diff --git a/tests/unittests/tests-registry_storage/Makefile b/tests/unittests/tests-registry_storage/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry_storage/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry_storage/Makefile.include b/tests/unittests/tests-registry_storage/Makefile.include new file mode 100644 index 000000000000..524ab54bf748 --- /dev/null +++ b/tests/unittests/tests-registry_storage/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += registry_storage + +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry_storage/namespace b/tests/unittests/tests-registry_storage/namespace new file mode 120000 index 000000000000..6a0bcd8e750e --- /dev/null +++ b/tests/unittests/tests-registry_storage/namespace @@ -0,0 +1 @@ +../tests-registry/namespace \ No newline at end of file diff --git a/tests/unittests/tests-registry_storage/tests-registry_storage.c b/tests/unittests/tests-registry_storage/tests-registry_storage.c new file mode 100644 index 000000000000..89d92b925f4a --- /dev/null +++ b/tests/unittests/tests-registry_storage/tests-registry_storage.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage`` module + * @{ + * + * @file + * + * @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 "registry.h" +#include "registry/storage.h" + +#include "tests-registry_storage.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +#define _TESTS_REGISTRY_LOAD_STORED_VALUE 60 + +static bool successful = false; + +static registry_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_nested_instance = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = NULL, +}; + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb); +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value); + +static registry_storage_t storage_test = { + .load = load, + .save = save, +}; + +static uint8_t storage_test_instance_data = 7; + +static registry_storage_instance_t storage_test_instance = { + .storage = &storage_test, + .data = &storage_test_instance_data, +}; + +static registry_error_t load(const registry_storage_instance_t *storage, + const load_cb_t load_cb) +{ + if (storage == &storage_test_instance) { + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance, + .parameter = ®istry_tests_nested_parameter, + }, + }; + uint8_t buf = _TESTS_REGISTRY_LOAD_STORED_VALUE; + return load_cb(&node, &buf, sizeof(buf)); + } + + return -REGISTRY_ERROR_INVALID_ARGUMENT; +} + +static registry_error_t save(const registry_storage_instance_t *storage, + const registry_node_t *node, + const registry_value_t *value) +{ + if (storage == &storage_test_instance && + node->value.parameter.instance == &test_nested_instance && + node->value.parameter.parameter == ®istry_tests_nested_group_parameter && + value->buf == &test_nested_instance_data.group_parameter && + value->buf_len == sizeof(uint8_t) && + value->type == REGISTRY_TYPE_UINT8) { + + successful = true; + } + + return REGISTRY_ERROR_NONE; +} + +static void test_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance); + + /* reset testing variable */ + successful = false; +} + +static void tests_registry_load(void) +{ + /* check if the registry_load function gets the correct input values internally */ + TEST_ASSERT_EQUAL_INT(registry_storage_load(&storage_test_instance), REGISTRY_ERROR_NONE); + + /* check if the load_cb sets the value to the registry */ + registry_value_t output; + + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance, + .parameter = ®istry_tests_nested_parameter, + }, + }; + + registry_get(&node, &output); + TEST_ASSERT_EQUAL_INT(_TESTS_REGISTRY_LOAD_STORED_VALUE, *(uint8_t *)output.buf); +} + +static void tests_registry_save_parameter(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance, + .parameter = ®istry_tests_nested_group_parameter, + }, + }; + + registry_storage_save(&storage_test_instance, &node); + + TEST_ASSERT(successful); +} + +static void tests_registry_save_group(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = &test_nested_instance, + .group = ®istry_tests_nested_group, + }, + }; + + registry_storage_save(&storage_test_instance, &node); + + TEST_ASSERT(successful); +} + +static void tests_registry_save_instance(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = &test_nested_instance, + }; + + registry_storage_save(&storage_test_instance, &node); + + TEST_ASSERT(successful); +} + +static void tests_registry_save_schema(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = ®istry_tests_nested, + }; + + registry_storage_save(&storage_test_instance, &node); + + TEST_ASSERT(successful); +} + +static void tests_registry_save_namespace(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = ®istry_tests, + }; + + registry_storage_save(&storage_test_instance, &node); + + TEST_ASSERT(successful); +} + +static void tests_registry_save_all(void) +{ + + registry_storage_save(&storage_test_instance, NULL); + + TEST_ASSERT(successful); +} + +Test *tests_registry_storage_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + /* load */ + new_TestFixture(tests_registry_load), + /* save */ + new_TestFixture(tests_registry_save_parameter), + new_TestFixture(tests_registry_save_group), + new_TestFixture(tests_registry_save_instance), + new_TestFixture(tests_registry_save_schema), + new_TestFixture(tests_registry_save_namespace), + new_TestFixture(tests_registry_save_all), + }; + + EMB_UNIT_TESTCALLER(registry_storage_tests, test_setup, NULL, fixtures); + + return (Test *)®istry_storage_tests; +} + +void tests_registry_storage(void) +{ + TESTS_RUN(tests_registry_storage_tests()); +} + +/** @} */ diff --git a/tests/unittests/tests-registry_storage/tests-registry_storage.h b/tests/unittests/tests-registry_storage/tests-registry_storage.h new file mode 100644 index 000000000000..9c0ee335ab8c --- /dev/null +++ b/tests/unittests/tests-registry_storage/tests-registry_storage.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef TESTS_REGISTRY_STORAGE_H +#define TESTS_REGISTRY_STORAGE_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry_storage(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_STORAGE_H */ +/** @} */ diff --git a/tests/unittests/tests-registry_storage_heap/Makefile b/tests/unittests/tests-registry_storage_heap/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry_storage_heap/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry_storage_heap/Makefile.include b/tests/unittests/tests-registry_storage_heap/Makefile.include new file mode 100644 index 000000000000..d82a4fa7f631 --- /dev/null +++ b/tests/unittests/tests-registry_storage_heap/Makefile.include @@ -0,0 +1,4 @@ +USEMODULE += registry_storage_heap +USEMODULE += registry_int_path + +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry_storage_heap/namespace b/tests/unittests/tests-registry_storage_heap/namespace new file mode 120000 index 000000000000..6a0bcd8e750e --- /dev/null +++ b/tests/unittests/tests-registry_storage_heap/namespace @@ -0,0 +1 @@ +../tests-registry/namespace \ No newline at end of file diff --git a/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.c b/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.c new file mode 100644 index 000000000000..b48b8935e017 --- /dev/null +++ b/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.c @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage_heap`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#include +#include +#include +#include +#include +#include +#include "embUnit.h" +#include "fmt.h" +#include "assert.h" +#include "registry.h" +#include "registry/storage.h" + +#include "tests-registry_storage_heap.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +#if IS_USED(MODULE_REGISTRY_STORAGE_HEAP) || IS_ACTIVE(DOXYGEN) + +static registry_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_nested_instance = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = NULL, +}; + +static registry_storage_instance_t heap_instance = { + .storage = ®istry_storage_heap, + .data = NULL, +}; + +static void test_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance); +} + +static void tests_load_and_save(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance, + .parameter = ®istry_tests_nested_group_parameter, + }, + }; + + /* set input to 8 */ + const uint8_t saved_input = 8; + + registry_set(&node, &saved_input, sizeof(saved_input)); + + /* save input to storage */ + registry_storage_save(&heap_instance, NULL); + + /* override input with the value 20 */ + const uint8_t override_input = 20; + + registry_set(&node, &override_input, sizeof(override_input)); + + /* load old value from storage */ + registry_storage_load(&heap_instance); + + /* check if the value is set back to 8 and not 20 anymore */ + registry_value_t output_value; + + registry_get(&node, &output_value); + + TEST_ASSERT_EQUAL_INT(saved_input, *(uint8_t *)output_value.buf); +} + +Test *tests_registry_storage_heap_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_load_and_save), + }; + + EMB_UNIT_TESTCALLER(registry_tests, test_setup, NULL, fixtures); + + return (Test *)®istry_tests; +} + +void tests_registry_storage_heap(void) +{ + TESTS_RUN(tests_registry_storage_heap_tests()); +} + +#endif + +/** @} */ diff --git a/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.h b/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.h new file mode 100644 index 000000000000..a3e5285384d3 --- /dev/null +++ b/tests/unittests/tests-registry_storage_heap/tests-registry_storage_heap.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage_heap`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef TESTS_REGISTRY_STORAGE_heap_H +#define TESTS_REGISTRY_STORAGE_heap_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry_storage_heap(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_STORAGE_heap_H */ +/** @} */ diff --git a/tests/unittests/tests-registry_storage_vfs/Makefile b/tests/unittests/tests-registry_storage_vfs/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry_storage_vfs/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry_storage_vfs/Makefile.include b/tests/unittests/tests-registry_storage_vfs/Makefile.include new file mode 100644 index 000000000000..838a823cc5c1 --- /dev/null +++ b/tests/unittests/tests-registry_storage_vfs/Makefile.include @@ -0,0 +1,10 @@ +CFLAGS += -DCONFIG_REGISTRY_ENABLE_META_NAME=1 + +USEMODULE += littlefs2 +USEMODULE += mtd +USEMODULE += vfs + +USEMODULE += registry_storage_vfs +USEMODULE += registry_int_path + +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry_storage_vfs/namespace b/tests/unittests/tests-registry_storage_vfs/namespace new file mode 120000 index 000000000000..6a0bcd8e750e --- /dev/null +++ b/tests/unittests/tests-registry_storage_vfs/namespace @@ -0,0 +1 @@ +../tests-registry/namespace \ No newline at end of file diff --git a/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.c b/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.c new file mode 100644 index 000000000000..978d49c75700 --- /dev/null +++ b/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.c @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage_vfs`` module + * @{ + * + * @file + * + * @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 "fs/littlefs2_fs.h" +#include "registry.h" +#include "registry/storage.h" + +#include "tests-registry_storage_vfs.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +#if IS_USED(MODULE_REGISTRY_STORAGE_VFS) || IS_ACTIVE(DOXYGEN) + +static registry_tests_nested_instance_t test_nested_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_nested_instance = { +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) + .name = "test-nested-parameter-test", +#endif /* CONFIG_REGISTRY_ENABLE_META_NAME */ + .data = &test_nested_instance_data, + .commit_cb = NULL, +}; + + +#define FS_DRIVER littlefs2_file_system +static littlefs2_desc_t fs_desc = { + .lock = MUTEX_INIT, +}; + +static vfs_mount_t _vfs_mount = { + .fs = &FS_DRIVER, + .mount_point = "/sda", + .private_data = &fs_desc, +}; + +static registry_storage_instance_t vfs_instance = { + .storage = ®istry_storage_vfs, + .data = &_vfs_mount, +}; + +static void test_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_nested_instance); + + /* init storage */ + #ifdef MTD_0 + fs_desc.dev = MTD_0; + #endif +} + +static void tests_load_and_save(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_nested_instance, + ®istry_tests_nested_group_parameter, + }, + }; + + /* set input to 8 */ + const uint8_t saved_input = 8; + + registry_set(&node, &saved_input, sizeof(saved_input)); + + /* save input to storage */ + registry_storage_save(&vfs_instance, NULL); + + /* override input with the value 20 */ + const uint8_t override_input = 20; + + registry_set(&node, &override_input, sizeof(override_input)); + + /* load old value from storage */ + registry_storage_load(&vfs_instance); + + /* check if the value is set back to 8 and not 20 anymore */ + registry_value_t output_value; + + registry_get(&node, &output_value); + + TEST_ASSERT_EQUAL_INT(saved_input, *(uint8_t *)output_value.buf); +} + +Test *tests_registry_storage_vfs_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(tests_load_and_save), + }; + + EMB_UNIT_TESTCALLER(registry_tests, test_setup, NULL, fixtures); + + return (Test *)®istry_tests; +} + +void tests_registry_storage_vfs(void) +{ + TESTS_RUN(tests_registry_storage_vfs_tests()); +} + +#endif + +/** @} */ diff --git a/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.h b/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.h new file mode 100644 index 000000000000..0867df39c6c4 --- /dev/null +++ b/tests/unittests/tests-registry_storage_vfs/tests-registry_storage_vfs.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_storage_vfs`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef TESTS_REGISTRY_STORAGE_VFS_H +#define TESTS_REGISTRY_STORAGE_VFS_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry_storage_vfs(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_STORAGE_VFS_H */ +/** @} */ diff --git a/tests/unittests/tests-registry_string_path/Makefile b/tests/unittests/tests-registry_string_path/Makefile new file mode 100644 index 000000000000..e90483f4f15a --- /dev/null +++ b/tests/unittests/tests-registry_string_path/Makefile @@ -0,0 +1,3 @@ +DIRS += namespace + +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-registry_string_path/Makefile.include b/tests/unittests/tests-registry_string_path/Makefile.include new file mode 100644 index 000000000000..385bacd9e2ea --- /dev/null +++ b/tests/unittests/tests-registry_string_path/Makefile.include @@ -0,0 +1,3 @@ +USEMODULE += registry_string_path + +USEMODULE += tests-registry-namespace diff --git a/tests/unittests/tests-registry_string_path/namespace b/tests/unittests/tests-registry_string_path/namespace new file mode 120000 index 000000000000..6a0bcd8e750e --- /dev/null +++ b/tests/unittests/tests-registry_string_path/namespace @@ -0,0 +1 @@ +../tests-registry/namespace \ No newline at end of file diff --git a/tests/unittests/tests-registry_string_path/tests-registry_string_path.c b/tests/unittests/tests-registry_string_path/tests-registry_string_path.c new file mode 100644 index 000000000000..f1110e4c3ffd --- /dev/null +++ b/tests/unittests/tests-registry_string_path/tests-registry_string_path.c @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_string_path`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#include "embUnit/embUnit.h" +#include "tests-registry_string_path.h" + +#include +#include +#include +#include +#include +#include +#include "embUnit.h" +#include "kernel_defines.h" +#include "fmt.h" +#include "assert.h" +#include "vfs.h" +#include "board.h" +#include "mtd.h" +#include "registry.h" +#include "registry/string_path.h" +#include "namespace/tests.h" +#include "namespace/tests/nested.h" + +#if IS_ACTIVE(CONFIG_REGISTRY_ENABLE_META_NAME) || IS_ACTIVE(DOXYGEN) + +static registry_tests_nested_instance_t test_instance_data = { + .parameter = 9, + .group_parameter = 5, +}; + +static registry_instance_t test_instance = { + .name = "instance-1", + .data = &test_instance_data, + .commit_cb = NULL, +}; + +static void test_setup(void) +{ + /* init registry */ + registry_init(); + + /* add schema instances */ + registry_add_schema_instance(®istry_tests_nested, &test_instance); +} + +/* to string_path */ +static void tests_registry_to_parameter_string_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_PARAMETER, + .value.parameter = { + .instance = &test_instance, + .parameter = ®istry_tests_nested_group_parameter, + }, + }; + + int size = registry_node_to_string_path(&node, NULL); + char path[size + 1]; + + registry_node_to_string_path(&node, path); + + TEST_ASSERT_EQUAL_STRING("/tests/nested/instance-1/group/parameter", path); +} + +static void tests_registry_to_group_string_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_GROUP, + .value.group = { + .instance = &test_instance, + .group = ®istry_tests_nested_group, + }, + }; + + int size = registry_node_to_string_path(&node, NULL); + char path[size + 1]; + + registry_node_to_string_path(&node, path); + + TEST_ASSERT_EQUAL_STRING("/tests/nested/instance-1/group", path); +} + +static void tests_registry_to_instance_string_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_INSTANCE, + .value.instance = &test_instance, + }; + + int size = registry_node_to_string_path(&node, NULL); + char path[size + 1]; + + registry_node_to_string_path(&node, path); + + TEST_ASSERT_EQUAL_STRING("/tests/nested/instance-1", path); +} + +static void tests_registry_to_schema_string_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_SCHEMA, + .value.schema = ®istry_tests_nested, + }; + + int size = registry_node_to_string_path(&node, NULL); + char path[size + 1]; + + registry_node_to_string_path(&node, path); + + TEST_ASSERT_EQUAL_STRING("/tests/nested", path); +} + +static void tests_registry_to_namespace_string_path(void) +{ + registry_node_t node = { + .type = REGISTRY_NODE_NAMESPACE, + .value.namespace = ®istry_tests, + }; + + int size = registry_node_to_string_path(&node, NULL); + char path[size + 1]; + + registry_node_to_string_path(&node, path); + + TEST_ASSERT_EQUAL_STRING("/tests", path); +} + +/* from string_path */ +static void tests_registry_from_parameter_string_path(void) +{ + registry_node_t node; + + const char *str[] = { "tests", "nested", "instance-1", "group", "parameter" }; + + registry_node_from_string_path(str, ARRAY_SIZE(str), &node); + + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_PARAMETER, node.type); + TEST_ASSERT_EQUAL_STRING("tests", node.value.parameter.parameter->schema->namespace->name); + TEST_ASSERT_EQUAL_STRING("nested", node.value.parameter.parameter->schema->name); + TEST_ASSERT_EQUAL_STRING("instance-1", node.value.parameter.instance->name); + TEST_ASSERT_EQUAL_STRING("parameter", node.value.parameter.parameter->name); +} + +static void tests_registry_from_group_string_path(void) +{ + registry_node_t node; + + const char *str[] = { "tests", "nested", "instance-1", "group" }; + + registry_node_from_string_path(str, ARRAY_SIZE(str), &node); + + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_GROUP, node.type); + TEST_ASSERT_EQUAL_STRING("tests", node.value.group.group->schema->namespace->name); + TEST_ASSERT_EQUAL_STRING("nested", node.value.group.group->schema->name); + TEST_ASSERT_EQUAL_STRING("instance-1", node.value.group.instance->name); + TEST_ASSERT_EQUAL_STRING("group", node.value.group.group->name); +} + +static void tests_registry_from_instance_string_path(void) +{ + registry_node_t node; + + const char *str[] = { "tests", "nested", "instance-1" }; + + registry_node_from_string_path(str, ARRAY_SIZE(str), &node); + + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_INSTANCE, node.type); + TEST_ASSERT_EQUAL_STRING("tests", node.value.instance->schema->namespace->name); + TEST_ASSERT_EQUAL_STRING("nested", node.value.instance->schema->name); + TEST_ASSERT_EQUAL_STRING("instance-1", node.value.instance->name); +} + +static void tests_registry_from_schema_string_path(void) +{ + registry_node_t node; + + const char *str[] = { "tests", "nested" }; + + registry_node_from_string_path(str, ARRAY_SIZE(str), &node); + + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_SCHEMA, node.type); + TEST_ASSERT_EQUAL_STRING("tests", node.value.schema->namespace->name); + TEST_ASSERT_EQUAL_STRING("nested", node.value.schema->name); +} + +static void tests_registry_from_namespace_string_path(void) +{ + registry_node_t node; + + const char *str[] = { "tests" }; + + registry_node_from_string_path(str, ARRAY_SIZE(str), &node); + + TEST_ASSERT_EQUAL_INT(REGISTRY_NODE_NAMESPACE, node.type); + TEST_ASSERT_EQUAL_STRING("tests", node.value.namespace->name); +} + +Test *tests_registry_string_path_tests(void) +{ + (void)tests_registry_to_parameter_string_path; + (void)tests_registry_to_group_string_path; + (void)tests_registry_to_instance_string_path; + (void)tests_registry_to_schema_string_path; + (void)tests_registry_to_namespace_string_path; + (void)tests_registry_from_parameter_string_path; + (void)tests_registry_from_group_string_path; + (void)tests_registry_from_instance_string_path; + (void)tests_registry_from_schema_string_path; + (void)tests_registry_from_namespace_string_path; + + EMB_UNIT_TESTFIXTURES(fixtures) { + /* to string_path */ + new_TestFixture(tests_registry_to_parameter_string_path), + new_TestFixture(tests_registry_to_group_string_path), + new_TestFixture(tests_registry_to_instance_string_path), + new_TestFixture(tests_registry_to_schema_string_path), + new_TestFixture(tests_registry_to_namespace_string_path), + // /* from string_path */ + new_TestFixture(tests_registry_from_parameter_string_path), + new_TestFixture(tests_registry_from_group_string_path), + new_TestFixture(tests_registry_from_instance_string_path), + new_TestFixture(tests_registry_from_schema_string_path), + new_TestFixture(tests_registry_from_namespace_string_path), + }; + + EMB_UNIT_TESTCALLER(registry_string_path_tests, test_setup, NULL, fixtures); + + return (Test *)®istry_string_path_tests; +} + +void tests_registry_string_path(void) +{ + TESTS_RUN(tests_registry_string_path_tests()); +} + +#endif + +/** @} */ diff --git a/tests/unittests/tests-registry_string_path/tests-registry_string_path.h b/tests/unittests/tests-registry_string_path/tests-registry_string_path.h new file mode 100644 index 000000000000..a419d2e33a29 --- /dev/null +++ b/tests/unittests/tests-registry_string_path/tests-registry_string_path.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2023 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup unittests + * @brief Unittests for the ``registry_string_path`` module + * @{ + * + * @file + * + * @author Lasse Rosenow + */ + +#ifndef TESTS_REGISTRY_STRING_PATH_H +#define TESTS_REGISTRY_STRING_PATH_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_registry_string_path(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_REGISTRY_STRING_PATH_H */ +/** @} */