Restructure config module for easier addition of items

This commit is contained in:
Camden Dixie O'Brien 2023-05-15 11:23:07 +01:00
parent 10c77203ab
commit d7f1c2ace0
2 changed files with 93 additions and 68 deletions

View File

@ -15,28 +15,28 @@
#define TAG "Config" #define TAG "Config"
#define NAMESPACE "config" #define NAMESPACE "config"
#define MAX_CONSUMERS 8 #define MAX_CALLBACKS 8
#define DEFAULT_HOSTNAME "bedside-clock" typedef enum {
#define HOSTNAME_KEY "hostname" HOSTNAME,
ITEM_COUNT,
} ItemIndex;
typedef struct { typedef struct {
char hostname[MAX_HOSTNAME_SIZE]; const char *id;
} Config; const char *default_value;
char value[CONFIG_MAX_VALUE_SIZE];
typedef struct { struct {
ConfigStringCallback callbacks[MAX_CONSUMERS]; ConfigCallback funcs[MAX_CALLBACKS];
unsigned count; unsigned count;
} StringConsumers; } callbacks;
} Item;
typedef struct { static Item state[ITEM_COUNT] = {
Config config; [HOSTNAME] = { .id = "hostname", .default_value = "bedside-clock" },
StringConsumers hostname_consumers; };
} State;
static State state; static bool load(ItemIndex item)
static bool load_hostname()
{ {
esp_err_t error; esp_err_t error;
@ -49,10 +49,12 @@ static bool load_hostname()
return false; return false;
} }
size_t size = MAX_HOSTNAME_SIZE; size_t size = CONFIG_MAX_VALUE_SIZE;
error = nvs_get_str(handle, HOSTNAME_KEY, state.config.hostname, &size); error = nvs_get_str(handle, state[item].id, state[item].value, &size);
if (error != ESP_OK) { if (error != ESP_OK) {
ESP_LOGE(TAG, "Error loading hostname from storage: %04x", error); ESP_LOGE(
TAG, "Error loading %s from storage: %04x", state[item].id,
error);
nvs_close(handle); nvs_close(handle);
return false; return false;
} }
@ -61,7 +63,7 @@ static bool load_hostname()
return true; return true;
} }
static void save_hostname() static void save(ItemIndex item)
{ {
esp_err_t error; esp_err_t error;
@ -74,9 +76,11 @@ static void save_hostname()
return; return;
} }
error = nvs_set_str(handle, HOSTNAME_KEY, state.config.hostname); error = nvs_set_str(handle, state[item].id, state[item].value);
if (error != ESP_OK) { if (error != ESP_OK) {
ESP_LOGE(TAG, "Error loading hostname from storage: %04x", error); ESP_LOGE(
TAG, "Error loading %s from storage: %04x", state[item].id,
error);
nvs_close(handle); nvs_close(handle);
return; return;
} }
@ -88,6 +92,58 @@ static void save_hostname()
nvs_close(handle); nvs_close(handle);
} }
static void set(ItemIndex item, const char *value)
{
if (value == NULL) {
ESP_LOGW(
TAG, "Attempt to set %s to null pointer; ignored",
state[item].id);
return;
}
size_t len = strlen(value);
if (len >= CONFIG_MAX_VALUE_SIZE) {
ESP_LOGW(
TAG, "%s value \"%s\" exceeds maximum size; truncated",
state[item].id, value);
len = CONFIG_MAX_VALUE_SIZE - 1;
}
memcpy(state[item].value, value, len);
state[item].value[len] = '\0';
save(item);
for (unsigned i = 0; i < state[item].callbacks.count; ++i)
state[item].callbacks.funcs[i](state[item].value);
}
static size_t get(ItemIndex item, char *buffer, size_t buffer_size)
{
size_t len = strlen(state[item].value);
if (len < buffer_size) {
memcpy(buffer, state[item].value, len);
buffer[len] = '\0';
}
return len;
}
static void add_callback(ItemIndex item, ConfigCallback callback)
{
if (callback == NULL) {
ESP_LOGW(
TAG, "Attempt to add null callback for %s; ignored",
state[item].id);
} else if (state[item].callbacks.count >= MAX_CALLBACKS) {
ESP_LOGE(
TAG, "Max callbacks exceeded for %s; callback discarded",
state[item].id);
} else {
const unsigned pos = state[item].callbacks.count;
state[item].callbacks.funcs[pos] = callback;
++state[item].callbacks.count;
}
}
void config_init() void config_init()
{ {
esp_err_t error; esp_err_t error;
@ -104,56 +160,25 @@ void config_init()
FATAL(); FATAL();
} }
memset(&state, 0, sizeof(state)); for (ItemIndex item = (ItemIndex)0; item < ITEM_COUNT; ++item) {
if (!load(item)) {
if (!load_hostname()) { set(item, state[item].default_value);
config_set_hostname(DEFAULT_HOSTNAME); save(item);
save_hostname(); }
} }
} }
void config_set_hostname(const char *hostname) void config_set_hostname(const char *hostname)
{ {
if (hostname == NULL) { set(HOSTNAME, hostname);
ESP_LOGW(TAG, "Null pointer passed to %s(); ignored", __func__);
return;
}
size_t len = strlen(hostname);
if (len >= MAX_HOSTNAME_SIZE) {
ESP_LOGW(
TAG, "Hostname \"%s\" exceeds maximum size; truncated",
hostname);
len = MAX_HOSTNAME_SIZE - 1;
}
memcpy(state.config.hostname, hostname, len);
state.config.hostname[len] = '\0';
save_hostname();
for (unsigned i = 0; i < state.hostname_consumers.count; ++i)
state.hostname_consumers.callbacks[i](state.config.hostname);
} }
size_t config_get_hostname(char *buffer, size_t buffer_size) size_t config_get_hostname(char *buffer, size_t buffer_size)
{ {
size_t len = strlen(state.config.hostname); return get(HOSTNAME, buffer, buffer_size);
if (len < buffer_size) {
memcpy(buffer, state.config.hostname, len);
buffer[len] = '\0';
}
return len;
} }
void config_add_hostname_consumer(ConfigStringCallback callback) void config_add_hostname_callback(ConfigCallback callback)
{ {
if (callback == NULL) { add_callback(HOSTNAME, callback);
} else if (state.hostname_consumers.count >= MAX_CONSUMERS) {
ESP_LOGE(
TAG, "Max consumers exceeded for hostname; callback discarded");
} else {
state.hostname_consumers.callbacks[state.hostname_consumers.count]
= callback;
++state.hostname_consumers.count;
}
} }

View File

@ -8,12 +8,12 @@
#include <stddef.h> #include <stddef.h>
#define MAX_HOSTNAME_SIZE 32 #define CONFIG_MAX_VALUE_SIZE 32U
/** /**
* Callback type for consumers of string settings. * Callback type for config updates
*/ */
typedef void (*ConfigStringCallback)(const char *value); typedef void (*ConfigCallback)(const char *value);
/** /**
* Initialize the configuration module. * Initialize the configuration module.
@ -21,7 +21,7 @@ typedef void (*ConfigStringCallback)(const char *value);
* If there is a saved configuration, it will be loaded. Otherwise, * If there is a saved configuration, it will be loaded. Otherwise,
* the default configuration will be loaded and saved. * the default configuration will be loaded and saved.
*/ */
void config_init(); void config_init(void);
/** /**
* Set the device's hostname. * Set the device's hostname.
@ -49,6 +49,6 @@ size_t config_get_hostname(char *buffer, size_t buffer_size);
* lifetime of the passed argument will be static, but the value may * lifetime of the passed argument will be static, but the value may
* be modified once the callback returns. * be modified once the callback returns.
*/ */
void config_add_hostname_callback(ConfigStringCallback callback); void config_add_hostname_callback(ConfigCallback callback);
#endif #endif