Added Zephyr settings and basic device in subsys. (#697)
* Added Zephyr settings subsys to enable storing of BACnet values according to BACnet object property value path. * Added BACnet Basic features to enable basic samples. Refactored the zephyr BACnet profile B-SS sample to use BACnet basic subsys.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# CMake for BACnet settings library
|
||||
#
|
||||
# @author Steve Karg <skarg@users.sourceforge.net>
|
||||
# @date May 2024
|
||||
# @copyright SPDX-License-Identifier: MIT
|
||||
zephyr_library(bacnet_settings)
|
||||
|
||||
zephyr_library_include_directories(include)
|
||||
|
||||
zephyr_library_sources(
|
||||
bacnet_storage.c
|
||||
bacnet_settings.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_BACNET_SETTINGS_SHELL
|
||||
bacnet_shell.c
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
# Kconfig - Subsystem configuration options
|
||||
#
|
||||
# @author Steve Karg <skarg@users.sourceforge.net>
|
||||
# @date May 2024
|
||||
# @copyright SPDX-License-Identifier: MIT
|
||||
menuconfig BACNETSTACK_BACNET_SETTINGS
|
||||
bool "BACNETSTACK_BACNET_SETTINGS"
|
||||
default y if BACNETSTACK && SETTINGS
|
||||
help
|
||||
This option enables BACnet Settings services
|
||||
|
||||
if BACNETSTACK_BACNET_SETTINGS
|
||||
|
||||
module = BACNETSTACK_BACNET_SETTINGS
|
||||
module-str = bac_settings
|
||||
|
||||
config BACNET_SETTINGS_BASE_NAME
|
||||
string "BACnet object path base name for every setting"
|
||||
default ".bacnet"
|
||||
help
|
||||
BACnet object path base name for every setting"
|
||||
|
||||
config BACNET_SETTINGS_SHELL
|
||||
bool "BACnet settings subsystem shell"
|
||||
depends on BACNETSTACK
|
||||
default y if SHELL && SETTINGS
|
||||
|
||||
endif # BACNETSTACK_BACNET_SETTINGS
|
||||
@@ -0,0 +1,379 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief Handle Get/Set of BACnet application encoded settings
|
||||
* @date May 2024
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include <bacnet_settings/bacnet_storage.h>
|
||||
#include <bacnet_settings/bacnet_settings.h>
|
||||
#include "bacnet/bacdef.h"
|
||||
#include "bacnet/bacapp.h"
|
||||
#include "bacnet/bacdcode.h"
|
||||
#include "bacnet/bacstr.h"
|
||||
#include "bacnet/bacint.h"
|
||||
|
||||
/**
|
||||
* @brief Get a BACnet SIGNED INTEGER value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param default_value [in] The default value if not found
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_value_get(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
BACNET_APPLICATION_DATA_VALUE *value)
|
||||
{
|
||||
uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int stored_len, len;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
stored_len = bacnet_storage_get(&key, name, sizeof(name));
|
||||
if (stored_len > 0) {
|
||||
len = bacapp_decode_application_data(name, stored_len, value);
|
||||
if (len <= 0) {
|
||||
if (value) {
|
||||
value->tag = MAX_BACNET_APPLICATION_TAG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return stored_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a BACnet SIGNED INTEGER value in non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param value [in] The value to store
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_value_set(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
BACNET_APPLICATION_DATA_VALUE *value)
|
||||
{
|
||||
uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int rc, len;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
len = bacapp_encode_application_data(NULL, value);
|
||||
if (len <= 0) {
|
||||
return false;
|
||||
} else if (len > sizeof(name)) {
|
||||
return false;
|
||||
}
|
||||
len = bacapp_encode_application_data(name, value);
|
||||
rc = bacnet_storage_set(&key, name, len);
|
||||
|
||||
return rc == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a BACnet REAL value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param default_value [in] The default value if not found
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_real_get(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
float default_value, float *value)
|
||||
{
|
||||
int stored_len;
|
||||
BACNET_APPLICATION_DATA_VALUE bvalue = { 0 };
|
||||
|
||||
stored_len =
|
||||
bacnet_settings_value_get(object_type, object_instance,
|
||||
property_id, array_index, &bvalue);
|
||||
if ((stored_len >= 0) && (bvalue.tag == BACNET_APPLICATION_TAG_REAL)) {
|
||||
if (value) {
|
||||
*value = bvalue.type.Real;
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
*value = default_value;
|
||||
}
|
||||
}
|
||||
|
||||
return stored_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a BACnet REAL value in non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param value [in] The value to store
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_real_set(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
float value)
|
||||
{
|
||||
BACNET_APPLICATION_DATA_VALUE bvalue = { 0 };
|
||||
|
||||
bvalue.context_specific = false;
|
||||
bvalue.tag = BACNET_APPLICATION_TAG_REAL;
|
||||
bvalue.type.Real = value;
|
||||
|
||||
return bacnet_settings_value_set(object_type, object_instance,
|
||||
property_id, array_index, &bvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a BACnet UNSIGNED value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param default_value [in] The default value if not found
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_unsigned_get(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
BACNET_UNSIGNED_INTEGER default_value,
|
||||
BACNET_UNSIGNED_INTEGER *value)
|
||||
{
|
||||
uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int stored_len, len;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
stored_len = bacnet_storage_get(&key, name, sizeof(name));
|
||||
if (stored_len > 0) {
|
||||
len = bacnet_unsigned_application_decode(name, stored_len,
|
||||
value);
|
||||
if (len <= 0) {
|
||||
if (value) {
|
||||
*value = default_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
*value = default_value;
|
||||
}
|
||||
}
|
||||
|
||||
return stored_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a BACnet UNSIGNED value in non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param value [int] The value to store
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_unsigned_set(uint16_t object_type,
|
||||
uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
BACNET_UNSIGNED_INTEGER value)
|
||||
{
|
||||
BACNET_APPLICATION_DATA_VALUE bvalue = { 0 };
|
||||
|
||||
bvalue.context_specific = false;
|
||||
bvalue.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT;
|
||||
bvalue.type.Unsigned_Int = value;
|
||||
|
||||
return bacnet_settings_value_set(object_type, object_instance,
|
||||
property_id, array_index, &bvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a BACnet SIGNED INTEGER value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param default_value [in] The default value if not found
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_signed_get(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
int32_t default_value, int32_t *value)
|
||||
{
|
||||
uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int stored_len, len;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
stored_len = bacnet_storage_get(&key, name, sizeof(name));
|
||||
if (stored_len > 0) {
|
||||
len = bacnet_signed_application_decode(name, stored_len, value);
|
||||
if (len <= 0) {
|
||||
if (value) {
|
||||
*value = default_value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
*value = default_value;
|
||||
}
|
||||
}
|
||||
|
||||
return stored_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a BACnet SIGNED INTEGER value in non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param array_index [in] The BACnet array index
|
||||
* @param value [in] The value to store
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_signed_set(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
int32_t value)
|
||||
{
|
||||
BACNET_APPLICATION_DATA_VALUE bvalue = { 0 };
|
||||
|
||||
bvalue.context_specific = false;
|
||||
bvalue.tag = BACNET_APPLICATION_TAG_SIGNED_INT;
|
||||
bvalue.type.Signed_Int = value;
|
||||
|
||||
return bacnet_settings_value_set(object_type, object_instance,
|
||||
property_id, array_index, &bvalue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a BACnet CHARACTER_STRING value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param default_value [in] The default value if not found
|
||||
* @param value [out] The character string value
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_characterstring_get(uint16_t object_type,
|
||||
uint32_t object_instance,
|
||||
uint32_t property_id,
|
||||
uint32_t array_index,
|
||||
const char *default_value,
|
||||
BACNET_CHARACTER_STRING *value)
|
||||
{
|
||||
uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int stored_len, len;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
stored_len = bacnet_storage_get(&key, name, sizeof(name));
|
||||
if (stored_len > 0) {
|
||||
len = bacnet_character_string_application_decode(
|
||||
name, stored_len, value);
|
||||
if (len <= 0) {
|
||||
characterstring_init_ansi(value, default_value);
|
||||
}
|
||||
} else {
|
||||
characterstring_init_ansi(value, default_value);
|
||||
}
|
||||
|
||||
return stored_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Store a BACnet CHARACTER_STRING value to non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param default_value [in] The default value if not found
|
||||
* @param value [out] The character string value
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_characterstring_ansi_set(uint16_t object_type,
|
||||
uint32_t object_instance,
|
||||
uint32_t property_id,
|
||||
uint32_t array_index,
|
||||
const char *cstring)
|
||||
{
|
||||
BACNET_APPLICATION_DATA_VALUE bvalue = { 0 };
|
||||
bool status;
|
||||
|
||||
bvalue.context_specific = false;
|
||||
bvalue.tag = BACNET_APPLICATION_TAG_CHARACTER_STRING;
|
||||
status = characterstring_init_ansi(&bvalue.type.Character_String,
|
||||
cstring);
|
||||
if (!status) {
|
||||
status = bacnet_settings_value_set(object_type, object_instance,
|
||||
property_id, array_index,
|
||||
&bvalue);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a C-string value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param default_value [in] The default value if not found
|
||||
* @param value [out] The string value
|
||||
* @param value_size [in] The size of the string value
|
||||
* @return stored data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_settings_string_get(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
const char *default_value, char *value,
|
||||
size_t value_size)
|
||||
{
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int rc;
|
||||
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
rc = bacnet_storage_get(&key, value, value_size);
|
||||
if (rc <= 0) {
|
||||
if (default_value) {
|
||||
strncpy(value, default_value, value_size);
|
||||
rc = strlen(default_value);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a C-string value from non-volatile storage
|
||||
* @param object_type [in] The BACnet object type
|
||||
* @param object_instance [in] The BACnet object instance
|
||||
* @param property_id [in] The BACnet property id
|
||||
* @param default_value [in] The default value if not found
|
||||
* @param value [in] The character string value
|
||||
* @return true on success, false on failure.
|
||||
*/
|
||||
bool bacnet_settings_string_set(uint16_t object_type, uint32_t object_instance,
|
||||
uint32_t property_id, uint32_t array_index,
|
||||
const char *value)
|
||||
{
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
int rc;
|
||||
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
bacnet_storage_key_init(&key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
rc = bacnet_storage_set(&key, (const char *)value, strlen(value) + 1);
|
||||
|
||||
return rc == 0;
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief The BACnet shell commands for debugging and testing
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date May 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/shell/shell.h>
|
||||
#include <bacnet_settings/bacnet_storage.h>
|
||||
|
||||
/**
|
||||
* @brief Get or set a string using BACnet storage subsystem
|
||||
* @param sh Shell
|
||||
* @param argc Number of arguments
|
||||
* @param argv Argument list
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc,
|
||||
char **argv)
|
||||
{
|
||||
uint16_t object_type;
|
||||
uint32_t object_instance;
|
||||
uint32_t property_id = 77;
|
||||
uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE;
|
||||
long value = 0;
|
||||
|
||||
if (argc < 3) {
|
||||
shell_error(
|
||||
sh,
|
||||
"Usage: %s <object-type> <instance> <property> [value]",
|
||||
argv[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
value = strtoul(argv[1], NULL, 0);
|
||||
if ((value < 0) || (value >= UINT16_MAX)) {
|
||||
shell_error(sh, "Invalid object-type: %s. Must be 0-65535.",
|
||||
argv[1]);
|
||||
return -EINVAL;
|
||||
}
|
||||
object_type = (uint16_t)value;
|
||||
value = strtoul(argv[2], NULL, 0);
|
||||
if (value > 4194303) {
|
||||
shell_error(sh,
|
||||
"Invalid object-instance: %s. Must be 0-4194303.",
|
||||
argv[2]);
|
||||
return -EINVAL;
|
||||
}
|
||||
object_instance = (uint32_t)value;
|
||||
value = strtoul(argv[3], NULL, 0);
|
||||
if (value > UINT32_MAX) {
|
||||
shell_error(sh, "Invalid property: %s. Must be 0-4294967295.",
|
||||
argv[3]);
|
||||
return -EINVAL;
|
||||
}
|
||||
property_id = (uint32_t)value;
|
||||
/* setup the storage key */
|
||||
bacnet_storage_key_init(key, object_type, object_instance, property_id,
|
||||
array_index);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get or set a string using BACnet storage subsystem
|
||||
* @param sh Shell
|
||||
* @param argc Number of arguments
|
||||
* @param argv Argument list
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
static int cmd_string(const struct shell *sh, size_t argc, char **argv)
|
||||
{
|
||||
char key_name[BACNET_STORAGE_KEY_SIZE_MAX + 1] = { 0 };
|
||||
uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = { 0 };
|
||||
BACNET_STORAGE_KEY key = { 0 };
|
||||
size_t arg_len = 0;
|
||||
int rc;
|
||||
|
||||
rc = cmd_key(&key, sh, argc, argv);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
/* convert the key to a string for the shell */
|
||||
(void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key);
|
||||
if (argc > 4) {
|
||||
arg_len = strlen(argv[4]);
|
||||
rc = bacnet_storage_set(&key, argv[4], arg_len);
|
||||
if (rc == 0) {
|
||||
shell_print(sh, "Set %s = %s", key_name, argv[4]);
|
||||
} else {
|
||||
shell_error(sh, "Unable to set %s = %s", key_name,
|
||||
argv[4]);
|
||||
return -EINVAL;
|
||||
}
|
||||
} else {
|
||||
rc = bacnet_storage_get(&key, data, sizeof(data));
|
||||
if (rc < 0) {
|
||||
shell_error(sh, "Unable to get %s", key_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
shell_print(sh, "Get %s = %s", key_name, data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SHELL_STATIC_SUBCMD_SET_CREATE(sub_bacnet_settings_cmds,
|
||||
SHELL_CMD(string, NULL,
|
||||
"get or set BACnet storage string",
|
||||
cmd_string),
|
||||
SHELL_SUBCMD_SET_END);
|
||||
|
||||
SHELL_SUBCMD_ADD((bacnet), settings, &sub_bacnet_settings_cmds,
|
||||
"BACnet settings commands", NULL, 1, 0);
|
||||
@@ -0,0 +1,266 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief The BACnet storage tasks for handling the device specific object data
|
||||
* @author Steve Karg <skarg@users.sourceforge.net>
|
||||
* @date April 2024
|
||||
* @copyright SPDX-License-Identifier: MIT
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <zephyr/settings/settings.h>
|
||||
#if defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_LITTLEFS)
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/fs/littlefs.h>
|
||||
#elif defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_EXT2)
|
||||
#include <zephyr/fs/fs.h>
|
||||
#include <zephyr/fs/ext2.h>
|
||||
#endif
|
||||
/* me! */
|
||||
#include "bacnet_settings/bacnet_storage.h"
|
||||
|
||||
#ifdef CONFIG_BACNET_SETTINGS_BASE_NAME
|
||||
#define BACNET_STORAGE_BASE_NAME CONFIG_BACNET_SETTINGS_BASE_NAME
|
||||
#else
|
||||
#define BACNET_STORAGE_BASE_NAME ".bacnet"
|
||||
#endif
|
||||
|
||||
/* Logging module registration is already done in bacnet/ports/zephyr/main.c */
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL);
|
||||
#define FAIL_MSG "fail (err %d)"
|
||||
|
||||
#define STORAGE_PARTITION storage_partition
|
||||
#define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION)
|
||||
|
||||
/**
|
||||
* @brief Initialize the non-volatile data
|
||||
*/
|
||||
void bacnet_storage_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
#if defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_LITTLEFS)
|
||||
FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage);
|
||||
|
||||
/* mounting info */
|
||||
static struct fs_mount_t littlefs_mnt = {
|
||||
.type = FS_LITTLEFS,
|
||||
.fs_data = &cstorage,
|
||||
.storage_dev = (void *)STORAGE_PARTITION_ID,
|
||||
.mnt_point = "/ff"
|
||||
};
|
||||
|
||||
rc = fs_mount(&littlefs_mnt);
|
||||
if (rc != 0) {
|
||||
LOG_INF("mounting littlefs error: [%d]", rc);
|
||||
} else {
|
||||
rc = fs_unlink(CONFIG_SETTINGS_FILE_PATH);
|
||||
if ((rc != 0) && (rc != -ENOENT)) {
|
||||
H("can't delete config file%d", rc);
|
||||
} else {
|
||||
LOG_INF("FS initialized: OK");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
rc = settings_subsys_init();
|
||||
if (rc) {
|
||||
LOG_INF("settings subsys initialization: fail (err %d)", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_INF("settings subsys initialization: OK.");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize a BACnet key object with optional array
|
||||
* @param key BACnet key (type, instance, property, array index)
|
||||
* @param object_type BACnet object type
|
||||
* @param object_instance BACnet object instance
|
||||
* @param property_id BACnet property id
|
||||
* @param array_index BACnet array index
|
||||
*/
|
||||
void bacnet_storage_key_init(BACNET_STORAGE_KEY *key, uint16_t object_type,
|
||||
uint32_t object_instance, uint32_t property_id,
|
||||
uint32_t array_index)
|
||||
{
|
||||
if (key) {
|
||||
key->object_type = object_type;
|
||||
key->object_instance = object_instance;
|
||||
key->property_id = property_id;
|
||||
key->array_index = array_index;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a storage key string for a BACnet object property
|
||||
* @param buffer buffer to store key string
|
||||
* @param buffer_size size of key buffer
|
||||
* @param key BACnet key (type, instance, property, array index)
|
||||
* @return length of the string
|
||||
*/
|
||||
int bacnet_storage_key_encode(char *buffer, size_t buffer_size,
|
||||
BACNET_STORAGE_KEY *key)
|
||||
{
|
||||
int rc = 0;
|
||||
const char base_name[] = CONFIG_BACNET_STORAGE_BASE_NAME;
|
||||
|
||||
if (buffer) {
|
||||
memset(buffer, 0, buffer_size);
|
||||
}
|
||||
if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) {
|
||||
rc = snprintf(buffer, buffer_size, "%s%c%u%c%lu%c%lu",
|
||||
base_name, SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned short)key->object_type,
|
||||
SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned long)key->object_instance,
|
||||
SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned long)key->property_id);
|
||||
} else {
|
||||
rc = snprintf(buffer, buffer_size, "%s%c%u%c%lu%c%lu%c%lu",
|
||||
base_name, SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned short)key->object_type,
|
||||
SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned long)key->object_instance,
|
||||
SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned long)key->property_id,
|
||||
SETTINGS_NAME_SEPARATOR,
|
||||
(unsigned long)key->array_index);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set a value with a specific key to non-volatile storage
|
||||
* @param key [in] Key in string format.
|
||||
* @param data [in] one or more bytes of data
|
||||
* @param data_len [in] Value length in bytes.
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
int bacnet_storage_set(BACNET_STORAGE_KEY *key, const void *data,
|
||||
size_t data_len)
|
||||
{
|
||||
char name[SETTINGS_MAX_NAME_LEN + 1] = { 0 };
|
||||
int rc;
|
||||
|
||||
rc = bacnet_storage_key_encode(name, sizeof(name), key);
|
||||
LOG_INF("Set a key-value pair. Key=%s", name);
|
||||
rc = settings_save_one(name, data, data_len);
|
||||
if (rc) {
|
||||
LOG_INF(FAIL_MSG, rc);
|
||||
} else {
|
||||
LOG_HEXDUMP_INF(data, data_len, "value");
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Structure to hold immediate values
|
||||
*/
|
||||
struct direct_immediate_value {
|
||||
size_t value_size;
|
||||
size_t value_len;
|
||||
void *value;
|
||||
bool fetched;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Direct loader for immediate values
|
||||
* @param name [in] Key in string format.
|
||||
* @param len [in] Length of the key
|
||||
* @param read_cb [in] Callback to read the value
|
||||
* @param cb_arg [in] Callback argument
|
||||
* @param param [in] Callback parameter
|
||||
* @return 0 on success, non-zero on failure.
|
||||
*/
|
||||
static int direct_loader_immediate_value(const char *name, size_t len,
|
||||
settings_read_cb read_cb, void *cb_arg,
|
||||
void *param)
|
||||
{
|
||||
const char *next;
|
||||
size_t name_len;
|
||||
int rc;
|
||||
struct direct_immediate_value *context =
|
||||
(struct direct_immediate_value *)param;
|
||||
|
||||
/* only the exact match and ignore descendants of the searched name */
|
||||
name_len = settings_name_next(name, &next);
|
||||
if (name_len == 0) {
|
||||
rc = read_cb(cb_arg, context->value, len);
|
||||
if ((rc >= 0) && (rc <= context->value_size)) {
|
||||
context->fetched = true;
|
||||
context->value_len = rc;
|
||||
LOG_INF("immediate load: OK.");
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* other keys aren't served by the callback
|
||||
* Return success in order to skip them
|
||||
* and keep storage processing.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Load an immediate value from non-volatile storage
|
||||
* @param name [in] Key in string format.
|
||||
* @param value [out] Buffer to store the value
|
||||
* @param value_size [in] size of the buffer
|
||||
* @return value length in bytes on success 0..N, negative on failure.
|
||||
*/
|
||||
static int load_immediate_value(const char *name, void *value,
|
||||
size_t value_size)
|
||||
{
|
||||
int rc;
|
||||
struct direct_immediate_value context;
|
||||
|
||||
context.fetched = false;
|
||||
context.value_size = value_size;
|
||||
context.value_len = 0;
|
||||
context.value = value;
|
||||
|
||||
rc = settings_load_subtree_direct(name, direct_loader_immediate_value,
|
||||
(void *)&context);
|
||||
if (rc == 0) {
|
||||
if (!context.fetched) {
|
||||
rc = -ENOENT;
|
||||
}
|
||||
}
|
||||
|
||||
return context.value_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get a value with a specific key to non-volatile storage
|
||||
* @param key [in] Key in string format.
|
||||
* @param data [out] Binary value.
|
||||
* @param data_size [in] requested value length in bytes
|
||||
* @return data length on success 0..N, negative on failure.
|
||||
*/
|
||||
int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size)
|
||||
{
|
||||
char name[SETTINGS_MAX_NAME_LEN + 1] = { 0 };
|
||||
int rc;
|
||||
|
||||
rc = bacnet_storage_key_encode(name, sizeof(name), key);
|
||||
LOG_INF("Get a key-value pair. Key=<%s>", name);
|
||||
rc = load_immediate_value(name, data, data_size);
|
||||
if (rc == 0) {
|
||||
LOG_INF("empty entry");
|
||||
} else if (rc > 0) {
|
||||
LOG_HEXDUMP_INF(data, rc, "value");
|
||||
} else if (rc == -ENOENT) {
|
||||
LOG_INF("no entry");
|
||||
} else {
|
||||
LOG_INF("unexpected" FAIL_MSG, rc);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
Reference in New Issue
Block a user