Add Settings Utility and example app

This commit is contained in:
TimJTi 2023-11-20 17:56:54 +00:00 committed by Xiang Xiao
parent 8669861312
commit 169beaeef7
13 changed files with 3327 additions and 0 deletions

61
examples/settings/Kconfig Normal file
View File

@ -0,0 +1,61 @@
#
# For a description of the syntax of this configuration file,
# see the file kconfig-language.txt in the NuttX tools repository.
#
config EXAMPLES_SETTINGS
tristate "Settings example"
default n
---help---
Enable the "settings" example. This uses the functions provided in
system/settings to test the storage and retrieval of various
configurable parameters
if EXAMPLES_SETTINGS
config EXAMPLES_SETTINGS_PROGNAME
string "Program name"
default "settings"
---help---
This is the name of the program that will be used when the NSH ELF
program is installed.
config EXAMPLES_SETTINGS_PRIORITY
int "settings task priority"
default 100
config EXAMPLES_SETTINGS_STACKSIZE
int "settings stack size"
default DEFAULT_TASK_STACKSIZE
choice
prompt "Select settings storage location"
default EXAMPLES_SETTINGS_USE_TMPFS
config EXAMPLES_SETTINGS_USE_TMPFS
bool "Use TMPFS"
select FS_TMPFS
---help---
TMPFS will be enabled and used
config EXAMPLES_SETTINGS_USE_OTHER
bool "Use existing/other storage media"
endchoice # Select settings storage location
if EXAMPLES_SETTINGS_USE_OTHER
config EXAMPLES_SETTINGS_EXISTING_STORAGE
string "Path to existing storage media"
default "/mnt/mmcsd0"
endif # EXAMPLES_SETTINGS_USE_OTHER
config EXAMPLES_SETTINGS_FILENAME
string "Filename"
default "settings"
---help---
filename to be used. This append will append .txt and .bin
to this name to use/create two settings files.
endif # EXAMPLES_SETTINGS

View File

@ -0,0 +1,23 @@
############################################################################
# apps/examples/settings/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_EXAMPLES_SETTINGS),)
CONFIGURED_APPS += $(APPDIR)/examples/settings
endif

View File

@ -0,0 +1,34 @@
############################################################################
# apps/examples/settings/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
# settings built-in application info
PROGNAME = $(CONFIG_EXAMPLES_SETTINGS_PROGNAME)
PRIORITY = $(CONFIG_EXAMPLES_SETTINGS_PRIORITY)
STACKSIZE = $(CONFIG_EXAMPLES_SETTINGS_STACKSIZE)
MODULE = $(CONFIG_EXAMPLES_SETTINGS)
# Settings Example
MAINSRC = settings_main.c
include $(APPDIR)/Application.mk

View File

@ -0,0 +1,301 @@
/****************************************************************************
* apps/examples/settings/settings_main.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/mount.h>
#include <sys/param.h>
#include "system/settings.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* settings_main
****************************************************************************/
int settings_main(int argc, FAR char *argv[])
{
int ret;
int fd;
enum settings_type_e stype;
char text_path[CONFIG_PATH_MAX];
char bin_path[CONFIG_PATH_MAX];
const char teststr[] = "I'm a string";
int testval = 0x5aa5;
char readstr[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE];
#ifdef CONFIG_EXAMPLES_SETTINGS_USE_TMPFS
struct stat sbuf;
# ifndef CONFIG_FS_TMPFS
# error TMPFS must be enabled
# endif
# ifndef CONFIG_LIBC_TMPDIR
# error LIBC_TMPDIR must be defined
# endif
if (stat(CONFIG_LIBC_TMPDIR, &sbuf))
{
ret = nx_mount(NULL, CONFIG_LIBC_TMPDIR, "tmpfs", 0, NULL);
if (ret < 0)
{
printf("ERROR: Failed to mount tmpfs at %s: %d\n",
CONFIG_LIBC_TMPDIR, ret);
goto end;
}
}
strcpy(bin_path, CONFIG_LIBC_TMPDIR);
#else
strcpy(bin_path, CONFIG_EXAMPLES_SETTINGS_EXISTING_STORAGE);
if (bin_path == NULL)
{
printf("Settings filepath is empty!");
goto end;
}
#endif
strcat(bin_path, "/");
strcat(bin_path, CONFIG_EXAMPLES_SETTINGS_FILENAME);
strcpy(text_path, bin_path);
strcat(bin_path, ".bin");
strcat(text_path, ".txt");
printf("Example of settings usage to file: %s and %s:",
bin_path, text_path);
printf("--------------------------------------------------------------\n");
settings_init();
ret = settings_setstorage(bin_path, STORAGE_BINARY);
if (ret == -ENOENT)
{
printf("No existing binary storage file found. Creating it.\n");
fd = open(bin_path, O_CREAT);
if (fd < 0)
{
printf("Failed to create settings file\n");
goto end;
}
close(fd);
}
else if (ret < 0)
{
printf("settings setstorage failed: %d\n", ret);
goto end;
}
else
{
printf("existing bin settings storage file found\n");
}
ret = settings_setstorage(text_path, STORAGE_TEXT);
if (ret == -ENOENT)
{
printf("No existing text storage file found. Creating it.\n");
fd = open(text_path, O_CREAT);
if (fd < 0)
{
printf("Failed to create settings file\n");
goto end;
}
close(fd);
}
else if (ret < 0)
{
printf("settings setstorage failed: %d\n", ret);
goto end;
}
else
{
printf("existing text settings storage file found\n");
}
ret = settings_create("v1", SETTING_STRING, "default value");
if (ret < 0)
{
printf("settings create failed: %d\n", ret);
goto end;
}
/* if this app has been run before, the setting type is likely changed */
ret = settings_type("v1", &stype);
if (ret < 0)
{
printf("Failed to get settings type: %d\n", ret);
goto end;
}
if (stype == SETTING_STRING)
{
ret = settings_get("v1", SETTING_STRING, &readstr, sizeof(readstr));
if (ret < 0)
{
printf("settings retrieve failed: %d\n", ret);
goto end;
}
printf("Retrieved settings value (v1) with value:%s\n", readstr);
}
else
{
ret = settings_get("v1", SETTING_INT, &testval,
CONFIG_SYSTEM_SETTINGS_VALUE_SIZE);
if (ret < 0)
{
printf("settings retrieve failed: %d\n", ret);
goto end;
}
printf("Retrieved settings value (v1) with value:%d\n", testval);
}
printf("Trying to (re)create a setting that already exists (v1)\n");
testval = 0xa5a5;
ret = settings_create("v1", SETTING_INT, testval);
if (ret == -EACCES)
{
printf("Deliberate fail: setting exists! Error: %d\n", ret);
}
else if (ret < 0)
{
printf("settings create failed: %d\n", ret);
goto end;
}
ret = settings_type("v1", &stype);
if (ret < 0)
{
printf("failed to read settings type: %d\n", ret);
goto end;
}
printf("Retrieved setting type is: %d\n", stype);
printf("Trying to change setting (v1) to integer type\n");
ret = settings_set("v1", SETTING_INT, &testval);
if (ret < 0)
{
printf("Deliberate fail: settings change invalid: %d\n", ret);
}
ret = settings_get("v2", SETTING_INT, &testval);
if (ret < 0)
{
printf("Deliberate fail: non-existent setting requested. Error:%d\n",
ret);
}
printf("Trying to change setting (v1) from int to string: %s\n", teststr);
ret = settings_set("v1", SETTING_STRING, &teststr);
if (ret < 0)
{
printf("Deliberate fail: settings change invalid: %d\n", ret);
}
printf("Creating a string settings value (s1):%s\n", teststr);
ret = settings_create("s1", SETTING_STRING, teststr);
if (ret < 0)
{
printf("settings changed failed: %d\n", ret);
goto end;
}
ret = settings_get("s1", SETTING_STRING, &readstr, sizeof(readstr));
if (ret < 0)
{
printf("settings retrieve failed: %d\n", ret);
goto end;
}
printf("Retrieved string settings value (s1) with value:%s\n",
readstr);
FAR struct in_addr save_ip;
FAR struct in_addr load_ip;
save_ip.s_addr = 0xc0a86401;
printf("Changing setting to an IP value (s1) with value:%d.%d.%d.%d\n",
(int)(save_ip.s_addr >> 24) & 0xff,
(int)(save_ip.s_addr >> 16) & 0xff,
(int)(save_ip.s_addr >> 8) & 0xff,
(int)(save_ip.s_addr >> 0) & 0xff);
ret = settings_set("s1", SETTING_IP_ADDR, &save_ip);
if (ret < 0)
{
printf("IP address settings create failed: %d\n", ret);
goto end;
}
ret = settings_get("s1", SETTING_IP_ADDR, &load_ip);
if (ret < 0)
{
printf("IP address settings retrieve failed: %d\n", ret);
goto end;
}
printf("Retrieved IP address settings value (s1) with value:0x%08lx\n",
load_ip.s_addr);
printf("syncing storages\n");
ret = settings_sync(true); /* wait for cached saves to complete
* (will only be done if caching enabled).
*/
if (ret < 0)
{
printf("Failed to sync storages: %d\n", ret);
goto end;
}
end:
printf("exiting settings example app\n");
fflush(stdout);
return ret;
}

306
include/system/settings.h Normal file
View File

@ -0,0 +1,306 @@
/****************************************************************************
* apps/include/system/settings.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef UTILS_SETTINGS_H_
#define UTILS_SETTINGS_H_
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <netinet/in.h>
#include <stdint.h>
#include <sys/types.h>
#include <pthread.h>
#include <signal.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define VALID 0x600d /* "Magic" number to confirm storage is OK */
/****************************************************************************
* Public Types
****************************************************************************/
enum settings_type_e
{
SETTING_EMPTY = 0,
SETTING_INT, /* INT32 */
SETTING_BOOL,
SETTING_FLOAT,
SETTING_STRING,
SETTING_IP_ADDR,
};
typedef struct
{
char key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE];
enum settings_type_e type;
union
{
int i;
double f;
char s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE];
struct in_addr ip;
} val;
} setting_t;
typedef struct
{
char file[CONFIG_SYSTEM_SETTINGS_MAX_FILENAME];
int (*load_fn)(FAR char *file);
int (*save_fn)(FAR char *file);
} storage_t;
struct notify_s
{
pid_t pid;
uint8_t signo;
};
enum storage_type_e
{
STORAGE_BINARY = 0,
STORAGE_TEXT,
};
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: settings_init
*
* Description:
* Initializes the settings storage.
*
* Input Parameters: none
*
* Returned Value: none
*
****************************************************************************/
void settings_init(void);
/****************************************************************************
* Name: settings_setstorage
*
* Description:
* Sets a file to be used as a settings storage.
* Except from the first file, if loading the file causes any changes
* to the settings, then the new map will be dumped to all files
* (effectively it syncs all storages).
*
* Input Parameters:
* file - the filename of the storage to use
* type - the type of the storage (BINARY or TEXT)
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_setstorage(FAR char *file, enum storage_type_e type);
/****************************************************************************
* Name: settings_sync
*
* Description:
* Synchronizes the storage.
*
* wait_dump - if cached saves are enabled, this determines whether
* the function will wait until the save is actually
* completed or just schedule a new save
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_sync(bool wait_dump);
/****************************************************************************
* Name: settings_notify
*
* Description:
* Registers a task to be notified on any change of the settings.
* Whenever any value is changed, a signal will be sent to all
* registered threads. Signals are NOT sent when new settings are
* created or when the whole storage is cleared.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_notify(void);
/****************************************************************************
* Name: settings_hash
*
* Description:
* Gets the hash of the settings storage.
*
* This hash represents the internal state of the settings map. A
* unique number is calculated based on the contents of the whole map.
*
* This hash can be used to check the settings for any alterations, i.e.
* any setting that may had its value changed since last check.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_hash(FAR uint32_t *h);
/****************************************************************************
* Name: settings_clear
*
* Description:
* Clears all settings.
* Data in all storages are purged.
*
* Note that if the settings are cleared during the application run-time
* (i.e. not during initialization), every access to the settings storage
* will fail.
*
* All settings must be created again. This can be done either by calling
* Settings_create() again, or by restarting the application.
*
* Input Parameters:
* none
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_clear(void);
/****************************************************************************
* Name: settings_create
*
* Description:
* Creates a new setting.
*
* If the setting is found to exist in any of the storages, it will
* be loaded. Else, it will be created and the default value will be
* assigned.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting.
* ... - the default value of the setting.
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_create(FAR char *key, enum settings_type_e type, ...);
/****************************************************************************
* Name: settings_type
*
* Description:
* Gets the type of a setting.
*
* Input Parameters:
* key - the key of the setting.
* type = pointer to int for the returned setting type
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_type(FAR char *key, FAR enum settings_type_e *stype);
/****************************************************************************
* Name: settings_get
*
* Description:
* Gets the value of a setting.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting
* ... - pointer to store the setting value.
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_get(FAR char *key, FAR enum settings_type_e type, ...);
/****************************************************************************
* Name: settings_set
*
* Description:
* Sets the value of a setting.
*
* Input Parameters:
* key - the key of the setting.
* type - the type of the setting
* ... - the new value of the setting.
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_set(FAR char *key, FAR enum settings_type_e type, ...);
/****************************************************************************
* Name: settings_iterate
*
* Description:
* Gets a copy of a setting at the specified position. It can be used to
* iterate over the settings map, by using successive values of idx.
*
* Input Parameters:
* idx - the iteration index for the setting.
* setting - pointer to return the setting value
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int settings_iterate(int idx, FAR setting_t *setting);
#endif /* UTILS_SETTINGS_H_ */

85
system/settings/Kconfig Normal file
View File

@ -0,0 +1,85 @@
#
# Settings Storage Configuration
#
config SYSTEM_SETTINGS
bool "Settings Functions"
default n
---help---
The settings storage can be used to store and retrieve various
configurable parameters. This storage is a RAM-based map (for fast access)
but one or more files can be used too (so values can persist across system
reboots).
if SYSTEM_SETTINGS
config SYSTEM_SETTINGS_MAP_SIZE
int "Settings storage size"
default 20
---help---
The size of the settings storage, i.e. the
maximum number of settings that can be created.
config SYSTEM_SETTINGS_KEY_SIZE
int "Settings key size"
default 20
---help---
Maximum size of a setting key.
config SYSTEM_SETTINGS_VALUE_SIZE
int "Settings value size"
default 20
---help---
Maximum size of a string setting value.
config SYSTEM_SETTINGS_MAX_STORAGES
int "Settings files"
default 2
---help---
Maximum number of supported settings files.
config SYSTEM_SETTINGS_MAX_FILENAME
int "Settings filename size"
default 50
---help---
Maximum size of settings filename.
config SYSTEM_SETTINGS_CACHED_SAVES
bool "Cache save operations"
default y
---help---
If enabled saving to the storages will be cached.
Whenever a save is triggered, the settings will only
be saved in the RAM map (and will be immediately
effective). Writes to the storages though will
happen after a small delay.
This improves performance greatly when multiple
successive saves are to be performed, as the actual
write will happen only once, asynchronously.
if SYSTEM_SETTINGS_CACHED_SAVES
config SYSTEM_SETTINGS_CACHE_TIME_MS
int "Cache Delay (ms)"
default 100
---help---
Sets the delay after a setting is changed before they are written
endif # SYSTEM_SETTINGS_CACHED_SAVES
config SYSTEM_SETTINGS_MAX_SIGNALS
int "Max. settings signals"
default 2
---help---
Maximum number of signals that can be registered
on the settings storage.
config SYSTEM_SETTINGS_SIGNO
int "Signal Number to use"
default 32
range 32 63
---help---
The Signal number to use for notifies
endif # SYSTEM_SETTINGS

24
system/settings/Make.defs Normal file
View File

@ -0,0 +1,24 @@
############################################################################
# apps/system/settings/Make.defs
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
ifneq ($(CONFIG_SYSTEM_SETTINGS),)
CONFIGURED_APPS += $(APPDIR)/system/settings
endif

28
system/settings/Makefile Normal file
View File

@ -0,0 +1,28 @@
############################################################################
# apps/system/settings/Makefile
#
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership. The
# ASF licenses this file to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance with the
# License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
############################################################################
include $(APPDIR)/Make.defs
ifneq ($CONFIG_SYSTEM_UTILS_SETTINGS,)
CSRCS += settings.c storage_bin.c storage_text.c
endif
include $(APPDIR)/Application.mk

94
system/settings/readme.md Normal file
View File

@ -0,0 +1,94 @@
# Introduction
The settings storage can be used to store and retrieve various configurable parameters. This storage is a RAM-based map (for fast access), but one or more files can be used too (so values can persist across system reboots).
This is a thread-safe implementation. Different threads may access the settings simultaneously.
# Setting Definition
Each setting is a key/value pair.
The key is always a string, while there are many value types supported.
## Strings
All strings, whether they represent a key or a string value, must use a specific format. They must be ASCII, NULL-terminated strings, and they are always case-sensitive. They must obey to the following rules:
- they cannot start with a number.
- they cannot contain the characters '=', ';'.
- they cannot contain the characters '\n', '\r'.
## Setting Type
Since each setting has its own type, it is the user's responsibility to access the setting using the correct type. Some "casts" are possible (e.g. between bool and int), but generally reading with the wrong type will lead to failure.
The following types are currently supported.
- String.
- int
- bool
- ip address
- float
### Setting Storage Size
Kconfig is used to determine the size of the various fields used:
- <code>CONFIG_SYSTEM_SETTINGS_MAP_SIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>the total number settings allowed
- <code>CONFIG_SYSTEM_SETTINGS_MAX_STORAGES&nbsp;</code>the number of storage files that can be used
- <code>CONFIG_SYSTEM_SETTINGS_VALUE_SIZE&nbsp;&nbsp;&nbsp;</code>the storage size of a STRING value
- <code>CONFIG_SYSTEM_SETTINGS_KEY_SIZE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</code>the size of the KEY field
- <code>CONFIG_SYSTEM_SETTINGS_MAX_FILENAME&nbsp;</code>the maximum filename size
# Signal
A POSIX signal can be chosen via Kconfig and is used to send notifications when a setting has been changed. <CONFIG_SYSTEM_SETTINGS_MAX_SIGNALS> is used to determine the maximum number of signals that can be registered for the settings storage functions.
# Files
There are also various types of files that can be used. Each file type is using a different data format to parse the stored data. Every type is expected to be best suited for a specific file system or medium type, or be better performing while used with external systems. In any case, all configured files are automatically synchronized whenever a value changes.
## Cached Saves
It is possible to enable cached saves, via Kconfig, along with the time (in ms) that should elapse after the last storage value was updated before a write to the file or files should be initiated.
## Storage Types
The current storage types are defined in <enum storage_type_e> and are as follows.
### STORAGE_BINARY
Data is stored as "raw" binary. Bool, int and float data are stored using the maximum number of bytes needed, as determined by the size of the union used for these settings; this will be architecture dependent.
### STORAGE_TEXT
All data is converted to ASCII characters making the storage easily human-readable.
# Usage
## Most common
This is the usual sequence of calls to use the settings utility.
1. Call <code>settings_init();</code>
2. Call <code>settings_setstorage("path", storage_type);</code> for every/any file storage in use
- for example <code>settings_setstorage("/dev/eeprom", STORAGE_EPROM);</code>
3. Call <code>settings_create(key_name, settings_type, default value)</code> for every setting required
4. <code>settings_notify()</code>.
- for example <code>settings_create("KEY1", SETTINGS_STRING, "Hello");</code>
5. If a settings value needs to be read, call the variadic function <code>settings_get(key_name, settings_type, &variable, size);</code>. "size" is only needed for STRING types.
- for example <code>settings_get("KEY1", SETTINGS_STRING, &read_str, sizeof(readstr));</code>
6. If a settings value needs to be changed, call the variadic function <code>settings_set(key_name, settings_type, &value, size);</code> "size" is only needed for STRING types.
- for example <code>settings_set("KEY1", SETTINGS_STRING, &new_string);</code>
## Other functions
Other functions exist to assist the user program.
1. <code>settings_sync(wait_dump)</code>. This will synchronise the settings between file storages if more than 1 are in use. if cached saves are enabled, the <code>wait_dump</code> parameter, if true, will force the sync function to wait until any saves are actually written out.
2. <code>settings_iterate(idx, &setting)</code>. This gets a copy of a setting at the specified position. It can be used to iterate over the entire settings map, by using successive values of idx.
3. <code>settings_type(key_name, &type)</code>. Gets the type of a given setting.
4. <code>settings_clear()</code>. Clears all settings and sata in all storages is purged.
5. <code>settings_hash(&hash)</code>. Gets the hash of the settings storage. This hash represents the internal state of the settings map. A unique number is calculated based on the contents of the whole map. This hash can be used to check the settings for any alterations: i.e. any setting that may had its value changed since last check.
## Error codes
The settings functions provide negated error return codes that can be used as required by the user application to deal with unexpected behaviour.
## Example App
There is an example app available to demonstrate the usage of many of the settings features <code>CONFIG_EXAMPLES_SETTINGS</code>.

1598
system/settings/settings.c Normal file

File diff suppressed because it is too large Load Diff

63
system/settings/storage.h Normal file
View File

@ -0,0 +1,63 @@
/****************************************************************************
* apps/system/settings/storage.h
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
#ifndef SETTINGS_STORAGE_H_
#define SETTINGS_STORAGE_H_
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include "system/settings.h"
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Public Types
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/* Text storage. */
int load_text(FAR char *file);
int save_text(FAR char *file);
/* Binary storage. */
int load_bin(FAR char *file);
int save_bin(FAR char *file);
/* EEPROM storage. */
int load_eeprom(FAR char *file);
int save_eeprom(FAR char *file);
#endif /* SETTINGS_STORAGE_H_*/

View File

@ -0,0 +1,285 @@
/****************************************************************************
* apps/system/settings/storage_bin.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "system/settings.h"
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <nuttx/crc32.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <nuttx/config.h>
#include <sys/types.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BUFFER_SIZE 256 /* Note alignment for Flash writes! */
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR static setting_t *getsetting(char *key);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
extern setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR setting_t *getsetting(FAR char *key)
{
int i;
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
FAR setting_t *setting = &map[i];
if (strcmp(key, setting->key) == 0)
{
return setting;
}
if (setting->type == SETTING_EMPTY)
{
return setting;
}
}
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: load_bin
*
* Description:
* Loads binary data from a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int load_bin(FAR char *file)
{
int fd;
int i;
int ret = OK;
uint16_t valid;
uint16_t count;
uint32_t calc_crc = 0;
uint32_t exp_crc = 0;
setting_t setting;
FAR setting_t *slot;
fd = open(file, O_RDONLY);
if (fd < 0)
{
return -ENOENT;
}
valid = 0;
read(fd, &valid, sizeof(uint16_t));
if (valid != VALID)
{
ret = -EBADMSG;
goto abort; /* Just exit - the settings aren't valid */
}
count = 0;
read(fd, &count, sizeof(uint16_t));
for (i = 0; i < count; i++)
{
read(fd, &setting, sizeof(setting_t));
calc_crc = crc32part((FAR uint8_t *)&setting, sizeof(setting_t),
calc_crc);
}
read(fd, &exp_crc, sizeof(uint32_t));
if (calc_crc != exp_crc)
{
ret = -EBADMSG;
goto abort;
}
lseek(fd, (sizeof(uint16_t) * 2), SEEK_SET); /* Get after valid & size */
for (i = 0; i < count; i++)
{
read(fd, &setting, sizeof(setting_t));
slot = getsetting(setting.key);
if (slot == NULL)
{
continue;
}
memcpy(slot, &setting, sizeof(setting_t));
}
abort:
close(fd);
return ret;
}
/****************************************************************************
* Name: save_bin
*
* Description:
* Saves binary data to a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int save_bin(FAR char *file)
{
int count;
int fd;
int ret = OK;
off_t offset = sizeof(uint16_t) * 2; /* Valid & count */
size_t data_size;
size_t rem_data;
uint32_t crc;
size_t rem_crc;
FAR uint8_t *buffer = malloc(BUFFER_SIZE);
if (buffer == NULL)
{
return -ENOMEM;
}
count = 0;
int i;
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
if (map[i].type == SETTING_EMPTY)
{
break;
}
count++;
}
fd = open(file, (O_RDWR | O_TRUNC), 0666);
if (fd < 0)
{
ret = -ENODEV;
goto abort;
}
memset(buffer, 0xff, BUFFER_SIZE);
*((FAR uint16_t *)buffer) = VALID;
*(((FAR uint16_t *)buffer) + 1) = count;
data_size = count *sizeof(setting_t);
rem_data = data_size;
crc = crc32((FAR uint8_t *)map, data_size);
rem_crc = sizeof(crc);
while ((offset + rem_data + rem_crc) > 0)
{
size_t to_write = ((offset + rem_data) > BUFFER_SIZE) ?
(size_t)(BUFFER_SIZE - offset) : rem_data;
memcpy((buffer + offset), (((FAR uint8_t *)map) +
(data_size - rem_data)), to_write);
size_t j;
for (j = (to_write + offset);
(j < BUFFER_SIZE) && (rem_crc > 0); j++, rem_crc--)
{
off_t crc_byte = (off_t)(sizeof(crc) - rem_crc);
buffer[j] = (crc >> (8 * crc_byte)) & 0xff;
}
write(fd, buffer, BUFFER_SIZE);
rem_data -= to_write;
offset = 0;
}
close(fd);
abort:
free(buffer);
return ret;
}

View File

@ -0,0 +1,425 @@
/****************************************************************************
* apps/system/settings/storage_text.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include "system/settings.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <nuttx/config.h>
#include <sys/types.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define BUFFER_SIZE 256 /* Note alignment for Flash writes! */
/****************************************************************************
* Private Types
****************************************************************************/
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR static setting_t *getsetting(char *key);
/****************************************************************************
* Private Data
****************************************************************************/
/****************************************************************************
* Public Data
****************************************************************************/
extern setting_t map[CONFIG_SYSTEM_SETTINGS_MAP_SIZE];
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: getsetting
*
* Description:
* Gets the setting information from a given key.
*
* Input Parameters:
* key - key of the required setting
*
* Returned Value:
* The setting
*
****************************************************************************/
FAR setting_t *getsetting(char *key)
{
int i;
FAR setting_t *setting;
if (strlen(key) >= CONFIG_SYSTEM_SETTINGS_KEY_SIZE)
{
return NULL;
}
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
setting = &map[i];
if (strcmp(key, setting->key) == 0)
{
return setting;
}
if (setting->type == SETTING_EMPTY)
{
strncpy(setting->key, key, CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
setting->key[CONFIG_SYSTEM_SETTINGS_KEY_SIZE - 1] = '\0';
return setting;
}
}
return NULL;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: load_text
*
* Description:
* Loads text from a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int load_text(FAR char *file)
{
int ret = OK;
FAR FILE *f;
FAR char *eq;
FAR char *key;
FAR char *val;
FAR char *backup_file;
FAR char *buffer;
FAR setting_t *setting;
/* Check that the file exists */
if (access(file, F_OK) != 0)
{
/* If not, try the backup file */
backup_file = malloc(strlen(file) + 2);
if (backup_file == NULL)
{
return -ENODEV;
}
strcpy(backup_file, file);
strcat(backup_file, "~");
if (access(backup_file, F_OK) == 0)
{
/* There is a backup file
* Restore this as the new settings file
*/
rename(backup_file, file);
}
free(backup_file);
}
f = fopen(file, "r");
if (f == NULL)
{
return -ENOENT;
}
buffer = malloc(BUFFER_SIZE);
if (buffer == NULL)
{
ret = -ENOMEM;
goto abort;
}
while (fgets(buffer, BUFFER_SIZE, f))
{
int i;
/* Remove any line terminators */
for (i = ((int)strlen(buffer) - 1); i > 0; i--)
{
if (buffer[i] == ';' || buffer[i] == '\n' || buffer[i] == '\r')
{
buffer[i] = '\0';
}
else
{
break;
}
}
/* Separate the key / value pair */
eq = strchr(buffer, '=');
if (eq == NULL)
{
continue;
}
key = buffer;
val = eq + 1;
*eq = '\0';
/* Check if the key is valid */
if (!isalpha(key[0]))
{
continue;
}
/* Get the setting slot */
setting = getsetting(key);
if (setting == NULL)
{
continue;
}
/* Parse the value */
if (isalpha(val[0]))
{
if (strcasecmp(val, "true") == 0)
{
/* It's a boolean */
setting->type = SETTING_BOOL;
setting->val.i = 1;
}
else if (strcasecmp(val, "false") == 0)
{
/* It's a boolean */
setting->type = SETTING_BOOL;
setting->val.i = 0;
}
else
{
/* It's a string */
if (strlen(val) >= CONFIG_SYSTEM_SETTINGS_VALUE_SIZE)
{
continue;
}
setting->type = SETTING_STRING;
strncpy(setting->val.s, val,
CONFIG_SYSTEM_SETTINGS_VALUE_SIZE);
setting->val.s[CONFIG_SYSTEM_SETTINGS_VALUE_SIZE - 1] = '\0';
}
}
else
{
if (strchr(val, '.') != NULL)
{
FAR char *s = val;
i = 0;
while (s[i]) s[i] == '.' ? i++ : *s++;
if (i == 1)
{
/* It's a float */
double d = 0;
if (sscanf(val, "%lf", &d) == 1)
{
setting->type = SETTING_FLOAT;
setting->val.f = d;
}
}
else if (i == 3)
{
/* It's an IP address */
setting->type = SETTING_IP_ADDR;
inet_pton(AF_INET, val, &setting->val.ip);
}
}
else
{
/* It's an integer */
i = 0;
if (sscanf(val, "%d", &i) == 1)
{
setting->type = SETTING_INT;
setting->val.i = i;
}
}
}
/* Handle parse errors */
if (setting->type == SETTING_EMPTY)
{
memset(setting->key, 0, CONFIG_SYSTEM_SETTINGS_KEY_SIZE);
}
}
free(buffer);
abort:
fclose(f);
return ret;
}
/****************************************************************************
* Name: save_text
*
* Description:
* Saves text to a storage file.
*
* Input Parameters:
* file - the filename of the storage to use
*
* Returned Value:
* Success or negated failure code
*
****************************************************************************/
int save_text(FAR char *file)
{
int ret = OK;
FAR char *backup_file = malloc(strlen(file) + 2);
FAR FILE *f;
int i;
if (backup_file == NULL)
{
return -ENODEV;
}
strcpy(backup_file, file);
strcat(backup_file, "~");
f = fopen(backup_file, "w");
if (f == NULL)
{
ret = -ENODEV;
goto abort;
}
for (i = 0; i < CONFIG_SYSTEM_SETTINGS_MAP_SIZE; i++)
{
if (map[i].type == SETTING_EMPTY)
{
break;
}
switch (map[i].type)
{
case SETTING_STRING:
{
fprintf(f, "%s=%s\n", map[i].key,
map[i].val.s);
}
break;
case SETTING_INT:
{
fprintf(f, "%s=%d\n", map[i].key,
map[i].val.i);
}
break;
case SETTING_BOOL:
{
fprintf(f, "%s=%s\n", map[i].key,
map[i].val.i ?
"true" : "false");
}
break;
case SETTING_FLOAT:
{
fprintf(f, "%s=%.06f\n", map[i].key,
map[i].val.f);
}
break;
case SETTING_IP_ADDR:
{
char buffer[20];
inet_ntop(AF_INET, &map[i].val.ip, buffer, 20);
fprintf(f, "%s=%s\n", map[i].key, buffer);
}
break;
default:
{
return -EINVAL;
}
break;
}
}
fclose(f);
remove(file);
rename(backup_file, file);
abort:
free(backup_file);
return ret;
}