From 6d24901c50f59715b90620b48d6821a2263ef80b Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Fri, 5 Sep 2025 17:48:04 -0500 Subject: [PATCH 1/9] Utilize zephyr settings_handler to restore data using settings_load() after initialization. --- .../include/bacnet_settings/bacnet_settings.h | 4 + .../include/bacnet_settings/bacnet_storage.h | 20 +- zephyr/samples/profiles/b-ss/src/main.c | 24 +- .../subsys/bacnet_settings/bacnet_settings.c | 50 ++-- zephyr/subsys/bacnet_settings/bacnet_shell.c | 26 +- .../subsys/bacnet_settings/bacnet_storage.c | 225 +++++++++++++++++- 6 files changed, 318 insertions(+), 31 deletions(-) diff --git a/zephyr/include/bacnet_settings/bacnet_settings.h b/zephyr/include/bacnet_settings/bacnet_settings.h index 4e77252..b21698e 100644 --- a/zephyr/include/bacnet_settings/bacnet_settings.h +++ b/zephyr/include/bacnet_settings/bacnet_settings.h @@ -25,6 +25,10 @@ void bacnet_settings_basic_store(BACNET_OBJECT_TYPE object_type, uint32_t object BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index, uint8_t *application_data, int application_data_len); bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data); +bool bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, + uint32_t property_id, uint32_t array_index, + const void *data, size_t data_len, + write_property_function write_function); bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t object_instance, uint32_t property_id, uint32_t array_index, write_property_function write_function); diff --git a/zephyr/include/bacnet_settings/bacnet_storage.h b/zephyr/include/bacnet_settings/bacnet_storage.h index 6d8abca..e966b4a 100644 --- a/zephyr/include/bacnet_settings/bacnet_storage.h +++ b/zephyr/include/bacnet_settings/bacnet_storage.h @@ -13,8 +13,8 @@ #include #include -#define BACNET_STORAGE_VALUE_SIZE_MAX SETTINGS_MAX_VAL_LEN -#define BACNET_STORAGE_KEY_SIZE_MAX SETTINGS_MAX_NAME_LEN +#define BACNET_STORAGE_VALUE_SIZE_MAX SETTINGS_MAX_VAL_LEN +#define BACNET_STORAGE_KEY_SIZE_MAX SETTINGS_MAX_NAME_LEN #define BACNET_STORAGE_ARRAY_INDEX_NONE UINT32_MAX typedef struct bacnet_storage_key_t { @@ -24,19 +24,19 @@ typedef struct bacnet_storage_key_t { uint32_t array_index; } BACNET_STORAGE_KEY; +typedef int (*bacnet_storage_restore_callback)(BACNET_STORAGE_KEY *key, const void *data, + size_t data_size); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -void bacnet_storage_init(void); - +int bacnet_storage_init(bacnet_storage_restore_callback cb); 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); -int bacnet_storage_key_encode(char *buffer, size_t buffer_size, - BACNET_STORAGE_KEY *key); -int bacnet_storage_set(BACNET_STORAGE_KEY *key, const void *data, - size_t data_size); + uint32_t object_instance, uint32_t property_id, uint32_t array_index); +int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key); +int bacnet_storage_key_encode(char *buffer, size_t buffer_size, BACNET_STORAGE_KEY *key); +int bacnet_storage_set(BACNET_STORAGE_KEY *key, const void *data, size_t data_size); int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size); #ifdef __cplusplus diff --git a/zephyr/samples/profiles/b-ss/src/main.c b/zephyr/samples/profiles/b-ss/src/main.c index d73d8c2..355896a 100644 --- a/zephyr/samples/profiles/b-ss/src/main.c +++ b/zephyr/samples/profiles/b-ss/src/main.c @@ -26,6 +26,7 @@ #include "bacnet/basic/server/bacnet_port.h" /* BACnet Stack Zephyr services */ #include +#include /* Logging module registration is already done in ports/zephyr/main.c */ #include LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); @@ -38,6 +39,22 @@ static const uint32_t Sensor_Instance = 1; /* timer for Sensor Update Interval */ static struct mstimer Sensor_Update_Timer; +int BACnet_Smart_Sensor_Settings_Restore( + BACNET_STORAGE_KEY *key, const void *data, size_t data_len) +{ + bool status; + int err = 0; + + status = bacnet_settings_restore(key->object_type, key->object_instance, + key->property_id, key->array_index, + data, data_len, Device_Write_Property); + if (!status) { + err = -EACCES; + } + + return err; +} + /** * @brief BACnet Project Initialization Handler * @param context [in] The context to pass to the callback function @@ -46,6 +63,7 @@ static struct mstimer Sensor_Update_Timer; static void BACnet_Smart_Sensor_Init_Handler(void *context) { const float default_temperature = 25.0f; +#if 0 uint32_t array_index = BACNET_ARRAY_ALL; bool status = false; int i; @@ -61,6 +79,7 @@ static void BACnet_Smart_Sensor_Init_Handler(void *context) PROP_OBJECT_IDENTIFIER, PROP_OBJECT_NAME, }; +#endif (void)context; LOG_INF("BACnet Stack Initialized"); @@ -73,6 +92,8 @@ static void BACnet_Smart_Sensor_Init_Handler(void *context) Analog_Input_Units_Set(Sensor_Instance, UNITS_DEGREES_CELSIUS); Analog_Input_COV_Increment_Set(Sensor_Instance, 1.0f); /* restore any property values previously stored via WriteProperty */ + bacnet_storage_init(BACnet_Smart_Sensor_Settings_Restore); +#if 0 for (i = 0; i < ARRAY_SIZE(device_writeable_property_list); i++) { status = bacnet_settings_write_property_restore( OBJECT_DEVICE, BACNET_MAX_INSTANCE, device_writeable_property_list[i], @@ -90,7 +111,8 @@ static void BACnet_Smart_Sensor_Init_Handler(void *context) /* no settings stored for this property, use defaults */ } } - /* These writable property values are stored WriteProperty. +#endif + /* writable property values are stored with WriteProperty. Set this callback after init to prevent recursion. */ bacnet_basic_store_callback_set(bacnet_settings_basic_store); LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); diff --git a/zephyr/subsys/bacnet_settings/bacnet_settings.c b/zephyr/subsys/bacnet_settings/bacnet_settings.c index 031947a..785f320 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_settings.c +++ b/zephyr/subsys/bacnet_settings/bacnet_settings.c @@ -79,32 +79,25 @@ bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data) } /** - * @brief Get a BACnet encoded value from non-volatile storage - * and write it to the object specific WriteProperty handler. - * @param object_type [in] The BACnet object type + * @brief Callback from the Zephyr settings_restore iterator + * @param key [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 write_function [in] the WriteProperty function of the object - * @param commandable [in] true if the object is commandable - * @param * @return true on success, false on failure. */ -bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t object_instance, +bool bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, uint32_t property_id, uint32_t array_index, + const void *data, size_t data_len, write_property_function write_function) { bool status = false; - uint8_t name[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = {0}; - BACNET_STORAGE_KEY key = {0}; - int stored_len; BACNET_WRITE_PROPERTY_DATA wp_data = {0}; - 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) && (stored_len <= MAX_APDU)) { - wp_data.application_data_len = stored_len; - memcpy(&wp_data.application_data[0], &name[0], stored_len); + if ((data_len > 0) && (data_len <= MAX_APDU)) { + wp_data.application_data_len = data_len; + memcpy(&wp_data.application_data[0], data, data_len); wp_data.object_type = object_type; wp_data.object_instance = object_instance; wp_data.object_property = property_id; @@ -122,6 +115,35 @@ bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t objec return status; } +/** + * @brief Get a BACnet encoded value from non-volatile storage + * and write it to the object specific WriteProperty handler. + * @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 write_function [in] the WriteProperty function of the object + * @return true on success, false on failure. + */ +bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t object_instance, + uint32_t property_id, uint32_t array_index, + write_property_function write_function) +{ + bool status = false; + uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = {0}; + BACNET_STORAGE_KEY key = {0}; + int data_len; + + bacnet_storage_key_init(&key, object_type, object_instance, property_id, array_index); + data_len = bacnet_storage_get(&key, data, sizeof(data)); + status = bacnet_settings_restore(object_type, object_instance, + property_id, array_index, + data, data_len, + write_function); + + return status; +} + /** * @brief Get a BACnet SIGNED INTEGER value from non-volatile storage * @param object_type [in] The BACnet object type diff --git a/zephyr/subsys/bacnet_settings/bacnet_shell.c b/zephyr/subsys/bacnet_settings/bacnet_shell.c index 8055529..41ff29f 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_shell.c +++ b/zephyr/subsys/bacnet_settings/bacnet_shell.c @@ -10,6 +10,7 @@ #include #include #include +#include /** * @brief Get or set a string using BACnet storage subsystem @@ -25,12 +26,18 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, uint32_t property_id = 77; uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; long value = 0; + int found_index = 0; if (argc < 3) { shell_error(sh, "Usage: %s [value]", argv[0]); return -EINVAL; } - value = strtoul(argv[1], NULL, 0); + if (bactext_object_type_strtol(argv[1], &found_index)) { + value = found_index; + } else { + shell_error(sh, "Invalid object-type: %s.", argv[1]); + return -EINVAL; + } if ((value < 0) || (value >= UINT16_MAX)) { shell_error(sh, "Invalid object-type: %s. Must be 0-65535.", argv[1]); return -EINVAL; @@ -42,7 +49,12 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, return -EINVAL; } object_instance = (uint32_t)value; - value = strtoul(argv[3], NULL, 0); + if (bactext_property_strtol(argv[3], &found_index)) { + value = found_index; + } else { + shell_error(sh, "Invalid property: %s.", argv[1]); + return -EINVAL; + } if (value > UINT32_MAX) { shell_error(sh, "Invalid property: %s. Must be 0-4294967295.", argv[3]); return -EINVAL; @@ -65,7 +77,7 @@ 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}; + BACNET_STORAGE_KEY key = {0}, test_key = {0}; size_t arg_len = 0; int rc; @@ -75,6 +87,14 @@ static int cmd_string(const struct shell *sh, size_t argc, char **argv) } /* convert the key to a string for the shell */ (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); + /* convert the key string to numbers for a test */ + if (bacnet_storage_key_decode(key_name, &test_key) == 0) { + shell_print(sh, "key=%lu/%lu/%lu/%lu", + (unsigned long)test_key.object_type, + (unsigned long)test_key.object_instance, + (unsigned long)test_key.property_id, + (unsigned long)test_key.array_index); + } if (argc > 4) { arg_len = strlen(argv[4]); rc = bacnet_storage_set(&key, argv[4], arg_len); diff --git a/zephyr/subsys/bacnet_settings/bacnet_storage.c b/zephyr/subsys/bacnet_settings/bacnet_storage.c index 447813c..b21f5a1 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_storage.c +++ b/zephyr/subsys/bacnet_settings/bacnet_storage.c @@ -36,13 +36,88 @@ LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); #define STORAGE_PARTITION storage_partition #define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION) +static bacnet_storage_restore_callback BACnet_Storage_Restore_Callback; + +int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, + size_t data_size) +{ + int err = 0; + + if (BACnet_Storage_Restore_Callback) { + err = BACnet_Storage_Restore_Callback(key, data, data_size); + } + + return err; +} + +/** + * @brief Attempt to convert a numeric string into a unsigned long integer + * @param search_name - string to convert + * @param value - where to put the converted value + * @return true if converted and found_index is set + * @return false if not converted and found_index is not set + */ +bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) +{ + char *endptr; + unsigned long value; + + value = strtoul(search_name, &endptr, 0); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if (value == ULONG_MAX) { + /* If the correct value is outside the range of representable values, + {ULONG_MAX} shall be returned */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (long_value) { + *long_value = (unsigned)value; + } + + return true; +} + +int bacnet_storage_handler_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg); +int bacnet_storage_handler_commit(void); +int bacnet_storage_handler_export(int (*cb)(const char *name, + const void *value, size_t val_len)); + +/* dynamic main tree handler */ +struct settings_handler bacnet_storage_handler = { + .name = "bacnet", + /* This gets called when asking for a settings element value + by its name using settings_runtime_get() from the runtime backend.*/ + .h_get = NULL, + /* This gets called when the value is loaded from persisted storage + with settings_load(), or when using settings_runtime_set() from + the runtime backend.*/ + .h_set = bacnet_storage_handler_set, + /* This gets called after the settings have been loaded in full. + Sometimes you don’t want an individual setting value to take + effect right away, for example if there are multiple settings + which are interdependent.*/ + .h_commit = bacnet_storage_handler_commit, + /* This gets called to write all current settings. + This happens when settings_save() tries to save the settings + or transfer to any user-implemented back-end.*/ + .h_export = bacnet_storage_handler_export +}; + /** * @brief Initialize the non-volatile data */ -void bacnet_storage_init(void) +int bacnet_storage_init(bacnet_storage_restore_callback restore_cb) { - int rc; + int rc = 0; + BACnet_Storage_Restore_Callback = restore_cb; #if defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_LITTLEFS) FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage); @@ -67,10 +142,18 @@ void bacnet_storage_init(void) rc = settings_subsys_init(); if (rc) { LOG_ERR("settings subsys initialization: fail (err %d)", rc); - return; + return rc; } + rc = settings_register(&bacnet_storage_handler); + if (rc) { + LOG_ERR("settings_register failed (err %d)", rc); + return rc; + } + settings_load(); LOG_INF("settings subsys initialization: OK."); + + return rc; } /** @@ -123,6 +206,142 @@ int bacnet_storage_key_encode(char *buffer, size_t buffer_size, BACNET_STORAGE_K return rc; } +/** + * @brief Decode a storage key string into a BACnet object property + * @param name settings name key string + * @param key BACnet key (type, instance, property, array index) + * @return 0=success, negative on error + */ +int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) +{ + const char *next; + size_t next_len; + char object_type_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char object_instance_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char property_id_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char array_index_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + unsigned long long_value = 0; + const char base_name[] = CONFIG_BACNET_STORAGE_BASE_NAME; + + /* settings root name */ + if (settings_name_steq(path, base_name, &next) && next) { + /* OPTIONAL - called from shell */ + path = next; + } + /* object-type */ + next_len = settings_name_next(path, &next); + if (next) { + if (next_len + 1 > sizeof(object_type_name)) { + LOG_ERR("object-type name too long: %d", next_len); + return -EINVAL; + } + memcpy(object_type_name, path, next_len); + if (bacnet_storage_strtoul(object_type_name, &long_value)) { + key->object_type = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* object-instance */ + path = next; + next_len = settings_name_next(path, &next); + if (next) { + if (next_len + 1 > sizeof(object_instance_name)) { + LOG_ERR("object-instance name too long: %d", next_len); + return -EINVAL; + } + memcpy(object_instance_name, path, next_len); + if (bacnet_storage_strtoul(object_instance_name, &long_value)) { + key->object_instance = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* property-id */ + path = next; + next_len = settings_name_next(path, &next); + if (next) { + if (next_len + 1 > sizeof(property_id_name)) { + LOG_ERR("property-id name too long: %d", next_len); + return -EINVAL; + } + memcpy(property_id_name, path, next_len); + if (bacnet_storage_strtoul(property_id_name, &long_value)) { + key->property_id = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* array-index - OPTIONAL */ + path = next; + next_len = settings_name_next(path, &next); + if (next) { + if (next_len + 1 > sizeof(array_index_name)) { + LOG_ERR("array-index name too long: %d", next_len); + return -EINVAL; + } + memcpy(array_index_name, path, next_len); + if (bacnet_storage_strtoul(array_index_name, &long_value)) { + key->array_index = long_value; + } else { + return -EINVAL; + } + } else { + key->array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; + } + + return 0; +} + +int bacnet_storage_handler_set(const char *path, size_t data_len, + settings_read_cb read_cb, void *cb_arg) +{ + int rc = -EINVAL; + BACNET_STORAGE_KEY key = { 0 }; + uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX] = { 0 }; + + if (bacnet_storage_key_decode(path, &key)==0) { + /* get the data if there is any */ + if (data_len == 0) { + rc = 0; + } else { + rc = read_cb(cb_arg, &data, sizeof(data)); + if (rc < 0) { + if (rc == -ENOENT) { + rc = 0; + } else { + LOG_ERR("Data restore error: %d", rc); + + } + } else { + bacnet_storage_restore(&key, data, data_len); + LOG_INF("Data restored:%s %d bytes", path, data_len); + } + } + } + + return rc; +} + +int bacnet_storage_handler_commit(void) +{ + LOG_INF("Restored all settings"); + return 0; +} + +int bacnet_storage_handler_export(int (*cb)(const char *name, + const void *value, size_t val_len)) +{ + LOG_INF("Export requested"); + return 0; +} + /** * @brief Set a value with a specific key to non-volatile storage * @param key [in] Key in string format. From 89bfd871439459c42446290b5ff08972612cfb7f Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Mon, 8 Sep 2025 14:32:14 -0500 Subject: [PATCH 2/9] Refactor BACnet settings to use settings_load as iterator. --- .pre-commit-config.yaml | 10 - .../include/bacnet_settings/bacnet_settings.h | 19 +- .../include/bacnet_settings/bacnet_storage.h | 19 +- zephyr/samples/profiles/b-ld/src/main.c | 60 ++--- zephyr/samples/profiles/b-ls/src/main.c | 72 ++---- zephyr/samples/profiles/b-sa/src/main.c | 49 ++--- zephyr/samples/profiles/b-ss/src/main.c | 63 +----- .../subsys/bacnet_settings/bacnet_settings.c | 145 +++++++++--- zephyr/subsys/bacnet_settings/bacnet_shell.c | 100 ++++++++- .../subsys/bacnet_settings/bacnet_storage.c | 206 ++++++++++++------ 10 files changed, 431 insertions(+), 312 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6c493f9..4a827bc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,10 +38,6 @@ repos: - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.5.5 hooks: - - id: remove-tabs - name: Remove tabs (4 spaces) - args: ["--whitespaces-count", "4"] - exclude: \.(yaml|yml|html|htm|sln|atsln|layout)$|Makefile|\.(mgw|mak|MAK)$|Dockerfile$ - id: remove-tabs name: Remove tabs (2 spaces) args: ["--whitespaces-count", "2"] @@ -52,12 +48,6 @@ repos: hooks: - id: clang-format - # We might enable this in future. - # - repo: https://github.com/pre-commit/mirrors-prettier - # rev: ffb6a759a979008c0e6dff86e39f4745a2d9eac4 # v3.1.0 - # hooks: - # - id: prettier - - repo: https://github.com/pre-commit/pygrep-hooks rev: v1.10.0 hooks: diff --git a/zephyr/include/bacnet_settings/bacnet_settings.h b/zephyr/include/bacnet_settings/bacnet_settings.h index b21698e..90a0306 100644 --- a/zephyr/include/bacnet_settings/bacnet_settings.h +++ b/zephyr/include/bacnet_settings/bacnet_settings.h @@ -17,6 +17,15 @@ #include "bacnet/bacint.h" #include "bacnet/wp.h" +/** + * @brief Callback data for WriteProperty restore iterator + * @param write_function The WriteProperty function to call + * @param context The context to pass to the WriteProperty function + * @return true if the WriteProperty succeeded + */ +typedef bool (*bacnet_settings_restore_callback)(BACNET_WRITE_PROPERTY_DATA *wp_data, + void *context); + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ @@ -25,13 +34,7 @@ void bacnet_settings_basic_store(BACNET_OBJECT_TYPE object_type, uint32_t object BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index, uint8_t *application_data, int application_data_len); bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data); -bool bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, - const void *data, size_t data_len, - write_property_function write_function); -bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, - write_property_function write_function); +bool bacnet_settings_write_property_restore(bacnet_settings_restore_callback cb, void *context); 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); @@ -71,6 +74,8 @@ int bacnet_settings_string_get(uint16_t object_type, uint32_t object_instance, u bool bacnet_settings_string_set(uint16_t object_type, uint32_t object_instance, uint32_t property_id, uint32_t array_index, const char *value); +bool bacnet_settings_init(void); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/zephyr/include/bacnet_settings/bacnet_storage.h b/zephyr/include/bacnet_settings/bacnet_storage.h index e966b4a..abf2a00 100644 --- a/zephyr/include/bacnet_settings/bacnet_storage.h +++ b/zephyr/include/bacnet_settings/bacnet_storage.h @@ -11,6 +11,8 @@ #include #include +#include +#include #include #define BACNET_STORAGE_VALUE_SIZE_MAX SETTINGS_MAX_VAL_LEN @@ -25,19 +27,32 @@ typedef struct bacnet_storage_key_t { } BACNET_STORAGE_KEY; typedef int (*bacnet_storage_restore_callback)(BACNET_STORAGE_KEY *key, const void *data, - size_t data_size); + size_t data_size, void *context); #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -int bacnet_storage_init(bacnet_storage_restore_callback cb); +int bacnet_storage_init(void); + +int bacnet_storage_load_callback_set(bacnet_storage_restore_callback cb, void *context); +int bacnet_storage_load(void); + +int bacnet_storage_handler_set(const char *name, size_t len, settings_read_cb read_cb, + void *cb_arg); +int bacnet_storage_handler_commit(void); +int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, size_t val_len)); + +bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value); + 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); int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key); int bacnet_storage_key_encode(char *buffer, size_t buffer_size, BACNET_STORAGE_KEY *key); + int bacnet_storage_set(BACNET_STORAGE_KEY *key, const void *data, size_t data_size); int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size); +int bacnet_storage_delete(BACNET_STORAGE_KEY *key); #ifdef __cplusplus } diff --git a/zephyr/samples/profiles/b-ld/src/main.c b/zephyr/samples/profiles/b-ld/src/main.c index 0e0b401..8ca34f1 100644 --- a/zephyr/samples/profiles/b-ld/src/main.c +++ b/zephyr/samples/profiles/b-ld/src/main.c @@ -64,6 +64,18 @@ void BACnet_Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, flo (double)value, (unsigned)steps, (unsigned)UINT16_MAX); } +/** + * @brief Callback data for WriteProperty restore iterator + * @param write_function The WriteProperty function to call + * @param context The context to pass to the WriteProperty function + * @return true if the WriteProperty succeeded + */ +static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +{ + (void)context; + return Device_Write_Property(wp_data); +} + /** * @brief BACnet Project Initialization Handler * @param context [in] The context to pass to the callback function @@ -71,31 +83,6 @@ void BACnet_Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, flo */ static void BACnet_Lighting_Device_Init_Handler(void *context) { - uint32_t array_index = BACNET_ARRAY_ALL; - bool status = false; - int i; - int32_t lighting_output_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OUT_OF_SERVICE, - PROP_DEFAULT_FADE_TIME, - PROP_DEFAULT_RAMP_RATE, - PROP_DEFAULT_STEP_INCREMENT, - PROP_TRANSITION, - PROP_PRESENT_VALUE, - PROP_RELINQUISH_DEFAULT, - PROP_BLINK_WARN_ENABLE, - PROP_EGRESS_TIME, - PROP_DEFAULT_FADE_TIME, - PROP_DEFAULT_RAMP_RATE, - PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, - PROP_DEFAULT_STEP_INCREMENT, - PROP_TRANSITION}; - int32_t device_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - }; - (void)context; LOG_INF("BACnet Stack Initialized"); /* initialize objects with default values for this basic sample */ @@ -105,25 +92,10 @@ static void BACnet_Lighting_Device_Init_Handler(void *context) Lighting_Output_Create(Lighting_Instance); Lighting_Output_Name_Set(Lighting_Instance, "Light-1"); /* restore any property values previously stored via WriteProperty */ - for (i = 0; i < ARRAY_SIZE(device_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_DEVICE, BACNET_MAX_INSTANCE, device_writeable_property_list[i], - array_index, Device_Write_Property_Local); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - for (i = 0; i < ARRAY_SIZE(lighting_output_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_LIGHTING_OUTPUT, Lighting_Instance, - lighting_output_writeable_property_list[i], array_index, - Lighting_Output_Write_Property); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - /* These writable property values are stored WriteProperty. - Set this callback after init to prevent recursion. */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ bacnet_basic_store_callback_set(bacnet_settings_basic_store); /* lighting output callbacks */ Lighting_Output_Write_Present_Value_Callback_Set( diff --git a/zephyr/samples/profiles/b-ls/src/main.c b/zephyr/samples/profiles/b-ls/src/main.c index 12206cb..a708385 100644 --- a/zephyr/samples/profiles/b-ls/src/main.c +++ b/zephyr/samples/profiles/b-ls/src/main.c @@ -101,6 +101,18 @@ void Binary_Lighting_Output_Blink_Warn_Handler(uint32_t object_instance) LOG_INF("Binary Lighting Output[%lu]: Blink Warning", (unsigned long)object_instance); } +/** + * @brief Callback data for WriteProperty restore iterator + * @param write_function The WriteProperty function to call + * @param context The context to pass to the WriteProperty function + * @return true if the WriteProperty succeeded + */ +static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +{ + (void)context; + return Device_Write_Property(wp_data); +} + /** * @brief BACnet Project Initialization Handler * @param context [in] The context to pass to the callback function @@ -109,35 +121,6 @@ void Binary_Lighting_Output_Blink_Warn_Handler(uint32_t object_instance) static void BACnet_Lighting_Device_Init_Handler(void *context) { BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE member; - uint32_t array_index = BACNET_ARRAY_ALL; - bool status = false; - int i; - int32_t channel_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_PRESENT_VALUE, PROP_OUT_OF_SERVICE, PROP_LIST_OF_OBJECT_PROPERTY_REFERENCES, - PROP_CHANNEL_NUMBER, PROP_CONTROL_GROUPS, - }; - int32_t lighting_output_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OUT_OF_SERVICE, - PROP_DEFAULT_FADE_TIME, - PROP_DEFAULT_RAMP_RATE, - PROP_DEFAULT_STEP_INCREMENT, - PROP_TRANSITION, - PROP_PRESENT_VALUE, - PROP_RELINQUISH_DEFAULT, - PROP_BLINK_WARN_ENABLE, - PROP_EGRESS_TIME, - PROP_DEFAULT_FADE_TIME, - PROP_DEFAULT_RAMP_RATE, - PROP_LIGHTING_COMMAND_DEFAULT_PRIORITY, - PROP_DEFAULT_STEP_INCREMENT, - PROP_TRANSITION}; - int32_t device_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - }; (void)context; LOG_INF("BACnet Stack Initialized"); @@ -164,33 +147,10 @@ static void BACnet_Lighting_Device_Init_Handler(void *context) member.deviceIdentifier.instance = Device_Instance; Channel_Reference_List_Member_Element_Set(Channel_Instance, 1, &member); /* restore any property values previously stored via WriteProperty */ - for (i = 0; i < ARRAY_SIZE(device_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_DEVICE, BACNET_MAX_INSTANCE, device_writeable_property_list[i], - array_index, Device_Write_Property_Local); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - for (i = 0; i < ARRAY_SIZE(lighting_output_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_LIGHTING_OUTPUT, Lighting_Instance, - lighting_output_writeable_property_list[i], array_index, - Lighting_Output_Write_Property); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - for (i = 0; i < ARRAY_SIZE(channel_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_CHANNEL, Channel_Instance, channel_writeable_property_list[i], - array_index, Channel_Write_Property); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - /* These writable property values are stored WriteProperty. - Set this callback after init to prevent recursion. */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ bacnet_basic_store_callback_set(bacnet_settings_basic_store); /* lighting output callbacks */ Lighting_Output_Write_Present_Value_Callback_Set(Lighting_Output_Tracking_Value_Handler); diff --git a/zephyr/samples/profiles/b-sa/src/main.c b/zephyr/samples/profiles/b-sa/src/main.c index ec7dee3..6a025e2 100644 --- a/zephyr/samples/profiles/b-sa/src/main.c +++ b/zephyr/samples/profiles/b-sa/src/main.c @@ -44,6 +44,18 @@ static void BACnet_Smart_Actuator_Datalink_Init(void) /* nothing to do */ } +/** + * @brief Callback data for WriteProperty restore iterator + * @param write_function The WriteProperty function to call + * @param context The context to pass to the WriteProperty function + * @return true if the WriteProperty succeeded + */ +static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +{ + (void)context; + return Device_Write_Property(wp_data); +} + /** * @brief BACnet Project Initialization Handler * @param context [in] The context to pass to the callback function @@ -51,20 +63,6 @@ static void BACnet_Smart_Actuator_Datalink_Init(void) */ static void BACnet_Smart_Actuator_Init_Handler(void *context) { - uint32_t array_index = BACNET_ARRAY_ALL; - bool status = false; - int i; - int32_t analog_output_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OUT_OF_SERVICE, PROP_PRESENT_VALUE, PROP_UNITS, - PROP_COV_INCREMENT, PROP_MIN_PRES_VALUE, PROP_MAX_PRES_VALUE, - }; - int32_t device_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - }; - (void)context; LOG_INF("BACnet Stack Initialized"); BACnet_Smart_Actuator_Datalink_Init(); @@ -78,25 +76,10 @@ static void BACnet_Smart_Actuator_Init_Handler(void *context) Analog_Output_Min_Pres_Value_Set(Actuator_Instance, 0.0f); Analog_Output_Max_Pres_Value_Set(Actuator_Instance, 100.0f); /* restore any property values previously stored via WriteProperty */ - for (i = 0; i < ARRAY_SIZE(device_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_DEVICE, BACNET_MAX_INSTANCE, device_writeable_property_list[i], - array_index, Device_Write_Property_Local); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - for (i = 0; i < ARRAY_SIZE(analog_output_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_ANALOG_OUTPUT, Actuator_Instance, - analog_output_writeable_property_list[i], array_index, - Analog_Output_Write_Property); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - /* These writable property values are stored WriteProperty. - Set this callback after init to prevent recursion. */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ bacnet_basic_store_callback_set(bacnet_settings_basic_store); LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); /* start the seconds cyclic timer */ diff --git a/zephyr/samples/profiles/b-ss/src/main.c b/zephyr/samples/profiles/b-ss/src/main.c index 355896a..293ae85 100644 --- a/zephyr/samples/profiles/b-ss/src/main.c +++ b/zephyr/samples/profiles/b-ss/src/main.c @@ -39,20 +39,16 @@ static const uint32_t Sensor_Instance = 1; /* timer for Sensor Update Interval */ static struct mstimer Sensor_Update_Timer; -int BACnet_Smart_Sensor_Settings_Restore( - BACNET_STORAGE_KEY *key, const void *data, size_t data_len) +/** + * @brief Callback data for WriteProperty restore iterator + * @param write_function The WriteProperty function to call + * @param context The context to pass to the WriteProperty function + * @return true if the WriteProperty succeeded + */ +static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { - bool status; - int err = 0; - - status = bacnet_settings_restore(key->object_type, key->object_instance, - key->property_id, key->array_index, - data, data_len, Device_Write_Property); - if (!status) { - err = -EACCES; - } - - return err; + (void)context; + return Device_Write_Property(wp_data); } /** @@ -63,23 +59,6 @@ int BACnet_Smart_Sensor_Settings_Restore( static void BACnet_Smart_Sensor_Init_Handler(void *context) { const float default_temperature = 25.0f; -#if 0 - uint32_t array_index = BACNET_ARRAY_ALL; - bool status = false; - int i; - int32_t analog_input_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OUT_OF_SERVICE, - PROP_PRESENT_VALUE, - PROP_UNITS, - PROP_COV_INCREMENT, - }; - int32_t device_writeable_property_list[] = { - /* list of properties to set via WriteProperty */ - PROP_OBJECT_IDENTIFIER, - PROP_OBJECT_NAME, - }; -#endif (void)context; LOG_INF("BACnet Stack Initialized"); @@ -92,28 +71,10 @@ static void BACnet_Smart_Sensor_Init_Handler(void *context) Analog_Input_Units_Set(Sensor_Instance, UNITS_DEGREES_CELSIUS); Analog_Input_COV_Increment_Set(Sensor_Instance, 1.0f); /* restore any property values previously stored via WriteProperty */ - bacnet_storage_init(BACnet_Smart_Sensor_Settings_Restore); -#if 0 - for (i = 0; i < ARRAY_SIZE(device_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_DEVICE, BACNET_MAX_INSTANCE, device_writeable_property_list[i], - array_index, Device_Write_Property_Local); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } - for (i = 0; i < ARRAY_SIZE(analog_input_writeable_property_list); i++) { - status = bacnet_settings_write_property_restore( - OBJECT_ANALOG_INPUT, Sensor_Instance, - analog_input_writeable_property_list[i], array_index, - Analog_Input_Write_Property); - if (!status) { - /* no settings stored for this property, use defaults */ - } - } -#endif + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); /* writable property values are stored with WriteProperty. - Set this callback after init to prevent recursion. */ + Set this callback after restore to prevent recursion. */ bacnet_basic_store_callback_set(bacnet_settings_basic_store); LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); /* start the seconds cyclic timer */ diff --git a/zephyr/subsys/bacnet_settings/bacnet_settings.c b/zephyr/subsys/bacnet_settings/bacnet_settings.c index 785f320..7f40931 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_settings.c +++ b/zephyr/subsys/bacnet_settings/bacnet_settings.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include "bacnet/bacdef.h" @@ -19,6 +18,9 @@ #include "bacnet/proplist.h" #include "bacnet/wp.h" +/* Callback for restore */ +static bacnet_settings_restore_callback Restore_Callback; + /** * @brief Store the BACnet data after a WriteProperty for object property * @param object_type - BACnet object type @@ -79,23 +81,28 @@ bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data) } /** - * @brief Callback from the Zephyr settings_restore iterator - * @param key [in] The BACnet object type + * @brief Write data to the write_function for the specific object + * instance property. + * @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 array_index [in] The BACnet array index or priority if commandable + * @param data [in] The data to restore + * @param data_len [in] The length of the data * @param write_function [in] the WriteProperty function of the object - * @return true on success, false on failure. + * @param context [in] The context to pass to the WriteProperty function + * @return 0 on success, negative on failure. */ -bool bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, - const void *data, size_t data_len, - write_property_function write_function) +static int bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, + uint32_t property_id, uint32_t array_index, const void *data, + size_t data_len, + bacnet_settings_restore_callback restore_function, void *context) { + int err = -EINVAL; bool status = false; BACNET_WRITE_PROPERTY_DATA wp_data = {0}; - if ((data_len > 0) && (data_len <= MAX_APDU)) { + if (data && (data_len > 0) && (data_len <= MAX_APDU)) { wp_data.application_data_len = data_len; memcpy(&wp_data.application_data[0], data, data_len); wp_data.object_type = object_type; @@ -109,39 +116,95 @@ bool bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, wp_data.priority = BACNET_MAX_PRIORITY; wp_data.array_index = array_index; } - status = write_function(&wp_data); + if (restore_function) { + status = restore_function(&wp_data, context); + if (status) { + err = 0; + } else { + /* map the BACnet Error to Zephyr Error */ + switch (wp_data.error_code) { + case ERROR_CODE_UNKNOWN_OBJECT: + case ERROR_CODE_UNKNOWN_PROPERTY: + err = -ENOENT; + break; + case ERROR_CODE_WRITE_ACCESS_DENIED: + err = -EACCES; + break; + case ERROR_CODE_DUPLICATE_NAME: + err = -EEXIST; + break; + case ERROR_CODE_VALUE_OUT_OF_RANGE: + err = -ERANGE; + break; + case ERROR_CODE_INVALID_DATA_TYPE: + case ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY: + err = -EINVAL; + break; + case ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY: + err = -ENOSPC; + break; + case ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED: + case ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED: + err = -ENOTSUP; + break; + default: + err = -EINVAL; + break; + } + } + } } - return status; + return err; } /** - * @brief Get a BACnet encoded value from non-volatile storage - * and write it to the object specific WriteProperty handler. - * @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 write_function [in] the WriteProperty function of the object + * @brief Callback from the Zephyr settings_restore iterator + * @param key [in] The BACnet object type + * @param data [in] The data to restore + * @param data_len [in] The length of the data + * @return 0 on success, negative on failure. + */ +static int bacnet_storage_restore_handler(BACNET_STORAGE_KEY *key, const void *data, + size_t data_len, void *context) +{ + bool status; + int err = 0; + + status = bacnet_settings_restore(key->object_type, key->object_instance, key->property_id, + key->array_index, data, data_len, Restore_Callback, + context); + if (!status) { + err = -EACCES; + } + + return err; +} + +/** + * @brief Utilize the settings_restore iterator + * @param write_function [in] the WriteProperty function of the device object * @return true on success, false on failure. */ -bool bacnet_settings_write_property_restore(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, - write_property_function write_function) +bool bacnet_settings_write_property_restore(bacnet_settings_restore_callback cb, void *context) { - bool status = false; - uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX + 1] = {0}; - BACNET_STORAGE_KEY key = {0}; - int data_len; + int err; - bacnet_storage_key_init(&key, object_type, object_instance, property_id, array_index); - data_len = bacnet_storage_get(&key, data, sizeof(data)); - status = bacnet_settings_restore(object_type, object_instance, - property_id, array_index, - data, data_len, - write_function); + if (!cb) { + return false; + } + Restore_Callback = cb; + err = bacnet_storage_load_callback_set(bacnet_storage_restore_handler, context); + if (err) { + return false; + } + /* iterate over all stored settings and call the restore callback for each */ + err = bacnet_storage_load(); + if (err) { + return false; + } - return status; + return true; } /** @@ -478,3 +541,19 @@ bool bacnet_settings_string_set(uint16_t object_type, uint32_t object_instance, return rc == 0; } + +/** + * @brief Initialize the BACnet settings storage + * @return true=success, false on error + */ +bool bacnet_settings_init(void) +{ + int err; + + err = bacnet_storage_init(); + if (err) { + return false; + } + + return true; +} diff --git a/zephyr/subsys/bacnet_settings/bacnet_shell.c b/zephyr/subsys/bacnet_settings/bacnet_shell.c index 41ff29f..3331441 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_shell.c +++ b/zephyr/subsys/bacnet_settings/bacnet_shell.c @@ -11,6 +11,9 @@ #include #include #include +#include + +static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; /** * @brief Get or set a string using BACnet storage subsystem @@ -89,11 +92,11 @@ static int cmd_string(const struct shell *sh, size_t argc, char **argv) (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); /* convert the key string to numbers for a test */ if (bacnet_storage_key_decode(key_name, &test_key) == 0) { - shell_print(sh, "key=%lu/%lu/%lu/%lu", - (unsigned long)test_key.object_type, - (unsigned long)test_key.object_instance, - (unsigned long)test_key.property_id, - (unsigned long)test_key.array_index); + shell_print(sh, "key=%s/%lu/%lu/%lu/%lu", Storage_Base_Name, + (unsigned long)test_key.object_type, + (unsigned long)test_key.object_instance, + (unsigned long)test_key.property_id, + (unsigned long)test_key.array_index); } if (argc > 4) { arg_len = strlen(argv[4]); @@ -116,10 +119,89 @@ static int cmd_string(const struct shell *sh, size_t argc, char **argv) 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); +/** + * @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_delete(const struct shell *sh, size_t argc, char **argv) +{ + char key_name[BACNET_STORAGE_KEY_SIZE_MAX + 1] = {0}; + BACNET_STORAGE_KEY key = {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 > 3) { + rc = bacnet_storage_delete(&key); + if (rc == 0) { + shell_print(sh, "Deleted %s", key_name); + } else { + shell_error(sh, "Unable to delete %s", key_name); + return -EINVAL; + } + } + + return 0; +} + +/** + * @brief Callback from the Zephyr settings_restore iterator + * @param key [in] The BACnet object type + * @param data [in] The data to restore + * @param data_len [in] The length of the data + * @return 0 on success, negative on failure. + */ +static int print_storage_data(BACNET_STORAGE_KEY *key, const void *data, size_t data_len, + void *context) +{ + const struct shell *sh = context; + if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) { + shell_print(sh, "%s/%u/%u/%u", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id); + } else { + shell_print(sh, "%s/%u/%u/%u/%u", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, key->array_index); + } + + return 0; +} + +/** + * @brief List all the BACnet stored settings + * @param sh Shell + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + */ +static int cmd_list(const struct shell *sh, size_t argc, char **argv) +{ + int err; + + err = bacnet_storage_load_callback_set(print_storage_data, (void *)sh); + if (err) { + shell_error(sh, "Failed to set storage load callback"); + return -EINVAL; + } + err = bacnet_storage_load(); + if (err) { + shell_error(sh, "Failed to load storage"); + return -EINVAL; + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_bacnet_settings_cmds, SHELL_CMD(list, NULL, "list BACnet storage strings", cmd_list), + SHELL_CMD(string, NULL, "get or set BACnet storage string", cmd_string), + SHELL_CMD(delete, NULL, "delete BACnet storage string", cmd_delete), SHELL_SUBCMD_SET_END); SHELL_SUBCMD_ADD((bacnet), settings, &sub_bacnet_settings_cmds, "BACnet settings commands", NULL, 1, 0); diff --git a/zephyr/subsys/bacnet_settings/bacnet_storage.c b/zephyr/subsys/bacnet_settings/bacnet_storage.c index b21f5a1..3009d0d 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_storage.c +++ b/zephyr/subsys/bacnet_settings/bacnet_storage.c @@ -37,14 +37,22 @@ LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); #define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION) static bacnet_storage_restore_callback BACnet_Storage_Restore_Callback; +static void *BACnet_Storage_Restore_Context; -int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, - size_t data_size) +/** + * @brief Restore a BACnet storage item + * @param key BACnet key (type, instance, property, array index) + * @param data - pointer to the data to restore + * @param data_size - size of the data to restore + * @return 0 on success, negative on failure + */ +int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, size_t data_size) { int err = 0; if (BACnet_Storage_Restore_Callback) { - err = BACnet_Storage_Restore_Callback(key, data, data_size); + err = BACnet_Storage_Restore_Callback(key, data, data_size, + BACnet_Storage_Restore_Context); } return err; @@ -59,65 +67,57 @@ int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, */ bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) { - char *endptr; - unsigned long value; - - value = strtoul(search_name, &endptr, 0); - if (endptr == search_name) { - /* No digits found */ - return false; - } - if (value == ULONG_MAX) { - /* If the correct value is outside the range of representable values, - {ULONG_MAX} shall be returned */ - return false; - } - if (*endptr != '\0') { - /* Extra text found */ - return false; - } + char *endptr; + unsigned long value; + + value = strtoul(search_name, &endptr, 0); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if (value == ULONG_MAX) { + /* If the correct value is outside the range of representable values, + {ULONG_MAX} shall be returned */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } if (long_value) { - *long_value = (unsigned)value; + *long_value = (unsigned)value; } - return true; + return true; } -int bacnet_storage_handler_set(const char *name, size_t len, settings_read_cb read_cb, - void *cb_arg); -int bacnet_storage_handler_commit(void); -int bacnet_storage_handler_export(int (*cb)(const char *name, - const void *value, size_t val_len)); - /* dynamic main tree handler */ struct settings_handler bacnet_storage_handler = { - .name = "bacnet", - /* This gets called when asking for a settings element value - by its name using settings_runtime_get() from the runtime backend.*/ - .h_get = NULL, - /* This gets called when the value is loaded from persisted storage - with settings_load(), or when using settings_runtime_set() from - the runtime backend.*/ - .h_set = bacnet_storage_handler_set, - /* This gets called after the settings have been loaded in full. - Sometimes you don’t want an individual setting value to take - effect right away, for example if there are multiple settings - which are interdependent.*/ - .h_commit = bacnet_storage_handler_commit, - /* This gets called to write all current settings. - This happens when settings_save() tries to save the settings - or transfer to any user-implemented back-end.*/ - .h_export = bacnet_storage_handler_export -}; + .name = "bacnet", + /* This gets called when asking for a settings element value + by its name using settings_runtime_get() from the runtime backend.*/ + .h_get = NULL, + /* This gets called when the value is loaded from persisted storage + with settings_load(), or when using settings_runtime_set() from + the runtime backend.*/ + .h_set = bacnet_storage_handler_set, + /* This gets called after the settings have been loaded in full. + Sometimes you don’t want an individual setting value to take + effect right away, for example if there are multiple settings + which are interdependent.*/ + .h_commit = bacnet_storage_handler_commit, + /* This gets called to write all current settings. + This happens when settings_save() tries to save the settings + or transfer to any user-implemented back-end.*/ + .h_export = bacnet_storage_handler_export}; /** * @brief Initialize the non-volatile data */ -int bacnet_storage_init(bacnet_storage_restore_callback restore_cb) +int bacnet_storage_init(void) { int rc = 0; - BACnet_Storage_Restore_Callback = restore_cb; #if defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_LITTLEFS) FS_LITTLEFS_DECLARE_DEFAULT_CONFIG(cstorage); @@ -149,13 +149,45 @@ int bacnet_storage_init(bacnet_storage_restore_callback restore_cb) LOG_ERR("settings_register failed (err %d)", rc); return rc; } - settings_load(); LOG_INF("settings subsys initialization: OK."); return rc; } +/** + * @brief Set the callback function for restoring BACnet storage items + * @param restore_cb - pointer to the restore callback function + * @return 0=success, negative on error + */ +int bacnet_storage_load_callback_set(bacnet_storage_restore_callback restore_cb, void *context) +{ + int rc = 0; + + BACnet_Storage_Restore_Callback = restore_cb; + BACnet_Storage_Restore_Context = context; + + return rc; +} + +/** + * @brief Load all BACnet settings from storage + * @return 0=success, negative on error + */ +int bacnet_storage_load(void) +{ + int rc = 0; + + rc = settings_load(); + if (rc) { + LOG_ERR("settings_load failed (err %d)", rc); + return rc; + } + LOG_INF("settings_load: OK."); + + return rc; +} + /** * @brief Initialize a BACnet key object with optional array * @param key BACnet key (type, instance, property, array index) @@ -216,10 +248,10 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) { const char *next; size_t next_len; - char object_type_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; - char object_instance_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; - char property_id_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; - char array_index_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char object_type_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; + char object_instance_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; + char property_id_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; + char array_index_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; unsigned long long_value = 0; const char base_name[] = CONFIG_BACNET_STORAGE_BASE_NAME; @@ -299,29 +331,39 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) return 0; } -int bacnet_storage_handler_set(const char *path, size_t data_len, - settings_read_cb read_cb, void *cb_arg) +/** + * @brief settings callback: Set a value in BACnet storage + * @param path - settings name key string + * @param data_len - length of the data to set + * @param read_cb - callback to read the value + * @param cb_arg - callback argument + * @return 0=success, negative on error + */ +int bacnet_storage_handler_set(const char *path, size_t data_len, settings_read_cb read_cb, + void *cb_arg) { int rc = -EINVAL; - BACNET_STORAGE_KEY key = { 0 }; - uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX] = { 0 }; + BACNET_STORAGE_KEY key = {0}; + uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX] = {0}; - if (bacnet_storage_key_decode(path, &key)==0) { + if (bacnet_storage_key_decode(path, &key) == 0) { /* get the data if there is any */ if (data_len == 0) { rc = 0; } else { rc = read_cb(cb_arg, &data, sizeof(data)); if (rc < 0) { + /* On error returns -ERRNO code. */ if (rc == -ENOENT) { rc = 0; } else { LOG_ERR("Data restore error: %d", rc); - } } else { - bacnet_storage_restore(&key, data, data_len); - LOG_INF("Data restored:%s %d bytes", path, data_len); + rc = bacnet_storage_restore(&key, data, data_len); + if (rc == 0) { + LOG_INF("Data restored:%s %d bytes", path, data_len); + } } } } @@ -329,22 +371,32 @@ int bacnet_storage_handler_set(const char *path, size_t data_len, return rc; } +/** + * @brief settings callback: Commit all changes to BACnet storage + * @return 0=success, negative on error + */ int bacnet_storage_handler_commit(void) { - LOG_INF("Restored all settings"); + LOG_INF("Restored all settings"); return 0; } -int bacnet_storage_handler_export(int (*cb)(const char *name, - const void *value, size_t val_len)) +/** + * @brief settings callback: This gets called to write all current settings. + * This happens when settings_save() tries to save the settings + * or transfer to any user-implemented back-end. + * @param cb - callback function to receive the settings + * @return 0=success, negative on error + */ +int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, size_t val_len)) { - LOG_INF("Export requested"); + LOG_INF("FIXME: Export requested"); return 0; } /** * @brief Set a value with a specific key to non-volatile storage - * @param key [in] Key in string format. + * @param key BACnet key (type, instance, property, array index) * @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. @@ -442,7 +494,7 @@ static int load_immediate_value(const char *name, void *value, size_t value_size /** * @brief Get a value with a specific key to non-volatile storage - * @param key [in] Key in string format. + * @param key BACnet key (type, instance, property, array index) * @param data [out] Binary value. * @param data_size [in] requested value length in bytes * @return data length on success 0..N, negative on failure. @@ -467,3 +519,23 @@ int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size) return rc; } + +/** + * @brief Delete a value with a specific key from non-volatile storage + * @param key BACnet key (type, instance, property, array index) + * @return 0 on success, non-zero on failure. + */ +int bacnet_storage_delete(BACNET_STORAGE_KEY *key) +{ + char name[SETTINGS_MAX_NAME_LEN + 1] = {0}; + int rc; + + rc = bacnet_storage_key_encode(name, sizeof(name), key); + LOG_INF("Delete a key-value pair. Key=%s", name); + rc = settings_delete(name); + if (rc) { + LOG_INF(FAIL_MSG, rc); + } + + return rc; +} From a3f74960e50b21093521991cf08f4b86f250be7b Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Mon, 8 Sep 2025 18:10:21 -0500 Subject: [PATCH 3/9] [WIP] improving shell to get/set values --- west.yml | 2 +- zephyr/samples/profiles/b-ld/sample.yaml | 2 + zephyr/subsys/bacnet_settings/bacnet_shell.c | 133 ++++++++++++++++++- 3 files changed, 133 insertions(+), 4 deletions(-) diff --git a/west.yml b/west.yml index d041c87..d22cdec 100644 --- a/west.yml +++ b/west.yml @@ -41,7 +41,7 @@ manifest: revision: master - name: zephyr path: zephyr - revision: v3.7.0 + revision: v3.7.1 remote: zephyrproject-rtos west-commands: scripts/west-commands.yml import: diff --git a/zephyr/samples/profiles/b-ld/sample.yaml b/zephyr/samples/profiles/b-ld/sample.yaml index c2530e0..06d04b3 100644 --- a/zephyr/samples/profiles/b-ld/sample.yaml +++ b/zephyr/samples/profiles/b-ld/sample.yaml @@ -4,6 +4,8 @@ sample: common: platform_allow: - nucleo_f429zi + - rpi_pico + - adafruit_grand_central_m4_express build_only: true tests: diff --git a/zephyr/subsys/bacnet_settings/bacnet_shell.c b/zephyr/subsys/bacnet_settings/bacnet_shell.c index 3331441..a43693a 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_shell.c +++ b/zephyr/subsys/bacnet_settings/bacnet_shell.c @@ -11,6 +11,7 @@ #include #include #include +#include #include static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; @@ -29,6 +30,7 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, uint32_t property_id = 77; uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; long value = 0; + unsigned long unsigned_value = 0; int found_index = 0; if (argc < 3) { @@ -46,12 +48,31 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, return -EINVAL; } object_type = (uint16_t)value; - value = strtoul(argv[2], NULL, 0); - if (value > 4194303) { + if (!bacnet_storage_strtoul(argv[2], &unsigned_value)) { + shell_error(sh, "Invalid object-instance: %s.", argv[2]); + return -EINVAL; + } + if (unsigned_value > 4194303) { shell_error(sh, "Invalid object-instance: %s. Must be 0-4194303.", argv[2]); return -EINVAL; } - object_instance = (uint32_t)value; + object_instance = (uint32_t)unsigned_value; + /* property can have @ to denote priority or array */ + char *at_ptr = strchr(argv[3], '@'); + if (at_ptr) { + if (!bacnet_storage_strtoul(at_ptr + 1, &unsigned_value)) { + shell_error(sh, "Invalid array-index: \"%s\"", at_ptr); + return -EINVAL; + } + if (unsigned_value > UINT32_MAX) { + shell_error(sh, "Invalid array-index: \"%s\". Must be 0-4294967295.", + at_ptr); + return -EINVAL; + } + array_index = (uint32_t)unsigned_value; + /* null terminate the string at the @ symbol */ + *at_ptr = 0; + } if (bactext_property_strtol(argv[3], &found_index)) { value = found_index; } else { @@ -69,6 +90,111 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, 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_value(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}; + int rc; + unsigned enumerated_value = 0; + bool status = false; + bool null_value = false; + BACNET_APPLICATION_DATA_VALUE value = {0}; + BACNET_OBJECT_PROPERTY_VALUE object_value = {0}; + char value_name[80] = {0}; + char *value_string = NULL; + uint8_t value_tag; + int len; + + rc = cmd_key(&key, sh, argc, argv); + if (rc < 0) { + return rc; + } + /* convert the key to a string for the shell to print */ + (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); + if (argc > 4) { + value_string = argv[4]; + if (property_list_commandable_member(key.object_type, key.property_id)) { + /* check for case insensitive NULL string */ + if (bacnet_strnicmp(value_string, "NULL", 4) == 0) { + null_value = true; + } + } + /* convert the string value into a tagged union value */ + if (null_value) { + value.tag = BACNET_APPLICATION_TAG_NULL; + status = true; + } else { + value_tag = bacapp_known_property_tag(key.object_type, key.property_id); + /* check for known property types */ + if (value_tag == BACNET_APPLICATION_TAG_ENUMERATED) { + status = bactext_object_property_strtoul( + (BACNET_OBJECT_TYPE)key.object_type, + (BACNET_PROPERTY_ID)key.property_id, value_string, + &enumerated_value); + if (status) { + value.tag = BACNET_APPLICATION_TAG_ENUMERATED; + value.type.Enumerated = (uint32_t)enumerated_value; + } + } else { + status = bacapp_parse_application_data(value_tag, value_string, + &value); + } + } + if (status) { + shell_print(sh, "Parsed %s = %s as tag=%u", key_name, value_string, + value.tag); + len = bacapp_encode_application_data(NULL, &value); + if (len <= 0) { + return false; + } else if (len > sizeof(data)) { + return false; + } + len = bacapp_encode_application_data(data, &value); + rc = bacnet_storage_set(&key, data, len); + if (rc == 0) { + shell_print(sh, "Set %s = %s", key_name, value_string); + } else { + shell_error(sh, "Unable to set %s = %s", key_name, value_string); + return -EINVAL; + } + } else { + shell_error(sh, "Unable to parse value for %s = %s", key_name, + value_string); + 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; + } + /* convert to printable value */ + len = bacapp_decode_known_array_property(data, rc, &value, key.object_type, + key.property_id, key.array_index); + if (len < 0) { + shell_error(sh, "Unable to decode value for %s", key_name); + return -EINVAL; + } + object_value.object_type = key.object_type; + object_value.object_instance = key.object_instance; + object_value.object_property = key.property_id; + object_value.array_index = key.array_index; + object_value.value = &value; + bacapp_snprintf_value(value_name, sizeof(value_name), &object_value); + shell_print(sh, "Get %s = %s", key_name, value_name); + } + + return 0; +} + /** * @brief Get or set a string using BACnet storage subsystem * @param sh Shell @@ -200,6 +326,7 @@ static int cmd_list(const struct shell *sh, size_t argc, char **argv) SHELL_STATIC_SUBCMD_SET_CREATE( sub_bacnet_settings_cmds, SHELL_CMD(list, NULL, "list BACnet storage strings", cmd_list), + SHELL_CMD(value, NULL, "get or set BACnet storage value", cmd_value), SHELL_CMD(string, NULL, "get or set BACnet storage string", cmd_string), SHELL_CMD(delete, NULL, "delete BACnet storage string", cmd_delete), SHELL_SUBCMD_SET_END); From 72fa6c78e47d5fd38ba8241fd9ff9ea7941388bb Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 06:48:16 -0500 Subject: [PATCH 4/9] Include more boards in sample builds --- zephyr/samples/profiles/b-ls/sample.yaml | 2 ++ zephyr/samples/profiles/b-sa/sample.yaml | 2 ++ zephyr/samples/profiles/b-ss/sample.yaml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/zephyr/samples/profiles/b-ls/sample.yaml b/zephyr/samples/profiles/b-ls/sample.yaml index 542a4b3..f249d5f 100644 --- a/zephyr/samples/profiles/b-ls/sample.yaml +++ b/zephyr/samples/profiles/b-ls/sample.yaml @@ -4,6 +4,8 @@ sample: common: platform_allow: - nucleo_f429zi + - rpi_pico + - adafruit_grand_central_m4_express build_only: true tests: diff --git a/zephyr/samples/profiles/b-sa/sample.yaml b/zephyr/samples/profiles/b-sa/sample.yaml index 83c920c..9245bab 100644 --- a/zephyr/samples/profiles/b-sa/sample.yaml +++ b/zephyr/samples/profiles/b-sa/sample.yaml @@ -4,6 +4,8 @@ sample: common: platform_allow: - nucleo_f429zi + - rpi_pico + - adafruit_grand_central_m4_express build_only: true tests: diff --git a/zephyr/samples/profiles/b-ss/sample.yaml b/zephyr/samples/profiles/b-ss/sample.yaml index 2ea6547..1713bab 100644 --- a/zephyr/samples/profiles/b-ss/sample.yaml +++ b/zephyr/samples/profiles/b-ss/sample.yaml @@ -4,6 +4,8 @@ sample: common: platform_allow: - nucleo_f429zi + - rpi_pico + - adafruit_grand_central_m4_express build_only: true tests: From 494112f183a0672ade575cd74bc299ef4ef127e5 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 10:55:29 -0500 Subject: [PATCH 5/9] [WIP] improving shell to get/set values --- .../include/bacnet_settings/bacnet_storage.h | 2 + zephyr/samples/profiles/b-ld/src/main.c | 8 + zephyr/subsys/bacnet_settings/bacnet_shell.c | 203 ++++++++++++------ .../subsys/bacnet_settings/bacnet_storage.c | 94 ++++++-- 4 files changed, 233 insertions(+), 74 deletions(-) diff --git a/zephyr/include/bacnet_settings/bacnet_storage.h b/zephyr/include/bacnet_settings/bacnet_storage.h index abf2a00..f41d614 100644 --- a/zephyr/include/bacnet_settings/bacnet_storage.h +++ b/zephyr/include/bacnet_settings/bacnet_storage.h @@ -44,6 +44,8 @@ int bacnet_storage_handler_commit(void); int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, size_t val_len)); bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value); +bool bacnet_storage_strtol(const char *search_name, long *long_value); +bool bacnet_storage_strtof(const char *search_name, float *float_value); 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); diff --git a/zephyr/samples/profiles/b-ld/src/main.c b/zephyr/samples/profiles/b-ld/src/main.c index 8ca34f1..513206b 100644 --- a/zephyr/samples/profiles/b-ld/src/main.c +++ b/zephyr/samples/profiles/b-ld/src/main.c @@ -73,6 +73,14 @@ void BACnet_Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, flo static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { (void)context; + if (wp_data == NULL) { + return false; + } + if ((wp_data->object_type == OBJECT_DEVICE) && + (wp_data->object_instance == BACNET_MAX_INSTANCE)) { + wp_data->object_instance = Device_Object_Instance_Number(); + } + return Device_Write_Property(wp_data); } diff --git a/zephyr/subsys/bacnet_settings/bacnet_shell.c b/zephyr/subsys/bacnet_settings/bacnet_shell.c index a43693a..7fdbf0f 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_shell.c +++ b/zephyr/subsys/bacnet_settings/bacnet_shell.c @@ -1,6 +1,6 @@ /** * @file - * @brief The BACnet shell commands for debugging and testing + * @brief The BACnet shell commands for debugging and testing settings * @author Steve Karg * @date May 2024 * @copyright SPDX-License-Identifier: Apache-2.0 @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -25,13 +26,14 @@ static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; */ 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; + uint16_t object_type = 0; + uint32_t object_instance = 0; + uint32_t property_id = 75; uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; long value = 0; unsigned long unsigned_value = 0; - int found_index = 0; + int found_index = 0, scan_count = 0; + char property_name[80] = {0}; if (argc < 3) { shell_error(sh, "Usage: %s [value]", argv[0]); @@ -57,33 +59,30 @@ static int cmd_key(BACNET_STORAGE_KEY *key, const struct shell *sh, size_t argc, return -EINVAL; } object_instance = (uint32_t)unsigned_value; - /* property can have @ to denote priority or array */ - char *at_ptr = strchr(argv[3], '@'); - if (at_ptr) { - if (!bacnet_storage_strtoul(at_ptr + 1, &unsigned_value)) { - shell_error(sh, "Invalid array-index: \"%s\"", at_ptr); + /* property can have [] to denote priority or array */ + scan_count = sscanf(argv[3], "%lu[%u]", &unsigned_value, &array_index); + if (scan_count < 1) { + scan_count = sscanf(argv[3], "%80s[%u]", property_name, &array_index); + if (scan_count < 1) { + shell_error(sh, "Invalid property: %s.", argv[3]); return -EINVAL; } - if (unsigned_value > UINT32_MAX) { - shell_error(sh, "Invalid array-index: \"%s\". Must be 0-4294967295.", - at_ptr); + if (bactext_property_strtol(property_name, &found_index)) { + value = found_index; + } else { + shell_error(sh, "Invalid property: %s.", property_name); return -EINVAL; } - array_index = (uint32_t)unsigned_value; - /* null terminate the string at the @ symbol */ - *at_ptr = 0; - } - if (bactext_property_strtol(argv[3], &found_index)) { - value = found_index; - } else { - shell_error(sh, "Invalid property: %s.", argv[1]); - return -EINVAL; + if (value > UINT32_MAX) { + shell_error(sh, "Invalid property: %s. Must be 0-4294967295.", argv[3]); + return -EINVAL; + } + unsigned_value = (uint32_t)value; } - if (value > UINT32_MAX) { - shell_error(sh, "Invalid property: %s. Must be 0-4294967295.", argv[3]); - return -EINVAL; + if (scan_count < 2) { + array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; } - property_id = (uint32_t)value; + property_id = (uint32_t)unsigned_value; /* setup the storage key */ bacnet_storage_key_init(key, object_type, object_instance, property_id, array_index); @@ -102,39 +101,59 @@ static int cmd_value(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}; - int rc; + int rc, len, data_len; unsigned enumerated_value = 0; + unsigned long unsigned_value = 0; + float real_value = 0.0f; + long signed_value = 0; + int scan_count = 0; + unsigned object_type = 0; + unsigned instance = 0; bool status = false; - bool null_value = false; BACNET_APPLICATION_DATA_VALUE value = {0}; BACNET_OBJECT_PROPERTY_VALUE object_value = {0}; char value_name[80] = {0}; char *value_string = NULL; - uint8_t value_tag; - int len; rc = cmd_key(&key, sh, argc, argv); if (rc < 0) { return rc; } + /* read the current value which also determines + the tag when setting the value */ + rc = bacnet_storage_get(&key, data, sizeof(data)); + if (rc < 0) { + shell_error(sh, "Unable to get %s", key_name); + return -EINVAL; + } + data_len = rc; /* convert the key to a string for the shell to print */ (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); - if (argc > 4) { + /* check for an assigned tag */ + if ((argc > 5) && bacnet_storage_strtoul(argv[4], &unsigned_value)) { + value.tag = unsigned_value; + value_string = argv[5]; + } else if (argc > 4) { value_string = argv[4]; - if (property_list_commandable_member(key.object_type, key.property_id)) { - /* check for case insensitive NULL string */ - if (bacnet_strnicmp(value_string, "NULL", 4) == 0) { - null_value = true; - } - } + } + if (value_string) { + len = bacapp_decode_application_data(data, data_len, &value); /* convert the string value into a tagged union value */ - if (null_value) { - value.tag = BACNET_APPLICATION_TAG_NULL; - status = true; - } else { - value_tag = bacapp_known_property_tag(key.object_type, key.property_id); - /* check for known property types */ - if (value_tag == BACNET_APPLICATION_TAG_ENUMERATED) { + if (isalpha(value_string[0])) { + if (property_list_commandable_member(key.object_type, key.property_id) && + (bacnet_strnicmp(value_string, "NULL", 4) == 0)) { + /* check for case insensitive NULL string */ + value.tag = BACNET_APPLICATION_TAG_NULL; + status = true; + } else if (bacnet_stricmp(value_string, "true") == 0) { + value.type.Boolean = true; + value.tag = BACNET_APPLICATION_TAG_BOOLEAN; + status = true; + } else if (bacnet_stricmp(value_string, "false") == 0) { + value.type.Boolean = false; + value.tag = BACNET_APPLICATION_TAG_BOOLEAN; + status = true; + } else { status = bactext_object_property_strtoul( (BACNET_OBJECT_TYPE)key.object_type, (BACNET_PROPERTY_ID)key.property_id, value_string, @@ -143,9 +162,63 @@ static int cmd_value(const struct shell *sh, size_t argc, char **argv) value.tag = BACNET_APPLICATION_TAG_ENUMERATED; value.type.Enumerated = (uint32_t)enumerated_value; } - } else { - status = bacapp_parse_application_data(value_tag, value_string, - &value); + } + } + if (!status) { + switch (value.tag) { + case BACNET_APPLICATION_TAG_ENUMERATED: + if (bacnet_storage_strtoul(value_string, &unsigned_value)) { + value.type.Enumerated = (uint32_t)unsigned_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (bacnet_storage_strtoul(value_string, &unsigned_value)) { + value.type.Unsigned_Int = (uint32_t)unsigned_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (bacnet_storage_strtol(value_string, &signed_value)) { + value.type.Signed_Int = (int32_t)signed_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_REAL: + if (bacnet_storage_strtof(value_string, &real_value)) { + value.type.Real = (float)real_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + status = bitstring_init_ascii(&value.type.Bit_String, value_string); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + status = octetstring_init_ascii_hex(&value.type.Octet_String, + value_string); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + status = characterstring_init_ansi(&value.type.Character_String, + value_string); + break; + case BACNET_APPLICATION_TAG_DATE: + status = datetime_date_init_ascii(&value.type.Date, value_string); + break; + case BACNET_APPLICATION_TAG_TIME: + status = datetime_time_init_ascii(&value.type.Time, value_string); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + scan_count = + sscanf(value_string, "%4d:%7u", &object_type, &instance); + if (scan_count == 2) { + value.type.Object_Id.type = (uint16_t)object_type; + value.type.Object_Id.instance = instance; + } else { + status = false; + } + break; + default: + break; } } if (status) { @@ -171,14 +244,9 @@ static int cmd_value(const struct shell *sh, size_t argc, char **argv) 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; - } /* convert to printable value */ - len = bacapp_decode_known_array_property(data, rc, &value, key.object_type, - key.property_id, key.array_index); + len = bacapp_decode_known_property(data, rc, &value, key.object_type, + key.property_id); if (len < 0) { shell_error(sh, "Unable to decode value for %s", key_name); return -EINVAL; @@ -287,13 +355,21 @@ static int cmd_delete(const struct shell *sh, size_t argc, char **argv) static int print_storage_data(BACNET_STORAGE_KEY *key, const void *data, size_t data_len, void *context) { + char data_string[80] = {0}; + char hex_string[3] = {0}; + unsigned i; const struct shell *sh = context; + + for (i = 0; i < data_len && i < ((sizeof(data_string) / 2) - 1); i++) { + snprintf(hex_string, sizeof(hex_string), "%02X", ((const uint8_t *)data)[i]); + strcat(data_string, hex_string); + } if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) { - shell_print(sh, "%s/%u/%u/%u", Storage_Base_Name, key->object_type, - key->object_instance, key->property_id); + shell_print(sh, "%s/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, data_string); } else { - shell_print(sh, "%s/%u/%u/%u/%u", Storage_Base_Name, key->object_type, - key->object_instance, key->property_id, key->array_index); + shell_print(sh, "%s/%u/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, key->array_index, data_string); } return 0; @@ -326,9 +402,14 @@ static int cmd_list(const struct shell *sh, size_t argc, char **argv) SHELL_STATIC_SUBCMD_SET_CREATE( sub_bacnet_settings_cmds, SHELL_CMD(list, NULL, "list BACnet storage strings", cmd_list), - SHELL_CMD(value, NULL, "get or set BACnet storage value", cmd_value), - SHELL_CMD(string, NULL, "get or set BACnet storage string", cmd_string), - SHELL_CMD(delete, NULL, "delete BACnet storage string", cmd_delete), SHELL_SUBCMD_SET_END); + SHELL_CMD(value, NULL, " [array index] [value]", + cmd_value), + SHELL_CMD(string, NULL, + " [array index] [string]", + cmd_string), + SHELL_CMD(delete, NULL, " [array index]", + cmd_delete), + SHELL_SUBCMD_SET_END); SHELL_SUBCMD_ADD((bacnet), settings, &sub_bacnet_settings_cmds, "BACnet settings commands", NULL, 1, 0); diff --git a/zephyr/subsys/bacnet_settings/bacnet_storage.c b/zephyr/subsys/bacnet_settings/bacnet_storage.c index 3009d0d..db24854 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_storage.c +++ b/zephyr/subsys/bacnet_settings/bacnet_storage.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #if defined(CONFIG_SETTINGS_FILE) && defined(CONFIG_FILE_SYSTEM_LITTLEFS) #include @@ -62,8 +64,8 @@ int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, size_t dat * @brief Attempt to convert a numeric string into a unsigned long integer * @param search_name - string to convert * @param value - where to put the converted value - * @return true if converted and found_index is set - * @return false if not converted and found_index is not set + * @return true if converted and value is set + * @return false if not converted and value is not set */ bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) { @@ -85,7 +87,72 @@ bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) return false; } if (long_value) { - *long_value = (unsigned)value; + *long_value = value; + } + + return true; +} + +/** + * @brief Attempt to convert a numeric string into a signed long integer + * @param search_name - string to convert + * @param value - where to put the converted value + * @return true if converted and value is set + * @return false if not converted and value is not set + */ +bool bacnet_storage_strtol(const char *search_name, long *long_value) +{ + char *endptr; + long value; + + value = strtol(search_name, &endptr, 0); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if (value == LONG_MAX || value == LONG_MIN) { + /* If the correct value is outside the range of representable values, + {LONG_MAX} or {LONG_MIN} shall be returned */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (long_value) { + *long_value = value; + } + + return true; +} + +/** + * @brief Attempt to convert a numeric string into a floating point value + * @param search_name - string to convert + * @param value - where to put the converted value + * @return true if converted and value is set + * @return false if not converted and value is not set + */ +bool bacnet_storage_strtof(const char *search_name, float *float_value) +{ + char *endptr; + float value; + + value = strtof(search_name, &endptr); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if ((value == INFINITY) || (value == -INFINITY) || (value == NAN)) { + /* the correct value is outside the range of representable values */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (float_value) { + *float_value = value; } return true; @@ -262,9 +329,9 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) } /* object-type */ next_len = settings_name_next(path, &next); - if (next) { + if (path) { if (next_len + 1 > sizeof(object_type_name)) { - LOG_ERR("object-type name too long: %d", next_len); + LOG_ERR("key: object-type name too long: %d", next_len); return -EINVAL; } memcpy(object_type_name, path, next_len); @@ -279,9 +346,9 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) /* object-instance */ path = next; next_len = settings_name_next(path, &next); - if (next) { + if (path) { if (next_len + 1 > sizeof(object_instance_name)) { - LOG_ERR("object-instance name too long: %d", next_len); + LOG_ERR("key: object-instance name too long: %d", next_len); return -EINVAL; } memcpy(object_instance_name, path, next_len); @@ -296,9 +363,9 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) /* property-id */ path = next; next_len = settings_name_next(path, &next); - if (next) { + if (path) { if (next_len + 1 > sizeof(property_id_name)) { - LOG_ERR("property-id name too long: %d", next_len); + LOG_ERR("key: property-id name too long: %d", next_len); return -EINVAL; } memcpy(property_id_name, path, next_len); @@ -311,11 +378,12 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) return -EINVAL; } /* array-index - OPTIONAL */ + key->array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; path = next; next_len = settings_name_next(path, &next); - if (next) { + if (path) { if (next_len + 1 > sizeof(array_index_name)) { - LOG_ERR("array-index name too long: %d", next_len); + LOG_ERR("key: array-index name too long: %d", next_len); return -EINVAL; } memcpy(array_index_name, path, next_len); @@ -324,9 +392,9 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) } else { return -EINVAL; } - } else { - key->array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; } + LOG_INF("key: decoded:%u/%u/%u/%u", key->object_type, key->object_instance, + key->property_id, key->array_index); return 0; } From fb450df508d1b68e549542769b10a4e96a62fc88 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 14:26:35 -0500 Subject: [PATCH 6/9] Use the BACnet Stack clang-format style --- .clang-format | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..57317e2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,27 @@ +--- +BasedOnStyle: WebKit +AlignAfterOpenBracket: AlwaysBreak +BinPackArguments: true +BinPackParameters: false +PenaltyBreakBeforeFirstCallParameter: 1 +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlignEscapedNewlines: Left +AlignArrayOfStructures: None +PointerAlignment: Right +InsertBraces: true +BreakBeforeBraces: Linux +BreakBeforeBinaryOperators: None +KeepEmptyLinesAtTheStartOfBlocks: false +IndentCaseLabels: true +IndentWidth: 4 +UseTab: Never +SortIncludes: false +ColumnLimit: 80 +# Const always to left side. +QualifierAlignment: Left +--- +Language: JavaScript +DisableFormat: true +... From 4c0d9a4bc22b86f910fdedd5f6cfab3a52d0b013 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 14:29:27 -0500 Subject: [PATCH 7/9] restore tab to space in pre-commit --- .pre-commit-config.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4a827bc..bbe9f6d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -38,6 +38,10 @@ repos: - repo: https://github.com/Lucas-C/pre-commit-hooks rev: v1.5.5 hooks: + - id: remove-tabs + name: Remove tabs (4 spaces) + args: ["--whitespaces-count", "4"] + exclude: \.(yaml|yml|html|htm|sln|atsln|layout)$|Makefile|\.(mgw|mak|MAK)$|Dockerfile$ - id: remove-tabs name: Remove tabs (2 spaces) args: ["--whitespaces-count", "2"] From e665332e0b4d1b9047401803282820e8bf8d9301 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 15:00:54 -0500 Subject: [PATCH 8/9] Updated clang-format and pre-commit to use the style of the BACnet Stack library. --- .pre-commit-config.yaml | 4 +- zephyr/.clang-format | 94 -- zephyr/include/bacnet-config.h | 2 +- zephyr/subsys/bacnet_datalink/bip-init.c | 810 +++++++++--------- zephyr/subsys/bacnet_datalink/bip6-init.c | 641 +++++++------- zephyr/subsys/bacnet_osif/bacnet_mstimer.c | 2 +- .../bacnet_shell/bacnet_shell_objects.c | 47 +- .../bacnet_shell/bacnet_shell_packets.c | 12 +- .../subsys/bacnet_shell/bacnet_shell_uptime.c | 12 +- zephyr/subsys/object/bacfile.c | 1 - zephyr/subsys/object/device.c | 45 +- zephyr/subsys/object/mso.c | 1 - zephyr/subsys/object/nc.c | 2 +- zephyr/subsys/object/object.h | 61 +- zephyr/subsys/server/server.c | 281 +++--- zephyr/tests/bacnet/lighting/CMakeLists.txt | 4 +- zephyr/tests/unit/bacnet/bits/CMakeLists.txt | 1 - 17 files changed, 1009 insertions(+), 1011 deletions(-) delete mode 100644 zephyr/.clang-format diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bbe9f6d..6682286 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -41,11 +41,11 @@ repos: - id: remove-tabs name: Remove tabs (4 spaces) args: ["--whitespaces-count", "4"] - exclude: \.(yaml|yml|html|htm|sln|atsln|layout)$|Makefile|\.(mgw|mak|MAK)$|Dockerfile$ + exclude: \.(c|h|yaml|yml|overlay|html|htm|sln|atsln|layout|rst)$|Makefile|\.(mgw|mak|MAK)|Dockerfile$ - id: remove-tabs name: Remove tabs (2 spaces) args: ["--whitespaces-count", "2"] - files: '.*\.(yaml|yml|html|htm)|Dockerfile$' + files: '.*\.(yaml|yml|overlay|html|htm)|Dockerfile$' - repo: https://github.com/pre-commit/mirrors-clang-format rev: v20.1.5 diff --git a/zephyr/.clang-format b/zephyr/.clang-format deleted file mode 100644 index 8dc1f55..0000000 --- a/zephyr/.clang-format +++ /dev/null @@ -1,94 +0,0 @@ -# SPDX-License-Identifier: Apache-2.0 -# -# Note: The list of ForEachMacros can be obtained using: -# -# git grep -h '^#define [^[:space:]]*FOR_EACH[^[:space:]]*(' include/ \ -# | sed "s,^#define \([^[:space:]]*FOR_EACH[^[:space:]]*\)(.*$, - '\1'," \ -# | sort | uniq -# -# References: -# - https://clang.llvm.org/docs/ClangFormatStyleOptions.html - ---- -BasedOnStyle: LLVM -AlignConsecutiveMacros: AcrossComments -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -AttributeMacros: - - __aligned - - __deprecated - - __packed - - __printf_like - - __syscall - - __syscall_always_inline - - __subsystem -BitFieldColonSpacing: After -BreakBeforeBraces: Linux -ColumnLimit: 100 -ConstructorInitializerIndentWidth: 8 -ContinuationIndentWidth: 8 -ForEachMacros: - - 'ARRAY_FOR_EACH' - - 'ARRAY_FOR_EACH_PTR' - - 'FOR_EACH' - - 'FOR_EACH_FIXED_ARG' - - 'FOR_EACH_IDX' - - 'FOR_EACH_IDX_FIXED_ARG' - - 'FOR_EACH_NONEMPTY_TERM' - - 'RB_FOR_EACH' - - 'RB_FOR_EACH_CONTAINER' - - 'SYS_DLIST_FOR_EACH_CONTAINER' - - 'SYS_DLIST_FOR_EACH_CONTAINER_SAFE' - - 'SYS_DLIST_FOR_EACH_NODE' - - 'SYS_DLIST_FOR_EACH_NODE_SAFE' - - 'SYS_SFLIST_FOR_EACH_CONTAINER' - - 'SYS_SFLIST_FOR_EACH_CONTAINER_SAFE' - - 'SYS_SFLIST_FOR_EACH_NODE' - - 'SYS_SFLIST_FOR_EACH_NODE_SAFE' - - 'SYS_SLIST_FOR_EACH_CONTAINER' - - 'SYS_SLIST_FOR_EACH_CONTAINER_SAFE' - - 'SYS_SLIST_FOR_EACH_NODE' - - 'SYS_SLIST_FOR_EACH_NODE_SAFE' - - '_WAIT_Q_FOR_EACH' - - 'Z_FOR_EACH' - - 'Z_FOR_EACH_ENGINE' - - 'Z_FOR_EACH_EXEC' - - 'Z_FOR_EACH_FIXED_ARG' - - 'Z_FOR_EACH_FIXED_ARG_EXEC' - - 'Z_FOR_EACH_IDX' - - 'Z_FOR_EACH_IDX_EXEC' - - 'Z_FOR_EACH_IDX_FIXED_ARG' - - 'Z_FOR_EACH_IDX_FIXED_ARG_EXEC' - - 'Z_GENLIST_FOR_EACH_CONTAINER' - - 'Z_GENLIST_FOR_EACH_CONTAINER_SAFE' - - 'Z_GENLIST_FOR_EACH_NODE' - - 'Z_GENLIST_FOR_EACH_NODE_SAFE' - - 'STRUCT_SECTION_FOREACH' - - 'TYPE_SECTION_FOREACH' - - 'K_SPINLOCK' -IfMacros: - - 'CHECKIF' -# Disabled for now, see bug https://github.com/zephyrproject-rtos/zephyr/issues/48520 -#IncludeBlocks: Regroup -IncludeCategories: - - Regex: '^".*\.h"$' - Priority: 0 - - Regex: '^<(assert|complex|ctype|errno|fenv|float|inttypes|limits|locale|math|setjmp|signal|stdarg|stdbool|stddef|stdint|stdio|stdlib|string|tgmath|time|wchar|wctype)\.h>$' - Priority: 1 - - Regex: '^\$' - Priority: 2 - - Regex: '.*' - Priority: 3 -IndentCaseLabels: false -IndentWidth: 8 -InsertBraces: true -SpaceBeforeParens: ControlStatementsExceptControlMacros -SortIncludes: Never -UseTab: ForContinuationAndIndentation -WhitespaceSensitiveMacros: - - STRINGIFY - - Z_STRINGIFY diff --git a/zephyr/include/bacnet-config.h b/zephyr/include/bacnet-config.h index 8f19eab..d33c71e 100644 --- a/zephyr/include/bacnet-config.h +++ b/zephyr/include/bacnet-config.h @@ -8,7 +8,7 @@ #ifndef BACNET_PORTS_ZEPHYR_BACNET_CONFIG_H #define BACNET_PORTS_ZEPHYR_BACNET_CONFIG_H -#if ! defined BACNET_CONFIG_H || ! BACNET_CONFIG_H +#if !defined BACNET_CONFIG_H || !BACNET_CONFIG_H #error bacnet-config.h included outside of BACNET_CONFIG_H control #endif diff --git a/zephyr/subsys/bacnet_datalink/bip-init.c b/zephyr/subsys/bacnet_datalink/bip-init.c index b4bebe8..bfdf84a 100644 --- a/zephyr/subsys/bacnet_datalink/bip-init.c +++ b/zephyr/subsys/bacnet_datalink/bip-init.c @@ -46,7 +46,7 @@ static struct in_addr BIP_Broadcast_Addr; /* Used by inet_ntoa */ #if CONFIG_BACNETSTACK_LOG_LEVEL -static char ipv4_addr_str[16] = {0}; +static char ipv4_addr_str[16] = { 0 }; #else static char ipv4_addr_str[] = ""; #endif @@ -58,12 +58,13 @@ static char ipv4_addr_str[] = ""; */ char *inet_ntoa(struct in_addr *a) { - if (IS_ENABLED(CONFIG_BACNETSTACK_LOG_LEVEL)) { - snprintf(ipv4_addr_str, sizeof(ipv4_addr_str), "%d.%d.%d.%d", a->s4_addr[0], - a->s4_addr[1], a->s4_addr[2], a->s4_addr[3]); - } + if (IS_ENABLED(CONFIG_BACNETSTACK_LOG_LEVEL)) { + snprintf( + ipv4_addr_str, sizeof(ipv4_addr_str), "%d.%d.%d.%d", a->s4_addr[0], + a->s4_addr[1], a->s4_addr[2], a->s4_addr[3]); + } - return &ipv4_addr_str[0]; + return &ipv4_addr_str[0]; } /** @@ -71,11 +72,15 @@ char *inet_ntoa(struct in_addr *a) * @param str - debug info string * @param addr - IPv4 address */ -static void debug_print_ipv4(const char *str, const struct in_addr *addr, const unsigned int port, - const unsigned int count) +static void debug_print_ipv4( + const char *str, + const struct in_addr *addr, + const unsigned int port, + const unsigned int count) { - LOG_DBG("%s %s:%hu (%u bytes)", str, inet_ntoa((struct in_addr *)&addr), ntohs(port), - count); + LOG_DBG( + "%s %s:%hu (%u bytes)", str, inet_ntoa((struct in_addr *)&addr), + ntohs(port), count); } /** @@ -84,7 +89,7 @@ static void debug_print_ipv4(const char *str, const struct in_addr *addr, const */ void bip_set_port(uint16_t port) { - BIP_Port = htons(port); + BIP_Port = htons(port); } /** @@ -93,7 +98,7 @@ void bip_set_port(uint16_t port) */ uint16_t bip_get_port(void) { - return ntohs(BIP_Port); + return ntohs(BIP_Port); } /** @@ -102,21 +107,21 @@ uint16_t bip_get_port(void) */ void bip_get_my_address(BACNET_ADDRESS *addr) { - unsigned int i = 0; - - if (addr) { - addr->mac_len = BIP_ADDRESS_MAX; /* 6 */ - memcpy(&addr->mac[0], &BIP_Address.s_addr, IP_ADDRESS_MAX); /* 4 */ - memcpy(&addr->mac[IP_ADDRESS_MAX], &BIP_Port, sizeof(BIP_Port)); - /* local only, no routing */ - addr->net = 0; - /* no SLEN */ - addr->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - /* no SADR */ - addr->adr[i] = 0; - } - } + unsigned int i = 0; + + if (addr) { + addr->mac_len = BIP_ADDRESS_MAX; /* 6 */ + memcpy(&addr->mac[0], &BIP_Address.s_addr, IP_ADDRESS_MAX); /* 4 */ + memcpy(&addr->mac[IP_ADDRESS_MAX], &BIP_Port, sizeof(BIP_Port)); + /* local only, no routing */ + addr->net = 0; + /* no SLEN */ + addr->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + /* no SADR */ + addr->adr[i] = 0; + } + } } /** @@ -127,19 +132,19 @@ void bip_get_my_address(BACNET_ADDRESS *addr) void bip_get_broadcast_address(BACNET_ADDRESS *dest) { - int i = 0; - - if (dest) { - dest->mac_len = BIP_ADDRESS_MAX; - memcpy(&dest->mac[0], &BIP_Broadcast_Addr.s_addr, IP_ADDRESS_MAX); - memcpy(&dest->mac[IP_ADDRESS_MAX], &BIP_Port, sizeof(BIP_Port)); - dest->net = BACNET_BROADCAST_NETWORK; - dest->len = 0; - for (i = 0; i < MAX_MAC_LEN; i++) { - dest->adr[i] = 0; - } - } - return; + int i = 0; + + if (dest) { + dest->mac_len = BIP_ADDRESS_MAX; + memcpy(&dest->mac[0], &BIP_Broadcast_Addr.s_addr, IP_ADDRESS_MAX); + memcpy(&dest->mac[IP_ADDRESS_MAX], &BIP_Port, sizeof(BIP_Port)); + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + return; } /** @@ -149,12 +154,12 @@ void bip_get_broadcast_address(BACNET_ADDRESS *dest) */ bool bip_set_addr(const BACNET_IP_ADDRESS *addr) { - if (addr) { - memcpy(&BIP_Address.s_addr, &addr->address[0], IP_ADDRESS_MAX); - BIP_Port = htons(addr->port); - return true; - } - return false; + if (addr) { + memcpy(&BIP_Address.s_addr, &addr->address[0], IP_ADDRESS_MAX); + BIP_Port = htons(addr->port); + return true; + } + return false; } /** @@ -164,12 +169,12 @@ bool bip_set_addr(const BACNET_IP_ADDRESS *addr) */ bool bip_get_addr(BACNET_IP_ADDRESS *addr) { - if (addr) { - memcpy(&addr->address[0], &BIP_Address.s_addr, IP_ADDRESS_MAX); - addr->port = ntohs(BIP_Port); - return true; - } - return false; + if (addr) { + memcpy(&addr->address[0], &BIP_Address.s_addr, IP_ADDRESS_MAX); + addr->port = ntohs(BIP_Port); + return true; + } + return false; } /** @@ -179,11 +184,11 @@ bool bip_get_addr(BACNET_IP_ADDRESS *addr) */ bool bip_set_broadcast_addr(const BACNET_IP_ADDRESS *addr) { - if (addr) { - memcpy(&BIP_Broadcast_Addr.s_addr, &addr->address[0], IP_ADDRESS_MAX); - return true; - } - return false; + if (addr) { + memcpy(&BIP_Broadcast_Addr.s_addr, &addr->address[0], IP_ADDRESS_MAX); + return true; + } + return false; } /** @@ -193,12 +198,12 @@ bool bip_set_broadcast_addr(const BACNET_IP_ADDRESS *addr) */ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) { - if (addr) { - memcpy(&addr->address[0], &BIP_Broadcast_Addr.s_addr, IP_ADDRESS_MAX); - addr->port = ntohs(BIP_Port); - return true; - } - return false; + if (addr) { + memcpy(&addr->address[0], &BIP_Broadcast_Addr.s_addr, IP_ADDRESS_MAX); + addr->port = ntohs(BIP_Port); + return true; + } + return false; } /** @@ -207,8 +212,8 @@ bool bip_get_broadcast_addr(BACNET_IP_ADDRESS *addr) */ bool bip_set_subnet_prefix(uint8_t prefix) { - /* not something we do within this driver */ - return false; + /* not something we do within this driver */ + return false; } /** @@ -217,22 +222,22 @@ bool bip_set_subnet_prefix(uint8_t prefix) */ uint8_t bip_get_subnet_prefix(void) { - uint32_t address = 0; - uint32_t broadcast = 0; - uint32_t mask = 0xFFFFFFFE; - uint8_t prefix = 0; - - address = BIP_Address.s_addr; - broadcast = BIP_Broadcast_Addr.s_addr; - /* calculate the subnet prefix from the broadcast address */ - for (prefix = 1; prefix <= 32; prefix++) { - if ((address | mask) == broadcast) { - break; - } - mask = mask << 1; - } - - return prefix; + uint32_t address = 0; + uint32_t broadcast = 0; + uint32_t mask = 0xFFFFFFFE; + uint8_t prefix = 0; + + address = BIP_Address.s_addr; + broadcast = BIP_Broadcast_Addr.s_addr; + /* calculate the subnet prefix from the broadcast address */ + for (prefix = 1; prefix <= 32; prefix++) { + if ((address | mask) == broadcast) { + break; + } + mask = mask << 1; + } + + return prefix; } /** @@ -246,25 +251,28 @@ uint8_t bip_get_subnet_prefix(void) * @return Upon successful completion, returns the number of bytes sent. * Otherwise, -1 shall be returned and errno set to indicate the error. */ -int bip_send_mpdu(const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len) +int bip_send_mpdu( + const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mtu_len) { - struct sockaddr_in bip_dest = {0}; - - /* assumes that the driver has already been initialized */ - if (BIP_Socket < 0) { - LOG_ERR("%s:%d - Socket not initialized!", THIS_FILE, __LINE__); - return BIP_Socket; - } - - /* load destination IP address */ - bip_dest.sin_family = AF_INET; - memcpy(&bip_dest.sin_addr.s_addr, &dest->address[0], IP_ADDRESS_MAX); - bip_dest.sin_port = htons(dest->port); - - /* Send the packet */ - debug_print_ipv4("Sending MPDU->", &bip_dest.sin_addr, bip_dest.sin_port, mtu_len); - return zsock_sendto(BIP_Socket, (char *)mtu, mtu_len, 0, (struct sockaddr *)&bip_dest, - sizeof(struct sockaddr)); + struct sockaddr_in bip_dest = { 0 }; + + /* assumes that the driver has already been initialized */ + if (BIP_Socket < 0) { + LOG_ERR("%s:%d - Socket not initialized!", THIS_FILE, __LINE__); + return BIP_Socket; + } + + /* load destination IP address */ + bip_dest.sin_family = AF_INET; + memcpy(&bip_dest.sin_addr.s_addr, &dest->address[0], IP_ADDRESS_MAX); + bip_dest.sin_port = htons(dest->port); + + /* Send the packet */ + debug_print_ipv4( + "Sending MPDU->", &bip_dest.sin_addr, bip_dest.sin_port, mtu_len); + return zsock_sendto( + BIP_Socket, (char *)mtu, mtu_len, 0, (struct sockaddr *)&bip_dest, + sizeof(struct sockaddr)); } /** @@ -277,94 +285,102 @@ int bip_send_mpdu(const BACNET_IP_ADDRESS *dest, const uint8_t *mtu, uint16_t mt * * @return Number of bytes received, or 0 if none or timeout. */ -uint16_t bip_receive(BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout) +uint16_t bip_receive( + BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout) { - uint16_t npdu_len = 0; /* return value */ - zsock_fd_set read_fds; - int max = 0; - struct zsock_timeval select_timeout; - struct sockaddr_in sin = {0}; - BACNET_IP_ADDRESS addr = {0}; - socklen_t sin_len = sizeof(sin); - int received_bytes = 0; - int offset = 0; - uint16_t i = 0; - int socket; - - /* Make sure the socket is open */ - if (BIP_Socket < 0) { - return 0; - } - - /* we could just use a non-blocking socket, but that consumes all - the CPU time. We can use a timeout; it is only supported as - a select. */ - if (timeout >= 1000) { - select_timeout.tv_sec = timeout / 1000; - select_timeout.tv_usec = 1000 * (timeout - select_timeout.tv_sec * 1000); - } else { - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 1000 * timeout; - } - ZSOCK_FD_ZERO(&read_fds); - ZSOCK_FD_SET(BIP_Socket, &read_fds); - ZSOCK_FD_SET(BIP_Broadcast_Socket, &read_fds); - - max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; - - /* see if there is a packet for us */ - if (zsock_select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - socket = FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : BIP_Broadcast_Socket; - received_bytes = zsock_recvfrom(socket, (char *)&npdu[0], max_npdu, 0, - (struct sockaddr *)&sin, &sin_len); - } else { - return 0; - } - - /* See if there is a problem */ - if (received_bytes < 0) { - LOG_WRN("%s:%d - RX zsock_recvfrom() error: %d", THIS_FILE, __LINE__, - received_bytes); - return 0; - } - /* no problem, just no bytes */ - if (received_bytes == 0) { - return 0; - } - /* the signature of a BACnet/IP packet */ - if (npdu[0] != BVLL_TYPE_BACNET_IP) { - LOG_WRN("%s:%d - RX bad packet", THIS_FILE, __LINE__); - return 0; - } - - /* Data link layer addressing between B/IPv4 nodes consists of a 32-bit - IPv4 address followed by a two-octet UDP port number (both of which - shall be transmitted with the most significant octet first). This - address shall be referred to as a B/IPv4 address. - */ - - memcpy(&addr.address[0], &sin.sin_addr.s_addr, IP_ADDRESS_MAX); - addr.port = ntohs(sin.sin_port); - - debug_print_ipv4("Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); - /* pass the packet into the BBMD handler */ - offset = socket == BIP_Socket ? bvlc_handler(&addr, src, npdu, received_bytes) - : bvlc_broadcast_handler(&addr, src, npdu, received_bytes); - if (offset > 0) { - npdu_len = received_bytes - offset; - debug_print_ipv4("Received NPDU->", &sin.sin_addr, sin.sin_port, npdu_len); - if (npdu_len <= max_npdu) { - /* shift the buffer to return a valid NPDU */ - for (i = 0; i < npdu_len; i++) { - npdu[i] = npdu[offset + i]; - } - } else { - LOG_WRN("%s:%d - NPDU dropped!", THIS_FILE, __LINE__); - npdu_len = 0; - } - } - - return npdu_len; + uint16_t npdu_len = 0; /* return value */ + zsock_fd_set read_fds; + int max = 0; + struct zsock_timeval select_timeout; + struct sockaddr_in sin = { 0 }; + BACNET_IP_ADDRESS addr = { 0 }; + socklen_t sin_len = sizeof(sin); + int received_bytes = 0; + int offset = 0; + uint16_t i = 0; + int socket; + + /* Make sure the socket is open */ + if (BIP_Socket < 0) { + return 0; + } + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + ZSOCK_FD_ZERO(&read_fds); + ZSOCK_FD_SET(BIP_Socket, &read_fds); + ZSOCK_FD_SET(BIP_Broadcast_Socket, &read_fds); + + max = BIP_Socket > BIP_Broadcast_Socket ? BIP_Socket : BIP_Broadcast_Socket; + + /* see if there is a packet for us */ + if (zsock_select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { + socket = + FD_ISSET(BIP_Socket, &read_fds) ? BIP_Socket : BIP_Broadcast_Socket; + received_bytes = zsock_recvfrom( + socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, + &sin_len); + } else { + return 0; + } + + /* See if there is a problem */ + if (received_bytes < 0) { + LOG_WRN( + "%s:%d - RX zsock_recvfrom() error: %d", THIS_FILE, __LINE__, + received_bytes); + return 0; + } + /* no problem, just no bytes */ + if (received_bytes == 0) { + return 0; + } + /* the signature of a BACnet/IP packet */ + if (npdu[0] != BVLL_TYPE_BACNET_IP) { + LOG_WRN("%s:%d - RX bad packet", THIS_FILE, __LINE__); + return 0; + } + + /* Data link layer addressing between B/IPv4 nodes consists of a 32-bit + IPv4 address followed by a two-octet UDP port number (both of which + shall be transmitted with the most significant octet first). This + address shall be referred to as a B/IPv4 address. + */ + + memcpy(&addr.address[0], &sin.sin_addr.s_addr, IP_ADDRESS_MAX); + addr.port = ntohs(sin.sin_port); + + debug_print_ipv4( + "Received MPDU->", &sin.sin_addr, sin.sin_port, received_bytes); + /* pass the packet into the BBMD handler */ + offset = socket == BIP_Socket + ? bvlc_handler(&addr, src, npdu, received_bytes) + : bvlc_broadcast_handler(&addr, src, npdu, received_bytes); + if (offset > 0) { + npdu_len = received_bytes - offset; + debug_print_ipv4( + "Received NPDU->", &sin.sin_addr, sin.sin_port, npdu_len); + if (npdu_len <= max_npdu) { + /* shift the buffer to return a valid NPDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[offset + i]; + } + } else { + LOG_WRN("%s:%d - NPDU dropped!", THIS_FILE, __LINE__); + npdu_len = 0; + } + } + + return npdu_len; } /** @@ -379,40 +395,46 @@ uint16_t bip_receive(BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsi * @return Upon successful completion, returns the number of bytes sent. * Otherwise, -1 shall be returned and errno set to indicate the error. */ -int bip_send_pdu(BACNET_ADDRESS *dest, BACNET_NPDU_DATA *npdu_data, uint8_t *pdu, unsigned pdu_len) +int bip_send_pdu( + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, + unsigned pdu_len) { - dest->net = BACNET_BROADCAST_NETWORK; - return bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); + dest->net = BACNET_BROADCAST_NETWORK; + return bvlc_send_pdu(dest, npdu_data, pdu, pdu_len); } struct wait_data { - struct k_sem sem; - struct net_mgmt_event_callback cb; + struct k_sem sem; + struct net_mgmt_event_callback cb; }; -static void event_cb_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, - struct net_if *iface) +static void event_cb_handler( + struct net_mgmt_event_callback *cb, + uint32_t mgmt_event, + struct net_if *iface) { - struct wait_data *wait = CONTAINER_OF(cb, struct wait_data, cb); + struct wait_data *wait = CONTAINER_OF(cb, struct wait_data, cb); - if (mgmt_event == cb->event_mask) { - k_sem_give(&wait->sem); - } + if (mgmt_event == cb->event_mask) { + k_sem_give(&wait->sem); + } } static void wait_for_net_event(struct net_if *iface, uint32_t event) { - struct wait_data wait; + struct wait_data wait; - wait.cb.handler = event_cb_handler; - wait.cb.event_mask = event; + wait.cb.handler = event_cb_handler; + wait.cb.event_mask = event; - k_sem_init(&wait.sem, 0, 1); - net_mgmt_add_event_callback(&wait.cb); + k_sem_init(&wait.sem, 0, 1); + net_mgmt_add_event_callback(&wait.cb); - k_sem_take(&wait.sem, K_FOREVER); + k_sem_take(&wait.sem, K_FOREVER); - net_mgmt_del_event_callback(&wait.cb); + net_mgmt_del_event_callback(&wait.cb); } /** Gets the local IP address and local broadcast address from the system, @@ -423,155 +445,178 @@ static void wait_for_net_event(struct net_if *iface, uint32_t event) */ void bip_set_interface(const char *ifname) { - char hr_addr[NET_IPV4_ADDR_LEN]; - struct net_if *iface = 0; - int index = -1; - uint8_t x = 0; - BACNET_IP_ADDRESS unicast = {0}; - BACNET_IP_ADDRESS broadcast = {0}; - - /* Network byte order */ - unicast.port = ntohs(BIP_Port); - broadcast.port = ntohs(BIP_Port); - LOG_INF("bip_set_interface()"); - LOG_INF("UDP port: %d", unicast.port); - if (ifname) { - index = atoi(ifname); - /* if index is zero, discern between "0" and a parse error */ - if (!index && strcmp(ifname, "0")) { - LOG_ERR("%s:%d - Argument must parse to an integer", THIS_FILE, __LINE__); - } else { - iface = net_if_get_by_index(index); - if (iface) { - LOG_INF("Using iface %d", index); - } else { - LOG_ERR("%s:%d - No iface at index %d", THIS_FILE, __LINE__, index); - } - } - } - if (index == -1) { - LOG_INF("%s:%d - No valid interface specified - using default ", THIS_FILE, - __LINE__); - iface = net_if_get_default(); - } - if (iface) { - LOG_INF("Interface set."); - if (!net_if_is_up(iface)) { - LOG_INF("Bringing up network interface"); - int ret = net_if_up(iface); - if ((ret < 0) && (ret != -EALREADY)) { - LOG_ERR("Failed to bring up network interface: %d", ret); - return; - } - } - if (BIP_Address.s_addr != 0) { - net_if_ipv4_addr_add(iface, &BIP_Address, NET_ADDR_MANUAL, 0); - LOG_INF("static IPv4 address: %s", - net_addr_ntop(AF_INET, &BIP_Address, hr_addr, NET_IPV4_ADDR_LEN)); - - net_if_ipv4_set_netmask_by_addr(iface, &BIP_Address, &BIP_Broadcast_Addr); - LOG_INF("static IPv4 netmask: %s", - net_addr_ntop(AF_INET, &BIP_Broadcast_Addr, hr_addr, - NET_IPV4_ADDR_LEN)); - return; - } + char hr_addr[NET_IPV4_ADDR_LEN]; + struct net_if *iface = 0; + int index = -1; + uint8_t x = 0; + BACNET_IP_ADDRESS unicast = { 0 }; + BACNET_IP_ADDRESS broadcast = { 0 }; + + /* Network byte order */ + unicast.port = ntohs(BIP_Port); + broadcast.port = ntohs(BIP_Port); + LOG_INF("bip_set_interface()"); + LOG_INF("UDP port: %d", unicast.port); + if (ifname) { + index = atoi(ifname); + /* if index is zero, discern between "0" and a parse error */ + if (!index && strcmp(ifname, "0")) { + LOG_ERR( + "%s:%d - Argument must parse to an integer", THIS_FILE, + __LINE__); + } else { + iface = net_if_get_by_index(index); + if (iface) { + LOG_INF("Using iface %d", index); + } else { + LOG_ERR( + "%s:%d - No iface at index %d", THIS_FILE, __LINE__, index); + } + } + } + if (index == -1) { + LOG_INF( + "%s:%d - No valid interface specified - using default ", THIS_FILE, + __LINE__); + iface = net_if_get_default(); + } + if (iface) { + LOG_INF("Interface set."); + if (!net_if_is_up(iface)) { + LOG_INF("Bringing up network interface"); + int ret = net_if_up(iface); + if ((ret < 0) && (ret != -EALREADY)) { + LOG_ERR("Failed to bring up network interface: %d", ret); + return; + } + } + if (BIP_Address.s_addr != 0) { + net_if_ipv4_addr_add(iface, &BIP_Address, NET_ADDR_MANUAL, 0); + LOG_INF( + "static IPv4 address: %s", + net_addr_ntop( + AF_INET, &BIP_Address, hr_addr, NET_IPV4_ADDR_LEN)); + + net_if_ipv4_set_netmask_by_addr( + iface, &BIP_Address, &BIP_Broadcast_Addr); + LOG_INF( + "static IPv4 netmask: %s", + net_addr_ntop( + AF_INET, &BIP_Broadcast_Addr, hr_addr, NET_IPV4_ADDR_LEN)); + return; + } #if defined(CONFIG_NET_DHCPV4) - LOG_INF("Starting DHCP to obtain IP address"); - net_dhcpv4_start(iface); + LOG_INF("Starting DHCP to obtain IP address"); + net_dhcpv4_start(iface); #endif - LOG_INF("Waiting to obtain IP address"); - wait_for_net_event(iface, NET_EVENT_IPV4_ADDR_ADD); + LOG_INF("Waiting to obtain IP address"); + wait_for_net_event(iface, NET_EVENT_IPV4_ADDR_ADD); #if defined(CONFIG_BACDL_BIP_ADDRESS_INDEX) - LOG_INF("Config unicast address %d/%d", CONFIG_BACDL_BIP_ADDRESS_INDEX, - NET_IF_MAX_IPV4_ADDR - 1); - index = CONFIG_BACDL_BIP_ADDRESS_INDEX; + LOG_INF( + "Config unicast address %d/%d", CONFIG_BACDL_BIP_ADDRESS_INDEX, + NET_IF_MAX_IPV4_ADDR - 1); + index = CONFIG_BACDL_BIP_ADDRESS_INDEX; #else - int i; - char hr_addr[NET_IPV4_ADDR_LEN]; - index = 0; - for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { - struct net_if_addr *if_addr = &iface->config.ip.ipv4->unicast[i]; - - if (!if_addr->is_used) { - continue; - } + int i; + char hr_addr[NET_IPV4_ADDR_LEN]; + index = 0; + for (i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) { + struct net_if_addr *if_addr = &iface->config.ip.ipv4->unicast[i]; + + if (!if_addr->is_used) { + continue; + } #if defined(CONFIG_NET_DHCPV4) - if (if_addr->addr_type != NET_ADDR_DHCP) { - continue; - } + if (if_addr->addr_type != NET_ADDR_DHCP) { + continue; + } #endif - index = i; - LOG_INF("IPv4 address: %s", - net_addr_ntop(AF_INET, &if_addr->address.in_addr, hr_addr, - NET_IPV4_ADDR_LEN)); - LOG_INF("Subnet: %s", - net_addr_ntop(AF_INET, &iface->config.ip.ipv4->netmask, hr_addr, - NET_IPV4_ADDR_LEN)); - LOG_INF("Router: %s", net_addr_ntop(AF_INET, &iface->config.ip.ipv4->gw, - hr_addr, NET_IPV4_ADDR_LEN)); - break; - } + index = i; + LOG_INF( + "IPv4 address: %s", + net_addr_ntop( + AF_INET, &if_addr->address.in_addr, hr_addr, + NET_IPV4_ADDR_LEN)); + LOG_INF( + "Subnet: %s", + net_addr_ntop( + AF_INET, &iface->config.ip.ipv4->netmask, hr_addr, + NET_IPV4_ADDR_LEN)); + LOG_INF( + "Router: %s", + net_addr_ntop( + AF_INET, &iface->config.ip.ipv4->gw, hr_addr, + NET_IPV4_ADDR_LEN)); + break; + } #endif - if (index >= NET_IF_MAX_IPV4_ADDR) { - LOG_ERR("%s:%d - IPv4 address index of %d is out of range (0-%d)", - THIS_FILE, __LINE__, index, NET_IF_MAX_IPV4_ADDR - 1); - return; - } - LOG_INF("Using IPv4 address at index %d", index); - /* Build the broadcast address from the unicast and netmask */ - struct net_if_addr_ipv4 *if_addr = &iface->config.ip.ipv4->unicast[index]; - for (x = 0; x < IP_ADDRESS_MAX; x++) { - unicast.address[x] = if_addr->ipv4.address.in_addr.s4_addr[x]; - broadcast.address[x] = if_addr->ipv4.address.in_addr.s4_addr[x] | - ~if_addr->netmask.s4_addr[x]; - } - bip_set_addr(&unicast); - bip_set_broadcast_addr(&broadcast); - LOG_INF("BACnet/IP Unicast: %u.%u.%u.%u:%d", unicast.address[0], unicast.address[1], - unicast.address[2], unicast.address[3], unicast.port); - LOG_INF("BACnet/IP Broadcast: %u.%u.%u.%u", broadcast.address[0], - broadcast.address[1], broadcast.address[2], broadcast.address[3]); - } else { - LOG_ERR("%s:%d - Failed to set iface", THIS_FILE, __LINE__); - } + if (index >= NET_IF_MAX_IPV4_ADDR) { + LOG_ERR( + "%s:%d - IPv4 address index of %d is out of range (0-%d)", + THIS_FILE, __LINE__, index, NET_IF_MAX_IPV4_ADDR - 1); + return; + } + LOG_INF("Using IPv4 address at index %d", index); + /* Build the broadcast address from the unicast and netmask */ + struct net_if_addr_ipv4 *if_addr = + &iface->config.ip.ipv4->unicast[index]; + for (x = 0; x < IP_ADDRESS_MAX; x++) { + unicast.address[x] = if_addr->ipv4.address.in_addr.s4_addr[x]; + broadcast.address[x] = if_addr->ipv4.address.in_addr.s4_addr[x] | + ~if_addr->netmask.s4_addr[x]; + } + bip_set_addr(&unicast); + bip_set_broadcast_addr(&broadcast); + LOG_INF( + "BACnet/IP Unicast: %u.%u.%u.%u:%d", unicast.address[0], + unicast.address[1], unicast.address[2], unicast.address[3], + unicast.port); + LOG_INF( + "BACnet/IP Broadcast: %u.%u.%u.%u", broadcast.address[0], + broadcast.address[1], broadcast.address[2], broadcast.address[3]); + } else { + LOG_ERR("%s:%d - Failed to set iface", THIS_FILE, __LINE__); + } } static int createSocket(struct sockaddr_in *sin) { - int sock_fd = -1; - const int sockopt = 1; - int status = -1; - - /* assumes that the driver has already been initialized */ - sock_fd = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); - if (sock_fd < 0) { - LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); - return sock_fd; - } else { - LOG_DBG("Socket created"); - } - - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - status = zsock_setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); - if (status < 0) { - zsock_close(sock_fd); - return status; - } - - /* bind the socket to the local port number and IP address */ - status = zsock_bind(sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); - if (status < 0) { - zsock_close(sock_fd); - LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); - return status; - } else { - LOG_DBG("Socket bound"); - } - - return sock_fd; + int sock_fd = -1; + const int sockopt = 1; + int status = -1; + + /* assumes that the driver has already been initialized */ + sock_fd = zsock_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd < 0) { + LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); + return sock_fd; + } else { + LOG_DBG("Socket created"); + } + + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + status = zsock_setsockopt( + sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + if (status < 0) { + zsock_close(sock_fd); + return status; + } + + /* bind the socket to the local port number and IP address */ + status = zsock_bind( + sock_fd, (const struct sockaddr *)sin, sizeof(struct sockaddr)); + if (status < 0) { + zsock_close(sock_fd); + LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); + return status; + } else { + LOG_DBG("Socket bound"); + } + + return sock_fd; } /** Initialize the BACnet/IP services at the given interface. @@ -593,39 +638,40 @@ static int createSocket(struct sockaddr_in *sin) */ bool bip_init(char *ifname) { - int sock_fd; - struct sockaddr_in sin = {0}; - - bip_set_interface(ifname); - - if (BIP_Address.s_addr == 0) { - LOG_ERR("%s:%d - Failed to get an IP address on interface: %s\n", THIS_FILE, - __LINE__, ifname ? ifname : "[default]"); - return false; - } - - /* bind the socket to the local port number and IP address */ - sin.sin_family = AF_INET; - sin.sin_port = BIP_Port; - - sin.sin_addr.s_addr = BIP_Address.s_addr; - sock_fd = createSocket(&sin); - BIP_Socket = sock_fd; - if (sock_fd < 0) { - return false; - } - - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sock_fd = createSocket(&sin); - BIP_Broadcast_Socket = sock_fd; - if (sock_fd < 0) { - return false; - } - - bvlc_init(); - - LOG_DBG("bip_init() success"); - return true; + int sock_fd; + struct sockaddr_in sin = { 0 }; + + bip_set_interface(ifname); + + if (BIP_Address.s_addr == 0) { + LOG_ERR( + "%s:%d - Failed to get an IP address on interface: %s\n", THIS_FILE, + __LINE__, ifname ? ifname : "[default]"); + return false; + } + + /* bind the socket to the local port number and IP address */ + sin.sin_family = AF_INET; + sin.sin_port = BIP_Port; + + sin.sin_addr.s_addr = BIP_Address.s_addr; + sock_fd = createSocket(&sin); + BIP_Socket = sock_fd; + if (sock_fd < 0) { + return false; + } + + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sock_fd = createSocket(&sin); + BIP_Broadcast_Socket = sock_fd; + if (sock_fd < 0) { + return false; + } + + bvlc_init(); + + LOG_DBG("bip_init() success"); + return true; } /** @@ -634,7 +680,7 @@ bool bip_init(char *ifname) */ bool bip_valid(void) { - return (BIP_Socket != -1); + return (BIP_Socket != -1); } /** Cleanup and close out the BACnet/IP services by closing the socket. @@ -642,15 +688,15 @@ bool bip_valid(void) */ void bip_cleanup(void) { - LOG_DBG("bip_cleanup()"); + LOG_DBG("bip_cleanup()"); - memset(&BIP_Address, 0, sizeof(BIP_Address)); - memset(&BIP_Broadcast_Addr, 0, sizeof(BIP_Broadcast_Addr)); + memset(&BIP_Address, 0, sizeof(BIP_Address)); + memset(&BIP_Broadcast_Addr, 0, sizeof(BIP_Broadcast_Addr)); - if (BIP_Socket != -1) { - zsock_close(BIP_Socket); - } - BIP_Socket = -1; + if (BIP_Socket != -1) { + zsock_close(BIP_Socket); + } + BIP_Socket = -1; - return; + return; } diff --git a/zephyr/subsys/bacnet_datalink/bip6-init.c b/zephyr/subsys/bacnet_datalink/bip6-init.c index 2af9fac..cdae5fe 100644 --- a/zephyr/subsys/bacnet_datalink/bip6-init.c +++ b/zephyr/subsys/bacnet_datalink/bip6-init.c @@ -39,7 +39,7 @@ static BACNET_IP6_ADDRESS BIP6_Broadcast_Addr; /* Used by inet6_ntoa */ #if CONFIG_BACNETSTACK_LOG_LEVEL -static char ipv6_addr_str[42] = {0}; +static char ipv6_addr_str[42] = { 0 }; #else static char ipv6_addr_str[] = ""; #endif @@ -52,107 +52,120 @@ static char ipv6_addr_str[] = ""; static char *inet6_ntoa(struct in6_addr *a) { #if CONFIG_BACNETSTACK_LOG_LEVEL - uint8_t x = 0; - uint8_t d = 0; - uint8_t non_zero_count = 0; - - /* Avoid overwhelming the logging system */ - while (log_buffered_cnt()) { - k_sleep(K_MSEC(1)); - } - - for (x = 0; x < IP6_ADDRESS_MAX; x += 2) { - if (a->s6_addr[x] | a->s6_addr[x + 1]) { - non_zero_count++; - d += snprintf(&ipv6_addr_str[d], sizeof(ipv6_addr_str), "%02X%02X", - a->s6_addr[x], a->s6_addr[x + 1]); - } - - if (x < 14) { - d += snprintf(&ipv6_addr_str[d], sizeof(ipv6_addr_str), ":"); - } - } - - if (!non_zero_count) { - snprintf(&ipv6_addr_str[0], sizeof(ipv6_addr_str), "undefined"); - } + uint8_t x = 0; + uint8_t d = 0; + uint8_t non_zero_count = 0; + + /* Avoid overwhelming the logging system */ + while (log_buffered_cnt()) { + k_sleep(K_MSEC(1)); + } + + for (x = 0; x < IP6_ADDRESS_MAX; x += 2) { + if (a->s6_addr[x] | a->s6_addr[x + 1]) { + non_zero_count++; + d += snprintf( + &ipv6_addr_str[d], sizeof(ipv6_addr_str), "%02X%02X", + a->s6_addr[x], a->s6_addr[x + 1]); + } + + if (x < 14) { + d += snprintf(&ipv6_addr_str[d], sizeof(ipv6_addr_str), ":"); + } + } + + if (!non_zero_count) { + snprintf(&ipv6_addr_str[0], sizeof(ipv6_addr_str), "undefined"); + } #endif - return &ipv6_addr_str[0]; + return &ipv6_addr_str[0]; } void bip6_set_interface(char *ifname) { - struct net_if *interface = 0; - int index = -1; - int x = 0; - - BACNET_IP6_ADDRESS unicast = {0}; - BACNET_IP6_ADDRESS multicast = {0}; - - unicast.port = bip6_get_port(); - multicast.port = unicast.port; - - LOG_DBG("bip6_set_interface()"); - LOG_INF("BIP6: UDP port: 0x%04X", (unsigned)unicast.port); - LOG_INF("BIP6: seeking interface: %s", ifname ? ifname : "NULL"); - if (ifname) { - index = atoi(ifname); - - /* if index is zero, discern between "0" and a parse error */ - if (!index && strcmp(ifname, "0")) { - LOG_ERR("%s:%d - Argument must parse to an integer", THIS_FILE, __LINE__); - } else { - interface = net_if_get_by_index(index); - if (interface) { - LOG_INF("BIP6: Using interface %d", index); - } else { - LOG_ERR("%s:%d - No interface at index %d", THIS_FILE, __LINE__, - index); - } - } - } - if (index == -1) { - LOG_INF("BIP6: No valid interface specified. Using default "); - interface = net_if_get_default(); - } - - if (interface) { - BIP6_Socket_Scope_Id = net_if_get_by_iface(interface); - LOG_INF("BIP6: Socket Scope ID = %d", BIP6_Socket_Scope_Id); - LOG_INF("BIP6: Interface set - Configured addresses:"); - for (x = 0; x < NET_IF_MAX_IPV6_ADDR; x++) { - inet6_ntoa(&interface->config.ip.ipv6->unicast[x].address.in6_addr); - LOG_INF(" unicast[%d]: %s", x, ipv6_addr_str); - } - for (x = 0; x < NET_IF_MAX_IPV6_MADDR; x++) { - inet6_ntoa(&interface->config.ip.ipv6->mcast[x].address.in6_addr); - LOG_INF(" multicast[%d]: %s", x, ipv6_addr_str); - } - if (CONFIG_BACDL_BIP6_ADDRESS_INDEX >= NET_IF_MAX_IPV6_ADDR) { - LOG_ERR("%s:%d - IPv6 address index of %d is out of range (0-%d)", - THIS_FILE, __LINE__, CONFIG_BACDL_BIP6_ADDRESS_INDEX, - NET_IF_MAX_IPV6_ADDR - 1); - return; - } - LOG_INF("BIP6: Using configured index %d", CONFIG_BACDL_BIP6_ADDRESS_INDEX); - memcpy(&unicast.address, - &interface->config.ip.ipv6->unicast[CONFIG_BACDL_BIP6_ADDRESS_INDEX] - .address.in6_addr, - IP6_ADDRESS_MAX); - - if (net_addr_pton(AF_INET6, CONFIG_BACDL_BIP6_MCAST_ADDRESS, &multicast.address)) { - LOG_ERR("%s:%d - Failed to parse IPv6 multicast address: %s", THIS_FILE, - __LINE__, CONFIG_BACDL_BIP6_MCAST_ADDRESS); - } - - bip6_set_addr(&unicast); - bip6_set_broadcast_addr(&multicast); - - LOG_INF(" Unicast: %s", inet6_ntoa((struct in6_addr *)&unicast.address)); - LOG_INF(" Multicast: %s", inet6_ntoa((struct in6_addr *)&multicast.address)); - } else { - LOG_ERR("%s:%d - Failed to set interface", THIS_FILE, __LINE__); - } + struct net_if *interface = 0; + int index = -1; + int x = 0; + + BACNET_IP6_ADDRESS unicast = { 0 }; + BACNET_IP6_ADDRESS multicast = { 0 }; + + unicast.port = bip6_get_port(); + multicast.port = unicast.port; + + LOG_DBG("bip6_set_interface()"); + LOG_INF("BIP6: UDP port: 0x%04X", (unsigned)unicast.port); + LOG_INF("BIP6: seeking interface: %s", ifname ? ifname : "NULL"); + if (ifname) { + index = atoi(ifname); + + /* if index is zero, discern between "0" and a parse error */ + if (!index && strcmp(ifname, "0")) { + LOG_ERR( + "%s:%d - Argument must parse to an integer", THIS_FILE, + __LINE__); + } else { + interface = net_if_get_by_index(index); + if (interface) { + LOG_INF("BIP6: Using interface %d", index); + } else { + LOG_ERR( + "%s:%d - No interface at index %d", THIS_FILE, __LINE__, + index); + } + } + } + if (index == -1) { + LOG_INF("BIP6: No valid interface specified. Using default "); + interface = net_if_get_default(); + } + + if (interface) { + BIP6_Socket_Scope_Id = net_if_get_by_iface(interface); + LOG_INF("BIP6: Socket Scope ID = %d", BIP6_Socket_Scope_Id); + LOG_INF("BIP6: Interface set - Configured addresses:"); + for (x = 0; x < NET_IF_MAX_IPV6_ADDR; x++) { + inet6_ntoa(&interface->config.ip.ipv6->unicast[x].address.in6_addr); + LOG_INF(" unicast[%d]: %s", x, ipv6_addr_str); + } + for (x = 0; x < NET_IF_MAX_IPV6_MADDR; x++) { + inet6_ntoa(&interface->config.ip.ipv6->mcast[x].address.in6_addr); + LOG_INF(" multicast[%d]: %s", x, ipv6_addr_str); + } + if (CONFIG_BACDL_BIP6_ADDRESS_INDEX >= NET_IF_MAX_IPV6_ADDR) { + LOG_ERR( + "%s:%d - IPv6 address index of %d is out of range (0-%d)", + THIS_FILE, __LINE__, CONFIG_BACDL_BIP6_ADDRESS_INDEX, + NET_IF_MAX_IPV6_ADDR - 1); + return; + } + LOG_INF( + "BIP6: Using configured index %d", CONFIG_BACDL_BIP6_ADDRESS_INDEX); + memcpy( + &unicast.address, + &interface->config.ip.ipv6->unicast[CONFIG_BACDL_BIP6_ADDRESS_INDEX] + .address.in6_addr, + IP6_ADDRESS_MAX); + + if (net_addr_pton( + AF_INET6, CONFIG_BACDL_BIP6_MCAST_ADDRESS, + &multicast.address)) { + LOG_ERR( + "%s:%d - Failed to parse IPv6 multicast address: %s", THIS_FILE, + __LINE__, CONFIG_BACDL_BIP6_MCAST_ADDRESS); + } + + bip6_set_addr(&unicast); + bip6_set_broadcast_addr(&multicast); + + LOG_INF( + " Unicast: %s", inet6_ntoa((struct in6_addr *)&unicast.address)); + LOG_INF( + " Multicast: %s", + inet6_ntoa((struct in6_addr *)&multicast.address)); + } else { + LOG_ERR("%s:%d - Failed to set interface", THIS_FILE, __LINE__); + } } /** @@ -162,8 +175,8 @@ void bip6_set_interface(char *ifname) */ void bip6_set_port(uint16_t port) { - BIP6_Addr.port = port; - BIP6_Broadcast_Addr.port = port; + BIP6_Addr.port = port; + BIP6_Broadcast_Addr.port = port; } /** @@ -173,7 +186,7 @@ void bip6_set_port(uint16_t port) */ uint16_t bip6_get_port(void) { - return BIP6_Addr.port; + return BIP6_Addr.port; } /** @@ -184,11 +197,11 @@ uint16_t bip6_get_port(void) */ void bip6_get_broadcast_address(BACNET_ADDRESS *addr) { - if (addr) { - addr->net = BACNET_BROADCAST_NETWORK; - addr->mac_len = 0; - addr->len = 0; - } + if (addr) { + addr->net = BACNET_BROADCAST_NETWORK; + addr->mac_len = 0; + addr->len = 0; + } } /** @@ -198,12 +211,12 @@ void bip6_get_broadcast_address(BACNET_ADDRESS *addr) */ void bip6_get_my_address(BACNET_ADDRESS *addr) { - uint32_t device_id = 0; + uint32_t device_id = 0; - if (addr) { - device_id = Device_Object_Instance_Number(); - bvlc6_vmac_address_set(addr, device_id); - } + if (addr) { + device_id = Device_Object_Instance_Number(); + bvlc6_vmac_address_set(addr, device_id); + } } /** @@ -213,7 +226,7 @@ void bip6_get_my_address(BACNET_ADDRESS *addr) */ bool bip6_set_addr(BACNET_IP6_ADDRESS *addr) { - return bvlc6_address_copy(&BIP6_Addr, addr); + return bvlc6_address_copy(&BIP6_Addr, addr); } /** @@ -223,7 +236,7 @@ bool bip6_set_addr(BACNET_IP6_ADDRESS *addr) */ bool bip6_get_addr(BACNET_IP6_ADDRESS *addr) { - return bvlc6_address_copy(addr, &BIP6_Addr); + return bvlc6_address_copy(addr, &BIP6_Addr); } /** @@ -233,7 +246,7 @@ bool bip6_get_addr(BACNET_IP6_ADDRESS *addr) */ bool bip6_set_broadcast_addr(BACNET_IP6_ADDRESS *addr) { - return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); + return bvlc6_address_copy(&BIP6_Broadcast_Addr, addr); } /** @@ -243,7 +256,7 @@ bool bip6_set_broadcast_addr(BACNET_IP6_ADDRESS *addr) */ bool bip6_get_broadcast_addr(BACNET_IP6_ADDRESS *addr) { - return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); + return bvlc6_address_copy(addr, &BIP6_Broadcast_Addr); } /** @@ -259,34 +272,36 @@ bool bip6_get_broadcast_addr(BACNET_IP6_ADDRESS *addr) */ int bip6_send_mpdu(BACNET_IP6_ADDRESS *dest, uint8_t *mtu, uint16_t mtu_len) { - struct sockaddr_in6 bvlc_dest = {0}; - uint16_t addr16[8]; - - /* assumes that the driver has already been initialized */ - if (BIP6_Socket < 0) { - LOG_ERR("%s:%d - Socket not initialized!", THIS_FILE, __LINE__); - return BIP6_Socket; - } - - /* load destination IP address */ - bvlc_dest.sin6_family = AF_INET6; - bvlc6_address_get(dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], &addr16[4], - &addr16[5], &addr16[6], &addr16[7]); - bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); - bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); - bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); - bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); - bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); - bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); - bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); - bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); - bvlc_dest.sin6_port = htons(dest->port); - bvlc_dest.sin6_scope_id = BIP6_Socket_Scope_Id; - inet6_ntoa(&bvlc_dest.sin6_addr); - LOG_DBG("BIP6: Sending MPDU to %s", ipv6_addr_str); - /* Send the packet */ - return zsock_sendto(BIP6_Socket, (char *)mtu, mtu_len, 0, (struct sockaddr *)&bvlc_dest, - sizeof(bvlc_dest)); + struct sockaddr_in6 bvlc_dest = { 0 }; + uint16_t addr16[8]; + + /* assumes that the driver has already been initialized */ + if (BIP6_Socket < 0) { + LOG_ERR("%s:%d - Socket not initialized!", THIS_FILE, __LINE__); + return BIP6_Socket; + } + + /* load destination IP address */ + bvlc_dest.sin6_family = AF_INET6; + bvlc6_address_get( + dest, &addr16[0], &addr16[1], &addr16[2], &addr16[3], &addr16[4], + &addr16[5], &addr16[6], &addr16[7]); + bvlc_dest.sin6_addr.s6_addr16[0] = htons(addr16[0]); + bvlc_dest.sin6_addr.s6_addr16[1] = htons(addr16[1]); + bvlc_dest.sin6_addr.s6_addr16[2] = htons(addr16[2]); + bvlc_dest.sin6_addr.s6_addr16[3] = htons(addr16[3]); + bvlc_dest.sin6_addr.s6_addr16[4] = htons(addr16[4]); + bvlc_dest.sin6_addr.s6_addr16[5] = htons(addr16[5]); + bvlc_dest.sin6_addr.s6_addr16[6] = htons(addr16[6]); + bvlc_dest.sin6_addr.s6_addr16[7] = htons(addr16[7]); + bvlc_dest.sin6_port = htons(dest->port); + bvlc_dest.sin6_scope_id = BIP6_Socket_Scope_Id; + inet6_ntoa(&bvlc_dest.sin6_addr); + LOG_DBG("BIP6: Sending MPDU to %s", ipv6_addr_str); + /* Send the packet */ + return zsock_sendto( + BIP6_Socket, (char *)mtu, mtu_len, 0, (struct sockaddr *)&bvlc_dest, + sizeof(bvlc_dest)); } /** @@ -301,9 +316,13 @@ int bip6_send_mpdu(BACNET_IP6_ADDRESS *dest, uint8_t *mtu, uint16_t mtu_len) * @return Upon successful completion, returns the number of bytes sent. * Otherwise, -1 shall be returned and errno set to indicate the error. */ -int bip6_send_pdu(BACNET_ADDRESS *dest, BACNET_NPDU_DATA *npdu_data, uint8_t *pdu, unsigned pdu_len) +int bip6_send_pdu( + BACNET_ADDRESS *dest, + BACNET_NPDU_DATA *npdu_data, + uint8_t *pdu, + unsigned pdu_len) { - return bvlc6_send_pdu(dest, npdu_data, pdu, pdu_len); + return bvlc6_send_pdu(dest, npdu_data, pdu, pdu_len); } /** @@ -314,13 +333,15 @@ int bip6_send_pdu(BACNET_ADDRESS *dest, BACNET_NPDU_DATA *npdu_data, uint8_t *pd */ static int bvlc6_snprintf_addr(char *s, size_t n, BACNET_IP6_ADDRESS *addr) { - uint16_t addr16[8]; + uint16_t addr16[8]; - bvlc6_address_get(addr, &addr16[0], &addr16[1], &addr16[2], &addr16[3], &addr16[4], - &addr16[5], &addr16[6], &addr16[7]); + bvlc6_address_get( + addr, &addr16[0], &addr16[1], &addr16[2], &addr16[3], &addr16[4], + &addr16[5], &addr16[6], &addr16[7]); - return snprintf(s, n, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", addr16[0], addr16[1], - addr16[2], addr16[3], addr16[4], addr16[5], addr16[6], addr16[7]); + return snprintf( + s, n, "%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X", addr16[0], addr16[1], + addr16[2], addr16[3], addr16[4], addr16[5], addr16[6], addr16[7]); } /** @@ -333,82 +354,87 @@ static int bvlc6_snprintf_addr(char *s, size_t n, BACNET_IP6_ADDRESS *addr) * * @return Number of bytes received, or 0 if none or timeout. */ -uint16_t bip6_receive(BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout) +uint16_t bip6_receive( + BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, unsigned timeout) { - uint16_t npdu_len = 0; /* return value */ - zsock_fd_set read_fds; - int max = 0; - struct zsock_timeval select_timeout; - struct sockaddr_in6 sin = {0}; - BACNET_IP6_ADDRESS addr = {0}; - socklen_t sin_len = sizeof(sin); - int received_bytes = 0; - int offset = 0; - uint16_t i = 0; - - /* Make sure the socket is open */ - if (BIP6_Socket < 0) { - return 0; - } - /* we could just use a non-blocking socket, but that consumes all - the CPU time. We can use a timeout; it is only supported as - a select. */ - if (timeout >= 1000) { - select_timeout.tv_sec = timeout / 1000; - select_timeout.tv_usec = 1000 * (timeout - select_timeout.tv_sec * 1000); - } else { - select_timeout.tv_sec = 0; - select_timeout.tv_usec = 1000 * timeout; - } - ZSOCK_FD_ZERO(&read_fds); - ZSOCK_FD_SET(BIP6_Socket, &read_fds); - max = BIP6_Socket; - /* see if there is a packet for us */ - if (zsock_select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { - received_bytes = zsock_recvfrom(BIP6_Socket, (char *)&npdu[0], max_npdu, 0, - (struct sockaddr *)&sin, &sin_len); - } else { - return 0; - } - - /* See if there is a problem */ - if (received_bytes < 0) { - LOG_WRN("%s:%d - RX zsock_recvfrom() error: %d", THIS_FILE, __LINE__, - received_bytes); - return 0; - } - /* no problem, just no bytes */ - if (received_bytes == 0) { - return 0; - } - /* the signature of a BACnet/IPv6 packet */ - if (npdu[0] != BVLL_TYPE_BACNET_IP6) { - LOG_DBG("BIP6: not a BACnet packet. Dropped."); - return 0; - } - /* pass the packet into the BBMD handler */ - inet6_ntoa(&sin.sin6_addr); - LOG_DBG("BIP6: Received MPDU from %s", ipv6_addr_str); - bvlc6_address_set(&addr, ntohs(sin.sin6_addr.s6_addr16[0]), - ntohs(sin.sin6_addr.s6_addr16[1]), ntohs(sin.sin6_addr.s6_addr16[2]), - ntohs(sin.sin6_addr.s6_addr16[3]), ntohs(sin.sin6_addr.s6_addr16[4]), - ntohs(sin.sin6_addr.s6_addr16[5]), ntohs(sin.sin6_addr.s6_addr16[6]), - ntohs(sin.sin6_addr.s6_addr16[7])); - addr.port = ntohs(sin.sin6_port); - offset = bvlc6_handler(&addr, src, npdu, received_bytes); - if (offset > 0) { - npdu_len = received_bytes - offset; - if (npdu_len <= max_npdu) { - /* shift the buffer to return a valid NPDU */ - for (i = 0; i < npdu_len; i++) { - npdu[i] = npdu[offset + i]; - } - } else { - LOG_WRN("%s:%d - NPDU dropped (too long)!", THIS_FILE, __LINE__); - npdu_len = 0; - } - } - return npdu_len; + uint16_t npdu_len = 0; /* return value */ + zsock_fd_set read_fds; + int max = 0; + struct zsock_timeval select_timeout; + struct sockaddr_in6 sin = { 0 }; + BACNET_IP6_ADDRESS addr = { 0 }; + socklen_t sin_len = sizeof(sin); + int received_bytes = 0; + int offset = 0; + uint16_t i = 0; + + /* Make sure the socket is open */ + if (BIP6_Socket < 0) { + return 0; + } + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } else { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + ZSOCK_FD_ZERO(&read_fds); + ZSOCK_FD_SET(BIP6_Socket, &read_fds); + max = BIP6_Socket; + /* see if there is a packet for us */ + if (zsock_select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { + received_bytes = zsock_recvfrom( + BIP6_Socket, (char *)&npdu[0], max_npdu, 0, (struct sockaddr *)&sin, + &sin_len); + } else { + return 0; + } + + /* See if there is a problem */ + if (received_bytes < 0) { + LOG_WRN( + "%s:%d - RX zsock_recvfrom() error: %d", THIS_FILE, __LINE__, + received_bytes); + return 0; + } + /* no problem, just no bytes */ + if (received_bytes == 0) { + return 0; + } + /* the signature of a BACnet/IPv6 packet */ + if (npdu[0] != BVLL_TYPE_BACNET_IP6) { + LOG_DBG("BIP6: not a BACnet packet. Dropped."); + return 0; + } + /* pass the packet into the BBMD handler */ + inet6_ntoa(&sin.sin6_addr); + LOG_DBG("BIP6: Received MPDU from %s", ipv6_addr_str); + bvlc6_address_set( + &addr, ntohs(sin.sin6_addr.s6_addr16[0]), + ntohs(sin.sin6_addr.s6_addr16[1]), ntohs(sin.sin6_addr.s6_addr16[2]), + ntohs(sin.sin6_addr.s6_addr16[3]), ntohs(sin.sin6_addr.s6_addr16[4]), + ntohs(sin.sin6_addr.s6_addr16[5]), ntohs(sin.sin6_addr.s6_addr16[6]), + ntohs(sin.sin6_addr.s6_addr16[7])); + addr.port = ntohs(sin.sin6_port); + offset = bvlc6_handler(&addr, src, npdu, received_bytes); + if (offset > 0) { + npdu_len = received_bytes - offset; + if (npdu_len <= max_npdu) { + /* shift the buffer to return a valid NPDU */ + for (i = 0; i < npdu_len; i++) { + npdu[i] = npdu[offset + i]; + } + } else { + LOG_WRN("%s:%d - NPDU dropped (too long)!", THIS_FILE, __LINE__); + npdu_len = 0; + } + } + return npdu_len; } /** Cleanup and close out the BACnet/IP services by closing the socket. @@ -416,12 +442,12 @@ uint16_t bip6_receive(BACNET_ADDRESS *src, uint8_t *npdu, uint16_t max_npdu, uns */ void bip6_cleanup(void) { - LOG_DBG("bip6_cleanup();"); - bvlc6_cleanup(); - if (BIP6_Socket != -1) { - zsock_close(BIP6_Socket); - } - BIP6_Socket = -1; + LOG_DBG("bip6_cleanup();"); + bvlc6_cleanup(); + if (BIP6_Socket != -1) { + zsock_close(BIP6_Socket); + } + BIP6_Socket = -1; } /** Initialize the BACnet/IP services at the given interface. @@ -441,63 +467,71 @@ void bip6_cleanup(void) */ bool bip6_init(char *ifname) { - int status = -1; - struct sockaddr_in6 server = {0}; - struct in6_addr broadcast_address; - struct ipv6_mreq join_request; - const int sockopt = 1; - char addr6_str[40] = ""; - - LOG_DBG("bip6_init()"); - if (BIP6_Addr.port == 0) { - bip6_set_port(0xBAC0U); - } - bip6_set_interface(ifname); - LOG_INF("BIP6: IPv6 UDP port: 0x%04X", (unsigned)BIP6_Addr.port); - bvlc6_snprintf_addr(addr6_str, sizeof(addr6_str), &BIP6_Addr); - LOG_INF("BIP6: IPv6 unicast addr: %s", addr6_str); - if (BIP6_Broadcast_Addr.address[0] == 0) { - bvlc6_address_set(&BIP6_Broadcast_Addr, BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0, - BIP6_MULTICAST_GROUP_ID); - LOG_INF("BIP6: IPv6 MULTICAST_SITE_LOCAL"); - } - bvlc6_snprintf_addr(addr6_str, sizeof(addr6_str), &BIP6_Broadcast_Addr); - LOG_INF("BIP6: IPv6 multicast addr: %s", addr6_str); - - /* assumes that the driver has already been initialized */ - BIP6_Socket = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (BIP6_Socket < 0) { - LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); - return false; - } else { - LOG_INF("Socket created"); - } - - /* Allow us to use the same socket for sending and receiving */ - /* This makes sure that the src port is correct when sending */ - status = zsock_setsockopt(BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); - if (status < 0) { - LOG_ERR("BIP6: setsockopt(SO_REUSEADDR)"); - } - /* allow us to send a broadcast */ - status = zsock_setsockopt(BIP6_Socket, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); - if (status < 0) { - /* ignored? For compatibility? Really? */ - LOG_ERR("BIP6: setsockopt(SO_BROADCAST)"); - } - /* subscribe to a multicast address */ - memcpy(&broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], IP6_ADDRESS_MAX); - memcpy(&join_request.ipv6mr_multiaddr, &broadcast_address, sizeof(struct in6_addr)); - /* Let system not choose the interface */ - join_request.ipv6mr_ifindex = BIP6_Socket_Scope_Id; - status = setsockopt(BIP6_Socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &join_request, - sizeof(join_request)); - if (status < 0) { - LOG_ERR("BIP6: setsockopt(IPV6_ADD_MEMBERSHIP)"); - return false; - } - /* bind the socket to the local port number and IP address */ - server.sin6_family = AF_INET6; + int status = -1; + struct sockaddr_in6 server = { 0 }; + struct in6_addr broadcast_address; + struct ipv6_mreq join_request; + const int sockopt = 1; + char addr6_str[40] = ""; + + LOG_DBG("bip6_init()"); + if (BIP6_Addr.port == 0) { + bip6_set_port(0xBAC0U); + } + bip6_set_interface(ifname); + LOG_INF("BIP6: IPv6 UDP port: 0x%04X", (unsigned)BIP6_Addr.port); + bvlc6_snprintf_addr(addr6_str, sizeof(addr6_str), &BIP6_Addr); + LOG_INF("BIP6: IPv6 unicast addr: %s", addr6_str); + if (BIP6_Broadcast_Addr.address[0] == 0) { + bvlc6_address_set( + &BIP6_Broadcast_Addr, BIP6_MULTICAST_SITE_LOCAL, 0, 0, 0, 0, 0, 0, + BIP6_MULTICAST_GROUP_ID); + LOG_INF("BIP6: IPv6 MULTICAST_SITE_LOCAL"); + } + bvlc6_snprintf_addr(addr6_str, sizeof(addr6_str), &BIP6_Broadcast_Addr); + LOG_INF("BIP6: IPv6 multicast addr: %s", addr6_str); + + /* assumes that the driver has already been initialized */ + BIP6_Socket = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); + if (BIP6_Socket < 0) { + LOG_ERR("%s:%d - Failed to create socket", THIS_FILE, __LINE__); + return false; + } else { + LOG_INF("Socket created"); + } + + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + status = zsock_setsockopt( + BIP6_Socket, SOL_SOCKET, SO_REUSEADDR, &sockopt, sizeof(sockopt)); + if (status < 0) { + LOG_ERR("BIP6: setsockopt(SO_REUSEADDR)"); + } + /* allow us to send a broadcast */ + status = zsock_setsockopt( + BIP6_Socket, SOL_SOCKET, SO_BROADCAST, &sockopt, sizeof(sockopt)); + if (status < 0) { + /* ignored? For compatibility? Really? */ + LOG_ERR("BIP6: setsockopt(SO_BROADCAST)"); + } + /* subscribe to a multicast address */ + memcpy( + &broadcast_address.s6_addr[0], &BIP6_Broadcast_Addr.address[0], + IP6_ADDRESS_MAX); + memcpy( + &join_request.ipv6mr_multiaddr, &broadcast_address, + sizeof(struct in6_addr)); + /* Let system not choose the interface */ + join_request.ipv6mr_ifindex = BIP6_Socket_Scope_Id; + status = setsockopt( + BIP6_Socket, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &join_request, + sizeof(join_request)); + if (status < 0) { + LOG_ERR("BIP6: setsockopt(IPV6_ADD_MEMBERSHIP)"); + return false; + } + /* bind the socket to the local port number and IP address */ + server.sin6_family = AF_INET6; #if 0 uint16_t addr16[8]; bvlc6_address_get(&BIP6_Addr, &addr16[0], &addr16[1], &addr16[2], @@ -511,22 +545,23 @@ bool bip6_init(char *ifname) server.sin6_addr.s6_addr16[6] = htons(addr16[6]); server.sin6_addr.s6_addr16[7] = htons(addr16[7]); #else - server.sin6_addr = in6addr_any; + server.sin6_addr = in6addr_any; #endif - server.sin6_port = htons(BIP6_Addr.port); - LOG_INF("BIP6: Binding to port %d", BIP6_Addr.port); - status = zsock_bind(BIP6_Socket, (const struct sockaddr *)&server, sizeof(server)); - if (status < 0) { - zsock_close(BIP6_Socket); - BIP6_Socket = -1; - LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); - return false; - } else { - LOG_INF("BIP6: Socket bound. Success!"); - } - bvlc6_init(); - - return true; + server.sin6_port = htons(BIP6_Addr.port); + LOG_INF("BIP6: Binding to port %d", BIP6_Addr.port); + status = zsock_bind( + BIP6_Socket, (const struct sockaddr *)&server, sizeof(server)); + if (status < 0) { + zsock_close(BIP6_Socket); + BIP6_Socket = -1; + LOG_ERR("%s:%d - zsock_bind() failure", THIS_FILE, __LINE__); + return false; + } else { + LOG_INF("BIP6: Socket bound. Success!"); + } + bvlc6_init(); + + return true; } /** @@ -535,5 +570,5 @@ bool bip6_init(char *ifname) */ bool bip6_valid(void) { - return (BIP6_Socket != -1); + return (BIP6_Socket != -1); } diff --git a/zephyr/subsys/bacnet_osif/bacnet_mstimer.c b/zephyr/subsys/bacnet_osif/bacnet_mstimer.c index ecc5b36..6e6d219 100644 --- a/zephyr/subsys/bacnet_osif/bacnet_mstimer.c +++ b/zephyr/subsys/bacnet_osif/bacnet_mstimer.c @@ -17,7 +17,7 @@ */ unsigned long mstimer_now(void) { - return (unsigned long)k_uptime_get_32(); + return (unsigned long)k_uptime_get_32(); } /** diff --git a/zephyr/subsys/bacnet_shell/bacnet_shell_objects.c b/zephyr/subsys/bacnet_shell/bacnet_shell_objects.c index 605f24c..d739b02 100644 --- a/zephyr/subsys/bacnet_shell/bacnet_shell_objects.c +++ b/zephyr/subsys/bacnet_shell/bacnet_shell_objects.c @@ -26,29 +26,32 @@ */ static int cmd_objects(const struct shell *sh, size_t argc, char **argv) { - int count; - BACNET_OBJECT_TYPE object_type; - uint32_t instance; - uint32_t array_index; - bool found; + int count; + BACNET_OBJECT_TYPE object_type; + uint32_t instance; + uint32_t array_index; + bool found; - (void)argc; - (void)argv; - /* display the object-list as well formed JSON */ - shell_print(sh, "{\"%s\": [", bactext_property_name(PROP_OBJECT_LIST)); - count = Device_Object_List_Count(); - for (array_index = 1; array_index <= count; array_index++) { - found = Device_Object_List_Identifier(array_index, &object_type, &instance); - if (found) { - shell_print(sh, "{\"%s\":{\"%s\":%u}}%s", - bactext_property_name(PROP_OBJECT_IDENTIFIER), - bactext_object_type_name(object_type), instance, - (array_index == count) ? "]," : ","); - } - } - shell_print(sh, "\"object-list-size\": %d}", count); + (void)argc; + (void)argv; + /* display the object-list as well formed JSON */ + shell_print(sh, "{\"%s\": [", bactext_property_name(PROP_OBJECT_LIST)); + count = Device_Object_List_Count(); + for (array_index = 1; array_index <= count; array_index++) { + found = + Device_Object_List_Identifier(array_index, &object_type, &instance); + if (found) { + shell_print( + sh, "{\"%s\":{\"%s\":%u}}%s", + bactext_property_name(PROP_OBJECT_IDENTIFIER), + bactext_object_type_name(object_type), instance, + (array_index == count) ? "]," : ","); + } + } + shell_print(sh, "\"object-list-size\": %d}", count); - return 0; + return 0; } -SHELL_SUBCMD_ADD((bacnet), objects, NULL, "list of BACnet objects", cmd_objects, 1, 0); +SHELL_SUBCMD_ADD( + (bacnet), objects, NULL, "list of BACnet objects", cmd_objects, 1, 0); diff --git a/zephyr/subsys/bacnet_shell/bacnet_shell_packets.c b/zephyr/subsys/bacnet_shell/bacnet_shell_packets.c index c785349..3f4e296 100644 --- a/zephyr/subsys/bacnet_shell/bacnet_shell_packets.c +++ b/zephyr/subsys/bacnet_shell/bacnet_shell_packets.c @@ -26,11 +26,13 @@ */ static int cmd_packets(const struct shell *sh, size_t argc, char **argv) { - (void)argc; - (void)argv; - shell_print(sh, "BACnet thread packets received: %ld", bacnet_basic_packet_count()); + (void)argc; + (void)argv; + shell_print( + sh, "BACnet thread packets received: %ld", bacnet_basic_packet_count()); - return 0; + return 0; } -SHELL_SUBCMD_ADD((bacnet), packets, NULL, "BACnet task packet stats", cmd_packets, 1, 0); +SHELL_SUBCMD_ADD( + (bacnet), packets, NULL, "BACnet task packet stats", cmd_packets, 1, 0); diff --git a/zephyr/subsys/bacnet_shell/bacnet_shell_uptime.c b/zephyr/subsys/bacnet_shell/bacnet_shell_uptime.c index 2060dab..7589eea 100644 --- a/zephyr/subsys/bacnet_shell/bacnet_shell_uptime.c +++ b/zephyr/subsys/bacnet_shell/bacnet_shell_uptime.c @@ -26,11 +26,13 @@ */ static int cmd_uptime(const struct shell *sh, size_t argc, char **argv) { - (void)argc; - (void)argv; - shell_print(sh, "BACnet thread uptime: %ld seconds", bacnet_basic_uptime_seconds()); + (void)argc; + (void)argv; + shell_print( + sh, "BACnet thread uptime: %ld seconds", bacnet_basic_uptime_seconds()); - return 0; + return 0; } -SHELL_SUBCMD_ADD((bacnet), uptime, NULL, "BACnet task uptime", cmd_uptime, 1, 0); +SHELL_SUBCMD_ADD( + (bacnet), uptime, NULL, "BACnet task uptime", cmd_uptime, 1, 0); diff --git a/zephyr/subsys/object/bacfile.c b/zephyr/subsys/object/bacfile.c index 6a73b8f..9435581 100644 --- a/zephyr/subsys/object/bacfile.c +++ b/zephyr/subsys/object/bacfile.c @@ -9,4 +9,3 @@ #include "bacnet/basic/object/bacfile.h" OBJECT_FUNCTIONS(Bacfile, BACNET_FILE_LISTING); - diff --git a/zephyr/subsys/object/device.c b/zephyr/subsys/object/device.c index 836742d..37fb26d 100644 --- a/zephyr/subsys/object/device.c +++ b/zephyr/subsys/object/device.c @@ -12,7 +12,7 @@ #include "object.h" #ifdef CONFIG_BACNET_USE_SECTION_ITERABLE_OBJECT_TABLE - extern struct object_functions _object_functions_list_end[]; +extern struct object_functions _object_functions_list_end[]; #endif #if BAC_ROUTING @@ -26,12 +26,11 @@ static object_functions Routing_object = { .Object_Read_Property = Routed_Device_Read_Property_Local, .Object_Write_Property = Routed_Device_Write_Property_Local, .Object_RPM_List = Device_Property_Lists, - .Object_RR_Info = DeviceGetRRInfo - .Object_Iterator = NULL, - .Object_Value_List = NULL, - .Object_COV = NULL, - .Object_COV_Clear = NULL, - .Object_Intrinsic_Reporting = NULL, + .Object_RR_Info = DeviceGetRRInfo.Object_Iterator = NULL, + .Object_Value_List = NULL, + .Object_COV = NULL, + .Object_COV_Clear = NULL, + .Object_Intrinsic_Reporting = NULL, }; static bool routing_Device = false; @@ -50,41 +49,43 @@ void Routing_Device_Init(uint32_t first_object_instance) #endif /* BAC_ROUTING */ #ifdef CONFIG_BACNET_USE_SECTION_ITERABLE_OBJECT_TABLE -static struct object_functions *Device_Object_Filter_Out( - struct object_functions *pObject) +static struct object_functions * +Device_Object_Filter_Out(struct object_functions *pObject) { #if BAC_ROUTING - if (routing_Device && pObject == &Device_object) + if (routing_Device && pObject == &Device_object) { return &Routing_object; - else + } else #endif - return pObject; + return pObject; } -static struct object_functions *Device_Object_Filter_In( - struct object_functions *pObject) +static struct object_functions * +Device_Object_Filter_In(struct object_functions *pObject) { #if BAC_ROUTING - if (routing_Device && pObject == &Routing_object) + if (routing_Device && pObject == &Routing_object) { return &Device_object; - else + } else #endif - return pObject; + return pObject; } struct object_functions *Device_Objects_Get_First_Object(void) { - STRUCT_SECTION_FOREACH(object_functions, pObject) { + STRUCT_SECTION_FOREACH(object_functions, pObject) + { return Device_Object_Filter_Out(pObject); } return NULL; } -struct object_functions *Device_Objects_Get_Next_Object( - struct object_functions *object) +struct object_functions * +Device_Objects_Get_Next_Object(struct object_functions *object) { - if (object == NULL) + if (object == NULL) { return NULL; + } object = Device_Object_Filter_In(object); ++object; @@ -99,7 +100,7 @@ struct object_functions *Device_Objects_Get_Next_Object( /** * Allocate a Bacnet object */ -void* Bacnet_Object_Allocate(size_t size) +void *Bacnet_Object_Allocate(size_t size) { return k_malloc(size); } diff --git a/zephyr/subsys/object/mso.c b/zephyr/subsys/object/mso.c index f3f5f0c..a72dd73 100644 --- a/zephyr/subsys/object/mso.c +++ b/zephyr/subsys/object/mso.c @@ -9,4 +9,3 @@ #include "bacnet/basic/object/mso.h" OBJECT_FUNCTIONS(Multistate_Output, MULTISTATE_OUTPUT_DESCR); - diff --git a/zephyr/subsys/object/nc.c b/zephyr/subsys/object/nc.c index 277bbfa..5b1a5d1 100644 --- a/zephyr/subsys/object/nc.c +++ b/zephyr/subsys/object/nc.c @@ -9,5 +9,5 @@ #include "bacnet/basic/object/nc.h" #if defined(INTRINSIC_REPORTING) - OBJECT_FUNCTIONS(Notification_Class, NOTIFICATION_CLASS_INFO); +OBJECT_FUNCTIONS(Notification_Class, NOTIFICATION_CLASS_INFO); #endif diff --git a/zephyr/subsys/object/object.h b/zephyr/subsys/object/object.h index 8337c45..2638b99 100644 --- a/zephyr/subsys/object/object.h +++ b/zephyr/subsys/object/object.h @@ -6,30 +6,36 @@ * @copyright SPDX-License-Identifier: Apache-2.0 */ #ifndef ZTEST_UNITTEST - #include +#include #else - #include "stdlib.h" - #define k_malloc(a) malloc(a) - #define k_free(a) free(a) +#include "stdlib.h" +#define k_malloc(a) malloc(a) +#define k_free(a) free(a) - #define _DO_CONCAT(x, y) x ## y - #define _CONCAT(x, y) _DO_CONCAT(x, y) +#define _DO_CONCAT(x, y) x##y +#define _CONCAT(x, y) _DO_CONCAT(x, y) #endif -#include "bacnet/bacdef.h" // Must be before all other bacnet/*.h files +#include "bacnet/bacdef.h" // Must be before all other bacnet/*.h files #include "bacnet/basic/sys/keylist.h" - -#define OBJECT_ENUM_FUNCTIONS(object_name, descr_type) \ - descr_type *_CONCAT(object_name, _Find_Description)(uint32_t instance) \ - { return Keylist_Data(Object_List, instance); } \ - unsigned _CONCAT(object_name, _Count)(void) \ - { return Keylist_Count(Object_List); } \ - uint32_t _CONCAT(object_name, _Index_To_Instance)(unsigned index) \ - { return Keylist_Key(Object_List, index); } \ - unsigned _CONCAT(object_name, _Instance_To_Index)(uint32_t instance) \ - { return (unsigned)Keylist_Index(Object_List, instance); } - +#define OBJECT_ENUM_FUNCTIONS(object_name, descr_type) \ + descr_type *_CONCAT(object_name, _Find_Description)(uint32_t instance) \ + { \ + return Keylist_Data(Object_List, instance); \ + } \ + unsigned _CONCAT(object_name, _Count)(void) \ + { \ + return Keylist_Count(Object_List); \ + } \ + uint32_t _CONCAT(object_name, _Index_To_Instance)(unsigned index) \ + { \ + return Keylist_Key(Object_List, index); \ + } \ + unsigned _CONCAT(object_name, _Instance_To_Index)(uint32_t instance) \ + { \ + return (unsigned)Keylist_Index(Object_List, instance); \ + } #define OBJECT_MEMORY_FUNCTIONS(object_name, descr_type) \ bool _CONCAT(object_name, _Create)(uint32_t instance) \ @@ -69,15 +75,16 @@ return status; \ } -#define OBJECT_FUNCTIONS_WITHOUT_INIT(object_name, descr_type) \ - static OS_Keylist Object_List = NULL; \ - OBJECT_ENUM_FUNCTIONS(object_name, descr_type) \ +#define OBJECT_FUNCTIONS_WITHOUT_INIT(object_name, descr_type) \ + static OS_Keylist Object_List = NULL; \ + OBJECT_ENUM_FUNCTIONS(object_name, descr_type) \ OBJECT_MEMORY_FUNCTIONS(object_name, descr_type) -#define OBJECT_FUNCTIONS(object_name, descr_type) \ - OBJECT_FUNCTIONS_WITHOUT_INIT(object_name, descr_type) \ - void _CONCAT(object_name, _Init)(void){ \ - if (!Object_List) { \ - Object_List = Keylist_Create(); \ - } \ +#define OBJECT_FUNCTIONS(object_name, descr_type) \ + OBJECT_FUNCTIONS_WITHOUT_INIT(object_name, descr_type) \ + void _CONCAT(object_name, _Init)(void) \ + { \ + if (!Object_List) { \ + Object_List = Keylist_Create(); \ + } \ } diff --git a/zephyr/subsys/server/server.c b/zephyr/subsys/server/server.c index 1eff8c8..d9456c9 100644 --- a/zephyr/subsys/server/server.c +++ b/zephyr/subsys/server/server.c @@ -41,26 +41,29 @@ LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); enum bacnet_server_msg_type { - SERVER_MSG_TYPE_INVALID = 0, - SERVER_MSG_TYPE_IPV4_EVENT, + SERVER_MSG_TYPE_INVALID = 0, + SERVER_MSG_TYPE_IPV4_EVENT, }; struct bacnet_server_msg { - uint8_t type; - uint8_t dummy[3]; - uint32_t parm_u32; - void *parm_ptr; + uint8_t type; + uint8_t dummy[3]; + uint32_t parm_u32; + void *parm_ptr; }; -K_MSGQ_DEFINE(bacnet_server_msgq, sizeof(struct bacnet_server_msg), 8, - alignof(struct bacnet_server_msg)); +K_MSGQ_DEFINE( + bacnet_server_msgq, + sizeof(struct bacnet_server_msg), + 8, + alignof(struct bacnet_server_msg)); -#define SERVER_IPV4_EVENTS_MASK \ - (NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ADDR_DEL) +#define SERVER_IPV4_EVENTS_MASK \ + (NET_EVENT_IPV4_ADDR_ADD | NET_EVENT_IPV4_ADDR_DEL) static struct k_thread server_thread_data; -static K_THREAD_STACK_DEFINE(server_thread_stack, - CONFIG_BACNETSTACK_BACNET_SERVER_STACK_SIZE); +static K_THREAD_STACK_DEFINE( + server_thread_stack, CONFIG_BACNETSTACK_BACNET_SERVER_STACK_SIZE); /* Keep a reference to the Ethernet interface */ static struct net_mgmt_event_callback mgmt_cb; @@ -75,61 +78,62 @@ static uint8_t BIP_Rx_Buffer[MAX_MPDU]; */ static void service_handlers_init(void) { - Device_Init(NULL); - /* we need to handle who-is to support dynamic device binding */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, - handler_who_is); - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, - handler_who_has); - /* set the handler for all the services we don't implement */ - /* It is required to send the proper reject message... */ - apdu_set_unrecognized_service_handler_handler( - handler_unrecognized_service); - /* Set the handlers for any confirmed services that we support. */ - /* We must implement read property - it's required! */ - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, - handler_read_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, - handler_read_property_multiple); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, - handler_write_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, - handler_write_property_multiple); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_REINITIALIZE_DEVICE, - handler_reinitialize_device); - /* handle communication so we can shutup when asked */ - apdu_set_confirmed_handler( - SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, - handler_device_communication_control); + Device_Init(NULL); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_HAS, handler_who_has); + /* set the handler for all the services we don't implement */ + /* It is required to send the proper reject message... */ + apdu_set_unrecognized_service_handler_handler(handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, handler_write_property_multiple); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_REINITIALIZE_DEVICE, handler_reinitialize_device); + /* handle communication so we can shutup when asked */ + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, + handler_device_communication_control); } /* TODO: Update copyright as this code pattern copied from * conn_mgr_ipv4_events_handler() */ -static void ipv4_events_handler(struct net_mgmt_event_callback *cb, - u32_t mgmt_event, struct net_if *iface) +static void ipv4_events_handler( + struct net_mgmt_event_callback *cb, u32_t mgmt_event, struct net_if *iface) { - static int counter = 0; - printk("\n*** Handler[%d]: IPv4 event %08x received on iface %p ***\n", - ++counter, mgmt_event, iface); - - if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) != mgmt_event) { - printk("\n*** Handler[%d]: ignoring event %08x on iface %p ***\n", - counter, mgmt_event, iface); - return; - } - - struct bacnet_server_msg msg = { - .type = SERVER_MSG_TYPE_IPV4_EVENT, - .parm_u32 = mgmt_event, - .parm_ptr = iface, - }; - - int ret = k_msgq_put(&bacnet_server_msgq, &msg, K_NO_WAIT); - if (ret != 0) { - printk("\n*** Handler[%d]: queue full, type %u event 0x%08x on iface %p dropped! ***\n", - counter, msg.type, msg.parm_u32, msg.parm_ptr); - } + static int counter = 0; + printk( + "\n*** Handler[%d]: IPv4 event %08x received on iface %p ***\n", + ++counter, mgmt_event, iface); + + if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) != mgmt_event) { + printk( + "\n*** Handler[%d]: ignoring event %08x on iface %p ***\n", counter, + mgmt_event, iface); + return; + } + + struct bacnet_server_msg msg = { + .type = SERVER_MSG_TYPE_IPV4_EVENT, + .parm_u32 = mgmt_event, + .parm_ptr = iface, + }; + + int ret = k_msgq_put(&bacnet_server_msgq, &msg, K_NO_WAIT); + if (ret != 0) { + printk( + "\n*** Handler[%d]: queue full, type %u event 0x%08x on iface %p " + "dropped! ***\n", + counter, msg.type, msg.parm_u32, msg.parm_ptr); + } } /** @@ -137,59 +141,56 @@ static void ipv4_events_handler(struct net_mgmt_event_callback *cb, */ static void server_thread(void) { - LOG_INF("Server: started"); - service_handlers_init(); - - bip_init("Server: from init"); - BIP_Net = 1; - - net_mgmt_init_event_callback(&mgmt_cb, ipv4_events_handler, - SERVER_IPV4_EVENTS_MASK); - net_mgmt_add_event_callback(&mgmt_cb); - - while (1) { - const s32_t sleep_ms = K_FOREVER; - - struct bacnet_server_msg msg = { - .type = SERVER_MSG_TYPE_INVALID, - }; - int ret = k_msgq_get(&bacnet_server_msgq, &msg, sleep_ms); - - /* Waiting period timed out */ - if (-EAGAIN == ret) { - BACNET_ADDRESS src = { - 0 - }; /* address where message came from */ - /* input */ - /* returns 0 bytes on timeout */ - uint16_t pdu_len = bip_receive(&src, &BIP_Rx_Buffer[0], - MAX_MPDU, 5); - - /* process */ - if (pdu_len) { - LOG_INF("Server: BIP received %u bytes.", - (unsigned)pdu_len); - } - } - - /* Message received */ - else if (0 == ret) { - switch (msg.type) { + LOG_INF("Server: started"); + service_handlers_init(); + + bip_init("Server: from init"); + BIP_Net = 1; + + net_mgmt_init_event_callback( + &mgmt_cb, ipv4_events_handler, SERVER_IPV4_EVENTS_MASK); + net_mgmt_add_event_callback(&mgmt_cb); + + while (1) { + const s32_t sleep_ms = K_FOREVER; + + struct bacnet_server_msg msg = { + .type = SERVER_MSG_TYPE_INVALID, + }; + int ret = k_msgq_get(&bacnet_server_msgq, &msg, sleep_ms); + + /* Waiting period timed out */ + if (-EAGAIN == ret) { + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + /* input */ + /* returns 0 bytes on timeout */ + uint16_t pdu_len = + bip_receive(&src, &BIP_Rx_Buffer[0], MAX_MPDU, 5); + + /* process */ + if (pdu_len) { + LOG_INF("Server: BIP received %u bytes.", (unsigned)pdu_len); + } + } + + /* Message received */ + else if (0 == ret) { + switch (msg.type) { #if defined(CONFIG_NET_IPV4) - case SERVER_MSG_TYPE_IPV4_EVENT: { - LOG_INF("Server: MSG_TYPE_IPV4_EVENT u32: %08x ptr: %p", - msg.parm_u32, msg.parm_ptr); - const u32_t mgmt_event = msg.parm_u32; - //TODO: const struct net_if *iface = msg.parm_ptr; - - /* Handle events */ - if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) != - mgmt_event) { - LOG_INF("Server: thread ignoring event"); - break; - } - - switch (NET_MGMT_GET_COMMAND(mgmt_event)) { + case SERVER_MSG_TYPE_IPV4_EVENT: { + LOG_INF( + "Server: MSG_TYPE_IPV4_EVENT u32: %08x ptr: %p", + msg.parm_u32, msg.parm_ptr); + const u32_t mgmt_event = msg.parm_u32; + // TODO: const struct net_if *iface = msg.parm_ptr; + + /* Handle events */ + if ((mgmt_event & SERVER_IPV4_EVENTS_MASK) != mgmt_event) { + LOG_INF("Server: thread ignoring event"); + break; + } + + switch (NET_MGMT_GET_COMMAND(mgmt_event)) { #if 0 case NET_EVENT_IPV4_CMD_ADDR_ADD: LOG_INF("Server: IPv4 addr activated"); @@ -202,42 +203,40 @@ static void server_thread(void) BIP_Net = 0; break; #endif - default: - LOG_INF("Server: Unsupported event %u", - mgmt_event); - break; - } + default: + LOG_INF("Server: Unsupported event %u", mgmt_event); + break; + } - } break; + } break; #endif /* defined(CONFIG_NET_IPV4) */ - default: - LOG_WRN("Server: Dropping unsupported type %u", - msg.type); - break; - } - } - - /* Returned without waiting and without message - why? */ - else { - LOG_WRN("Server: Msgq returned w/o timeout or msg! req = %d", - ret); - } - } + default: + LOG_WRN("Server: Dropping unsupported type %u", msg.type); + break; + } + } + + /* Returned without waiting and without message - why? */ + else { + LOG_WRN("Server: Msgq returned w/o timeout or msg! req = %d", ret); + } + } } static int server_init(struct device *dev) { - ARG_UNUSED(dev); + ARG_UNUSED(dev); - k_thread_create(&server_thread_data, server_thread_stack, - K_THREAD_STACK_SIZEOF(server_thread_stack), - (k_thread_entry_t)server_thread, NULL, NULL, NULL, - K_PRIO_PREEMPT(CONFIG_BACNETSTACK_BACNET_SERVER_PRIO), 0, - K_NO_WAIT); - k_thread_name_set(&server_thread_data, "BACserver"); + k_thread_create( + &server_thread_data, server_thread_stack, + K_THREAD_STACK_SIZEOF(server_thread_stack), + (k_thread_entry_t)server_thread, NULL, NULL, NULL, + K_PRIO_PREEMPT(CONFIG_BACNETSTACK_BACNET_SERVER_PRIO), 0, K_NO_WAIT); + k_thread_name_set(&server_thread_data, "BACserver"); - return 0; + return 0; } -SYS_INIT(server_init, APPLICATION, CONFIG_BACNETSTACK_BACNET_SERVER_APP_PRIORITY); +SYS_INIT( + server_init, APPLICATION, CONFIG_BACNETSTACK_BACNET_SERVER_APP_PRIORITY); diff --git a/zephyr/tests/bacnet/lighting/CMakeLists.txt b/zephyr/tests/bacnet/lighting/CMakeLists.txt index b349e70..cd553ab 100644 --- a/zephyr/tests/bacnet/lighting/CMakeLists.txt +++ b/zephyr/tests/bacnet/lighting/CMakeLists.txt @@ -35,8 +35,8 @@ if(BOARD STREQUAL unit_testing) ${BACNET_SRC}/bacstr.c ${BACNET_SRC}/bacint.c ${BACNET_SRC}/bacreal.c - ${BACNET_SRC}/bactext.c - ${BACNET_SRC}/indtext.c + ${BACNET_SRC}/bactext.c + ${BACNET_SRC}/indtext.c ${BACNET_SRC}/datetime.c ${BACNET_SRC}/timestamp.c ${BACNET_SRC}/basic/sys/days.c diff --git a/zephyr/tests/unit/bacnet/bits/CMakeLists.txt b/zephyr/tests/unit/bacnet/bits/CMakeLists.txt index 2e3ea28..83ffd73 100644 --- a/zephyr/tests/unit/bacnet/bits/CMakeLists.txt +++ b/zephyr/tests/unit/bacnet/bits/CMakeLists.txt @@ -29,4 +29,3 @@ target_include_directories(testbinary PRIVATE CONFIG_ZTEST_NEW_API BACNET_STACK_DEPRECATED_DISABLE ) - From c4c963f036aaf65598753dfc1ab89f67f90d1547 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Tue, 9 Sep 2025 15:01:38 -0500 Subject: [PATCH 9/9] Refactored bacnet settings and storage and their shells. --- .../include/bacnet_settings/bacnet_settings.h | 130 ++- .../include/bacnet_settings/bacnet_storage.h | 41 +- zephyr/samples/profiles/b-ld/src/main.c | 137 +-- zephyr/samples/profiles/b-ls/src/main.c | 212 ++-- zephyr/samples/profiles/b-sa/src/main.c | 116 +-- zephyr/samples/profiles/b-ss/src/main.c | 109 +-- zephyr/subsys/bacnet_settings/CMakeLists.txt | 3 +- .../subsys/bacnet_settings/bacnet_settings.c | 913 ++++++++++++------ .../bacnet_settings/bacnet_settings_shell.c | 226 +++++ zephyr/subsys/bacnet_settings/bacnet_shell.c | 415 -------- .../subsys/bacnet_settings/bacnet_storage.c | 830 +++++++++------- .../bacnet_settings/bacnet_storage_shell.c | 180 ++++ 12 files changed, 1880 insertions(+), 1432 deletions(-) create mode 100644 zephyr/subsys/bacnet_settings/bacnet_settings_shell.c delete mode 100644 zephyr/subsys/bacnet_settings/bacnet_shell.c create mode 100644 zephyr/subsys/bacnet_settings/bacnet_storage_shell.c diff --git a/zephyr/include/bacnet_settings/bacnet_settings.h b/zephyr/include/bacnet_settings/bacnet_settings.h index 90a0306..9454f52 100644 --- a/zephyr/include/bacnet_settings/bacnet_settings.h +++ b/zephyr/include/bacnet_settings/bacnet_settings.h @@ -23,56 +23,114 @@ * @param context The context to pass to the WriteProperty function * @return true if the WriteProperty succeeded */ -typedef bool (*bacnet_settings_restore_callback)(BACNET_WRITE_PROPERTY_DATA *wp_data, - void *context); +typedef bool (*bacnet_settings_restore_callback)( + BACNET_WRITE_PROPERTY_DATA *wp_data, void *context); #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ -void bacnet_settings_basic_store(BACNET_OBJECT_TYPE object_type, uint32_t object_instance, - BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index, - uint8_t *application_data, int application_data_len); +void bacnet_settings_basic_store( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + BACNET_ARRAY_INDEX array_index, + uint8_t *application_data, + int application_data_len); bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data); -bool bacnet_settings_write_property_restore(bacnet_settings_restore_callback cb, void *context); +bool bacnet_settings_write_property_restore( + bacnet_settings_restore_callback cb, void *context); -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); -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); +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); +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); +bool bacnet_settings_value_parse( + const char *value_string, + uint16_t object_type, + uint32_t property_id, + BACNET_APPLICATION_DATA_VALUE *value); -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); -bool bacnet_settings_real_set(uint16_t object_type, uint32_t object_instance, uint32_t property_id, - uint32_t array_index, float value); +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); +bool bacnet_settings_real_set( + uint16_t object_type, + uint32_t object_instance, + uint32_t property_id, + uint32_t array_index, + float value); -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); -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); +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); +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); -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); -bool bacnet_settings_signed_set(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, int32_t value); +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); +bool bacnet_settings_signed_set( + uint16_t object_type, + uint32_t object_instance, + uint32_t property_id, + uint32_t array_index, + int32_t value); -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); +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); -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); +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); -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); +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); -bool bacnet_settings_string_set(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, const char *value); +bool bacnet_settings_string_set( + uint16_t object_type, + uint32_t object_instance, + uint32_t property_id, + uint32_t array_index, + const char *value); bool bacnet_settings_init(void); diff --git a/zephyr/include/bacnet_settings/bacnet_storage.h b/zephyr/include/bacnet_settings/bacnet_storage.h index f41d614..3a85c43 100644 --- a/zephyr/include/bacnet_settings/bacnet_storage.h +++ b/zephyr/include/bacnet_settings/bacnet_storage.h @@ -15,19 +15,19 @@ #include #include -#define BACNET_STORAGE_VALUE_SIZE_MAX SETTINGS_MAX_VAL_LEN -#define BACNET_STORAGE_KEY_SIZE_MAX SETTINGS_MAX_NAME_LEN +#define BACNET_STORAGE_VALUE_SIZE_MAX SETTINGS_MAX_VAL_LEN +#define BACNET_STORAGE_KEY_SIZE_MAX SETTINGS_MAX_NAME_LEN #define BACNET_STORAGE_ARRAY_INDEX_NONE UINT32_MAX typedef struct bacnet_storage_key_t { - uint16_t object_type; - uint32_t object_instance; - uint32_t property_id; - uint32_t array_index; + uint16_t object_type; + uint32_t object_instance; + uint32_t property_id; + uint32_t array_index; } BACNET_STORAGE_KEY; -typedef int (*bacnet_storage_restore_callback)(BACNET_STORAGE_KEY *key, const void *data, - size_t data_size, void *context); +typedef int (*bacnet_storage_restore_callback)( + BACNET_STORAGE_KEY *key, const void *data, size_t data_size, void *context); #ifdef __cplusplus extern "C" { @@ -35,24 +35,33 @@ extern "C" { int bacnet_storage_init(void); -int bacnet_storage_load_callback_set(bacnet_storage_restore_callback cb, void *context); +int bacnet_storage_load_callback_set( + bacnet_storage_restore_callback cb, void *context); int bacnet_storage_load(void); -int bacnet_storage_handler_set(const char *name, size_t len, settings_read_cb read_cb, - void *cb_arg); +int bacnet_storage_handler_set( + const char *name, size_t len, settings_read_cb read_cb, void *cb_arg); int bacnet_storage_handler_commit(void); -int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, size_t val_len)); +int bacnet_storage_handler_export( + int (*cb)(const char *name, const void *value, size_t val_len)); bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value); bool bacnet_storage_strtol(const char *search_name, long *long_value); bool bacnet_storage_strtof(const char *search_name, float *float_value); -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); +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); +int bacnet_storage_key_parse(BACNET_STORAGE_KEY *key, size_t argc, char **argv); int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key); -int bacnet_storage_key_encode(char *buffer, size_t buffer_size, BACNET_STORAGE_KEY *key); +int bacnet_storage_key_encode( + char *buffer, size_t buffer_size, BACNET_STORAGE_KEY *key); -int bacnet_storage_set(BACNET_STORAGE_KEY *key, const void *data, size_t data_size); +int bacnet_storage_set( + BACNET_STORAGE_KEY *key, const void *data, size_t data_size); int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size); int bacnet_storage_delete(BACNET_STORAGE_KEY *key); diff --git a/zephyr/samples/profiles/b-ld/src/main.c b/zephyr/samples/profiles/b-ld/src/main.c index 513206b..f2c60f6 100644 --- a/zephyr/samples/profiles/b-ld/src/main.c +++ b/zephyr/samples/profiles/b-ld/src/main.c @@ -43,25 +43,27 @@ static const uint32_t Lighting_Instance = 1; * @param object-instance [in] The object-instance number of the object * @param old_value [in] The value to track */ -void BACnet_Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, float old_value, - float value) +void BACnet_Lighting_Output_Tracking_Value_Handler( + uint32_t object_instance, float old_value, float value) { - uint16_t steps = 0; + uint16_t steps = 0; - (void)old_value; - if (object_instance != Lighting_Instance) { - return; - } - /* Tracking value are 0.0 and 1.0-100.0 normalized */ - if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) { - steps = linear_interpolate(1.0, value, 100.0, 1, UINT16_MAX); - } else if (isgreater(value, 100.0)) { - steps = UINT16_MAX; - } else { - steps = 0; - } - LOG_INF("Lighting Output[%lu]: value=%f step=%u/%u", (unsigned long)object_instance, - (double)value, (unsigned)steps, (unsigned)UINT16_MAX); + (void)old_value; + if (object_instance != Lighting_Instance) { + return; + } + /* Tracking value are 0.0 and 1.0-100.0 normalized */ + if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) { + steps = linear_interpolate(1.0, value, 100.0, 1, UINT16_MAX); + } else if (isgreater(value, 100.0)) { + steps = UINT16_MAX; + } else { + steps = 0; + } + LOG_INF( + "Lighting Output[%lu]: value=%f step=%u/%u", + (unsigned long)object_instance, (double)value, (unsigned)steps, + (unsigned)UINT16_MAX); } /** @@ -70,18 +72,19 @@ void BACnet_Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, flo * @param context The context to pass to the WriteProperty function * @return true if the WriteProperty succeeded */ -static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +static bool +Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { - (void)context; - if (wp_data == NULL) { - return false; - } - if ((wp_data->object_type == OBJECT_DEVICE) && - (wp_data->object_instance == BACNET_MAX_INSTANCE)) { - wp_data->object_instance = Device_Object_Instance_Number(); - } + (void)context; + if (wp_data == NULL) { + return false; + } + if ((wp_data->object_type == OBJECT_DEVICE) && + (wp_data->object_instance == BACNET_MAX_INSTANCE)) { + wp_data->object_instance = Device_Object_Instance_Number(); + } - return Device_Write_Property(wp_data); + return Device_Write_Property(wp_data); } /** @@ -91,28 +94,28 @@ static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void */ static void BACnet_Lighting_Device_Init_Handler(void *context) { - (void)context; - LOG_INF("BACnet Stack Initialized"); - /* initialize objects with default values for this basic sample */ - Device_Set_Object_Instance_Number(Device_Instance); - Device_Object_Name_ANSI_Init(Device_Name); - /* lighting output object */ - Lighting_Output_Create(Lighting_Instance); - Lighting_Output_Name_Set(Lighting_Instance, "Light-1"); - /* restore any property values previously stored via WriteProperty */ - bacnet_settings_init(); - bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); - /* writable property values are stored with WriteProperty. - Set this callback after restore to prevent recursion. */ - bacnet_basic_store_callback_set(bacnet_settings_basic_store); - /* lighting output callbacks */ - Lighting_Output_Write_Present_Value_Callback_Set( - BACnet_Lighting_Output_Tracking_Value_Handler); - /* done */ - LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); - /* set the BACnet Basic Task device object timer for lighting output use */ - bacnet_basic_task_object_timer_set(10UL); - srand(sys_rand32_get()); + (void)context; + LOG_INF("BACnet Stack Initialized"); + /* initialize objects with default values for this basic sample */ + Device_Set_Object_Instance_Number(Device_Instance); + Device_Object_Name_ANSI_Init(Device_Name); + /* lighting output object */ + Lighting_Output_Create(Lighting_Instance); + Lighting_Output_Name_Set(Lighting_Instance, "Light-1"); + /* restore any property values previously stored via WriteProperty */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ + bacnet_basic_store_callback_set(bacnet_settings_basic_store); + /* lighting output callbacks */ + Lighting_Output_Write_Present_Value_Callback_Set( + BACnet_Lighting_Output_Tracking_Value_Handler); + /* done */ + LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); + /* set the BACnet Basic Task device object timer for lighting output use */ + bacnet_basic_task_object_timer_set(10UL); + srand(sys_rand32_get()); } /** @@ -122,28 +125,28 @@ static void BACnet_Lighting_Device_Init_Handler(void *context) */ static void BACnet_Lighting_Device_Task_Handler(void *context) { - (void)context; + (void)context; } int main(void) { - bool port_initialized = false; + bool port_initialized = false; - LOG_INF("BACnet Device: %s", Device_Name); - LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); - LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); - bacnet_basic_init_callback_set(BACnet_Lighting_Device_Init_Handler, NULL); - bacnet_basic_task_callback_set(BACnet_Lighting_Device_Task_Handler, NULL); - bacnet_basic_init(); - for (;;) { - k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); - bacnet_basic_task(); - if (port_initialized) { - bacnet_port_task(); - } else { - port_initialized = bacnet_port_init(); - } - } + LOG_INF("BACnet Device: %s", Device_Name); + LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); + LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); + bacnet_basic_init_callback_set(BACnet_Lighting_Device_Init_Handler, NULL); + bacnet_basic_task_callback_set(BACnet_Lighting_Device_Task_Handler, NULL); + bacnet_basic_init(); + for (;;) { + k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); + bacnet_basic_task(); + if (port_initialized) { + bacnet_port_task(); + } else { + port_initialized = bacnet_port_init(); + } + } - return 0; + return 0; } diff --git a/zephyr/samples/profiles/b-ls/src/main.c b/zephyr/samples/profiles/b-ls/src/main.c index a708385..831c16f 100644 --- a/zephyr/samples/profiles/b-ls/src/main.c +++ b/zephyr/samples/profiles/b-ls/src/main.c @@ -51,25 +51,27 @@ static BACNET_WRITE_GROUP_NOTIFICATION Write_Group_Notification; * @param object-instance [in] The object-instance number of the object * @param old_value [in] The value to track */ -static void Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, float old_value, - float value) +static void Lighting_Output_Tracking_Value_Handler( + uint32_t object_instance, float old_value, float value) { - uint16_t steps = 0; + uint16_t steps = 0; - (void)old_value; - if (object_instance != Lighting_Instance) { - return; - } - /* Tracking value are 0.0 and 1.0-100.0 normalized */ - if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) { - steps = linear_interpolate(1.0, value, 100.0, 1, UINT16_MAX); - } else if (isgreater(value, 100.0)) { - steps = UINT16_MAX; - } else { - steps = 0; - } - LOG_INF("Lighting Output[%lu]: value=%f step=%u/%u", (unsigned long)object_instance, - (double)value, (unsigned)steps, (unsigned)UINT16_MAX); + (void)old_value; + if (object_instance != Lighting_Instance) { + return; + } + /* Tracking value are 0.0 and 1.0-100.0 normalized */ + if (isgreaterequal(value, 1.0) && islessequal(value, 100.0)) { + steps = linear_interpolate(1.0, value, 100.0, 1, UINT16_MAX); + } else if (isgreater(value, 100.0)) { + steps = UINT16_MAX; + } else { + steps = 0; + } + LOG_INF( + "Lighting Output[%lu]: value=%f step=%u/%u", + (unsigned long)object_instance, (double)value, (unsigned)steps, + (unsigned)UINT16_MAX); } /** @@ -78,15 +80,18 @@ static void Lighting_Output_Tracking_Value_Handler(uint32_t object_instance, flo * @param old_value - value prior to write * @param value - value of the write */ -void Binary_Lighting_Output_Present_Value_Handler(uint32_t object_instance, - BACNET_BINARY_LIGHTING_PV old_value, - BACNET_BINARY_LIGHTING_PV value) +void Binary_Lighting_Output_Present_Value_Handler( + uint32_t object_instance, + BACNET_BINARY_LIGHTING_PV old_value, + BACNET_BINARY_LIGHTING_PV value) { - (void)old_value; - if (object_instance != Binary_Lighting_Instance) { - return; - } - LOG_INF("Binary Lighting Output[%lu]: value=%d", (unsigned long)object_instance, value); + (void)old_value; + if (object_instance != Binary_Lighting_Instance) { + return; + } + LOG_INF( + "Binary Lighting Output[%lu]: value=%d", (unsigned long)object_instance, + value); } /** @@ -95,10 +100,12 @@ void Binary_Lighting_Output_Present_Value_Handler(uint32_t object_instance, */ void Binary_Lighting_Output_Blink_Warn_Handler(uint32_t object_instance) { - if (object_instance != Binary_Lighting_Instance) { - return; - } - LOG_INF("Binary Lighting Output[%lu]: Blink Warning", (unsigned long)object_instance); + if (object_instance != Binary_Lighting_Instance) { + return; + } + LOG_INF( + "Binary Lighting Output[%lu]: Blink Warning", + (unsigned long)object_instance); } /** @@ -107,10 +114,11 @@ void Binary_Lighting_Output_Blink_Warn_Handler(uint32_t object_instance) * @param context The context to pass to the WriteProperty function * @return true if the WriteProperty succeeded */ -static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +static bool +Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { - (void)context; - return Device_Write_Property(wp_data); + (void)context; + return Device_Write_Property(wp_data); } /** @@ -120,57 +128,61 @@ static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void */ static void BACnet_Lighting_Device_Init_Handler(void *context) { - BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE member; + BACNET_DEVICE_OBJECT_PROPERTY_REFERENCE member; - (void)context; - LOG_INF("BACnet Stack Initialized"); - /* initialize objects with default values for this basic sample */ - Device_Set_Object_Instance_Number(Device_Instance); - Device_Object_Name_ANSI_Init(Device_Name); - /* lighting output object */ - Lighting_Output_Create(Lighting_Instance); - Lighting_Output_Name_Set(Lighting_Instance, "Light-1"); - /* binary lighting output object */ - Binary_Lighting_Output_Create(Binary_Lighting_Instance); - Binary_Lighting_Output_Name_Set(Binary_Lighting_Instance, "Binary-Light-1"); - /* channel object */ - Channel_Create(Channel_Instance); - Channel_Name_Set(Channel_Instance, "Lights"); - Channel_Number_Set(Channel_Instance, 1); - Channel_Control_Groups_Element_Set(Channel_Instance, 1, 1); - /* configure the channel members */ - member.objectIdentifier.type = OBJECT_LIGHTING_OUTPUT; - member.objectIdentifier.instance = Lighting_Instance; - member.propertyIdentifier = PROP_PRESENT_VALUE; - member.arrayIndex = BACNET_ARRAY_ALL; - member.deviceIdentifier.type = OBJECT_DEVICE; - member.deviceIdentifier.instance = Device_Instance; - Channel_Reference_List_Member_Element_Set(Channel_Instance, 1, &member); - /* restore any property values previously stored via WriteProperty */ - bacnet_settings_init(); - bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); - /* writable property values are stored with WriteProperty. - Set this callback after restore to prevent recursion. */ - bacnet_basic_store_callback_set(bacnet_settings_basic_store); - /* lighting output callbacks */ - Lighting_Output_Write_Present_Value_Callback_Set(Lighting_Output_Tracking_Value_Handler); - Binary_Lighting_Output_Write_Value_Callback_Set( - Binary_Lighting_Output_Present_Value_Handler); - Binary_Lighting_Output_Blink_Warn_Callback_Set(Binary_Lighting_Output_Blink_Warn_Handler); - /* link WriteGroup service to our channel object */ - Write_Group_Notification.callback = Channel_Write_Group; - handler_write_group_notification_add(&Write_Group_Notification); - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WRITE_GROUP, handler_write_group); - /* initialize timesync callback function. */ - /* local time and date */ - apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync); - handler_timesync_set_callback_set(datetime_timesync); - datetime_init(); - /* done */ - LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); - /* set the BACnet Basic Task device object timer for lighting output use */ - bacnet_basic_task_object_timer_set(10UL); - srand(sys_rand32_get()); + (void)context; + LOG_INF("BACnet Stack Initialized"); + /* initialize objects with default values for this basic sample */ + Device_Set_Object_Instance_Number(Device_Instance); + Device_Object_Name_ANSI_Init(Device_Name); + /* lighting output object */ + Lighting_Output_Create(Lighting_Instance); + Lighting_Output_Name_Set(Lighting_Instance, "Light-1"); + /* binary lighting output object */ + Binary_Lighting_Output_Create(Binary_Lighting_Instance); + Binary_Lighting_Output_Name_Set(Binary_Lighting_Instance, "Binary-Light-1"); + /* channel object */ + Channel_Create(Channel_Instance); + Channel_Name_Set(Channel_Instance, "Lights"); + Channel_Number_Set(Channel_Instance, 1); + Channel_Control_Groups_Element_Set(Channel_Instance, 1, 1); + /* configure the channel members */ + member.objectIdentifier.type = OBJECT_LIGHTING_OUTPUT; + member.objectIdentifier.instance = Lighting_Instance; + member.propertyIdentifier = PROP_PRESENT_VALUE; + member.arrayIndex = BACNET_ARRAY_ALL; + member.deviceIdentifier.type = OBJECT_DEVICE; + member.deviceIdentifier.instance = Device_Instance; + Channel_Reference_List_Member_Element_Set(Channel_Instance, 1, &member); + /* restore any property values previously stored via WriteProperty */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ + bacnet_basic_store_callback_set(bacnet_settings_basic_store); + /* lighting output callbacks */ + Lighting_Output_Write_Present_Value_Callback_Set( + Lighting_Output_Tracking_Value_Handler); + Binary_Lighting_Output_Write_Value_Callback_Set( + Binary_Lighting_Output_Present_Value_Handler); + Binary_Lighting_Output_Blink_Warn_Callback_Set( + Binary_Lighting_Output_Blink_Warn_Handler); + /* link WriteGroup service to our channel object */ + Write_Group_Notification.callback = Channel_Write_Group; + handler_write_group_notification_add(&Write_Group_Notification); + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_WRITE_GROUP, handler_write_group); + /* initialize timesync callback function. */ + /* local time and date */ + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_TIME_SYNCHRONIZATION, handler_timesync); + handler_timesync_set_callback_set(datetime_timesync); + datetime_init(); + /* done */ + LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); + /* set the BACnet Basic Task device object timer for lighting output use */ + bacnet_basic_task_object_timer_set(10UL); + srand(sys_rand32_get()); } /** @@ -180,28 +192,28 @@ static void BACnet_Lighting_Device_Init_Handler(void *context) */ static void BACnet_Lighting_Device_Task_Handler(void *context) { - (void)context; + (void)context; } int main(void) { - bool port_initialized = false; + bool port_initialized = false; - LOG_INF("BACnet Device: %s", Device_Name); - LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); - LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); - bacnet_basic_init_callback_set(BACnet_Lighting_Device_Init_Handler, NULL); - bacnet_basic_task_callback_set(BACnet_Lighting_Device_Task_Handler, NULL); - bacnet_basic_init(); - for (;;) { - k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); - bacnet_basic_task(); - if (port_initialized) { - bacnet_port_task(); - } else { - port_initialized = bacnet_port_init(); - } - } + LOG_INF("BACnet Device: %s", Device_Name); + LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); + LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); + bacnet_basic_init_callback_set(BACnet_Lighting_Device_Init_Handler, NULL); + bacnet_basic_task_callback_set(BACnet_Lighting_Device_Task_Handler, NULL); + bacnet_basic_init(); + for (;;) { + k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); + bacnet_basic_task(); + if (port_initialized) { + bacnet_port_task(); + } else { + port_initialized = bacnet_port_init(); + } + } - return 0; + return 0; } diff --git a/zephyr/samples/profiles/b-sa/src/main.c b/zephyr/samples/profiles/b-sa/src/main.c index 6a025e2..f77eedf 100644 --- a/zephyr/samples/profiles/b-sa/src/main.c +++ b/zephyr/samples/profiles/b-sa/src/main.c @@ -41,7 +41,7 @@ static struct mstimer Actuator_Update_Timer; static void BACnet_Smart_Actuator_Datalink_Init(void) { - /* nothing to do */ + /* nothing to do */ } /** @@ -50,10 +50,11 @@ static void BACnet_Smart_Actuator_Datalink_Init(void) * @param context The context to pass to the WriteProperty function * @return true if the WriteProperty succeeded */ -static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +static bool +Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { - (void)context; - return Device_Write_Property(wp_data); + (void)context; + return Device_Write_Property(wp_data); } /** @@ -63,28 +64,28 @@ static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void */ static void BACnet_Smart_Actuator_Init_Handler(void *context) { - (void)context; - LOG_INF("BACnet Stack Initialized"); - BACnet_Smart_Actuator_Datalink_Init(); - /* initialize objects with default values for this basic sample */ - Device_Init(NULL); - Device_Set_Object_Instance_Number(Device_Instance); - Device_Object_Name_ANSI_Init(Device_Name); - Analog_Output_Create(Actuator_Instance); - Analog_Output_Name_Set(Actuator_Instance, "Actuator"); - Analog_Output_Units_Set(Actuator_Instance, UNITS_PERCENT); - Analog_Output_Min_Pres_Value_Set(Actuator_Instance, 0.0f); - Analog_Output_Max_Pres_Value_Set(Actuator_Instance, 100.0f); - /* restore any property values previously stored via WriteProperty */ - bacnet_settings_init(); - bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); - /* writable property values are stored with WriteProperty. - Set this callback after restore to prevent recursion. */ - bacnet_basic_store_callback_set(bacnet_settings_basic_store); - LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); - /* start the seconds cyclic timer */ - mstimer_set(&Actuator_Update_Timer, 1000); - srand(sys_rand32_get()); + (void)context; + LOG_INF("BACnet Stack Initialized"); + BACnet_Smart_Actuator_Datalink_Init(); + /* initialize objects with default values for this basic sample */ + Device_Init(NULL); + Device_Set_Object_Instance_Number(Device_Instance); + Device_Object_Name_ANSI_Init(Device_Name); + Analog_Output_Create(Actuator_Instance); + Analog_Output_Name_Set(Actuator_Instance, "Actuator"); + Analog_Output_Units_Set(Actuator_Instance, UNITS_PERCENT); + Analog_Output_Min_Pres_Value_Set(Actuator_Instance, 0.0f); + Analog_Output_Max_Pres_Value_Set(Actuator_Instance, 100.0f); + /* restore any property values previously stored via WriteProperty */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ + bacnet_basic_store_callback_set(bacnet_settings_basic_store); + LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); + /* start the seconds cyclic timer */ + mstimer_set(&Actuator_Update_Timer, 1000); + srand(sys_rand32_get()); } /** @@ -94,42 +95,43 @@ static void BACnet_Smart_Actuator_Init_Handler(void *context) */ static void BACnet_Smart_Actuator_Task_Handler(void *context) { - float percent = 0.0f, change = 0.0f; + float percent = 0.0f, change = 0.0f; - (void)context; - if (mstimer_expired(&Actuator_Update_Timer)) { - mstimer_reset(&Actuator_Update_Timer); - /* simulate an internal software program, - and update the BACnet object values */ - if (Analog_Output_Out_Of_Service(Actuator_Instance)) { - return; - } - percent = Analog_Output_Present_Value(Actuator_Instance); - change = -1.0f + 2.0f * ((float)rand()) / RAND_MAX; - percent += change; - Analog_Output_Present_Value_Set(Actuator_Instance, percent, BACNET_MAX_PRIORITY); - } + (void)context; + if (mstimer_expired(&Actuator_Update_Timer)) { + mstimer_reset(&Actuator_Update_Timer); + /* simulate an internal software program, + and update the BACnet object values */ + if (Analog_Output_Out_Of_Service(Actuator_Instance)) { + return; + } + percent = Analog_Output_Present_Value(Actuator_Instance); + change = -1.0f + 2.0f * ((float)rand()) / RAND_MAX; + percent += change; + Analog_Output_Present_Value_Set( + Actuator_Instance, percent, BACNET_MAX_PRIORITY); + } } int main(void) { - bool port_initialized = false; + bool port_initialized = false; - LOG_INF("BACnet Device: %s", Device_Name); - LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); - LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); - bacnet_basic_init_callback_set(BACnet_Smart_Actuator_Init_Handler, NULL); - bacnet_basic_task_callback_set(BACnet_Smart_Actuator_Task_Handler, NULL); - bacnet_basic_init(); - for (;;) { - k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); - bacnet_basic_task(); - if (port_initialized) { - bacnet_port_task(); - } else { - port_initialized = bacnet_port_init(); - } - } + LOG_INF("BACnet Device: %s", Device_Name); + LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); + LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); + bacnet_basic_init_callback_set(BACnet_Smart_Actuator_Init_Handler, NULL); + bacnet_basic_task_callback_set(BACnet_Smart_Actuator_Task_Handler, NULL); + bacnet_basic_init(); + for (;;) { + k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); + bacnet_basic_task(); + if (port_initialized) { + bacnet_port_task(); + } else { + port_initialized = bacnet_port_init(); + } + } - return 0; + return 0; } diff --git a/zephyr/samples/profiles/b-ss/src/main.c b/zephyr/samples/profiles/b-ss/src/main.c index 293ae85..c8eeadc 100644 --- a/zephyr/samples/profiles/b-ss/src/main.c +++ b/zephyr/samples/profiles/b-ss/src/main.c @@ -45,10 +45,11 @@ static struct mstimer Sensor_Update_Timer; * @param context The context to pass to the WriteProperty function * @return true if the WriteProperty succeeded */ -static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) +static bool +Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void *context) { - (void)context; - return Device_Write_Property(wp_data); + (void)context; + return Device_Write_Property(wp_data); } /** @@ -58,28 +59,28 @@ static bool Settings_Restore_Callback(BACNET_WRITE_PROPERTY_DATA *wp_data, void */ static void BACnet_Smart_Sensor_Init_Handler(void *context) { - const float default_temperature = 25.0f; + const float default_temperature = 25.0f; - (void)context; - LOG_INF("BACnet Stack Initialized"); - /* initialize objects with default values for this basic sample */ - Device_Set_Object_Instance_Number(Device_Instance); - Device_Object_Name_ANSI_Init(Device_Name); - Analog_Input_Create(Sensor_Instance); - Analog_Input_Name_Set(Sensor_Instance, "Sensor"); - Analog_Input_Present_Value_Set(Sensor_Instance, default_temperature); - Analog_Input_Units_Set(Sensor_Instance, UNITS_DEGREES_CELSIUS); - Analog_Input_COV_Increment_Set(Sensor_Instance, 1.0f); - /* restore any property values previously stored via WriteProperty */ - bacnet_settings_init(); - bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); - /* writable property values are stored with WriteProperty. - Set this callback after restore to prevent recursion. */ - bacnet_basic_store_callback_set(bacnet_settings_basic_store); - LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); - /* start the seconds cyclic timer */ - mstimer_set(&Sensor_Update_Timer, 1000); - srand(sys_rand32_get()); + (void)context; + LOG_INF("BACnet Stack Initialized"); + /* initialize objects with default values for this basic sample */ + Device_Set_Object_Instance_Number(Device_Instance); + Device_Object_Name_ANSI_Init(Device_Name); + Analog_Input_Create(Sensor_Instance); + Analog_Input_Name_Set(Sensor_Instance, "Sensor"); + Analog_Input_Present_Value_Set(Sensor_Instance, default_temperature); + Analog_Input_Units_Set(Sensor_Instance, UNITS_DEGREES_CELSIUS); + Analog_Input_COV_Increment_Set(Sensor_Instance, 1.0f); + /* restore any property values previously stored via WriteProperty */ + bacnet_settings_init(); + bacnet_settings_write_property_restore(&Settings_Restore_Callback, NULL); + /* writable property values are stored with WriteProperty. + Set this callback after restore to prevent recursion. */ + bacnet_basic_store_callback_set(bacnet_settings_basic_store); + LOG_INF("BACnet Device ID: %u", Device_Object_Instance_Number()); + /* start the seconds cyclic timer */ + mstimer_set(&Sensor_Update_Timer, 1000); + srand(sys_rand32_get()); } /** @@ -89,41 +90,41 @@ static void BACnet_Smart_Sensor_Init_Handler(void *context) */ static void BACnet_Smart_Sensor_Task_Handler(void *context) { - float temperature = 0.0f, change = 0.0f; + float temperature = 0.0f, change = 0.0f; - (void)context; - if (mstimer_expired(&Sensor_Update_Timer)) { - mstimer_reset(&Sensor_Update_Timer); - /* simulate a sensor reading, and update the BACnet object values */ - if (Analog_Input_Out_Of_Service(Sensor_Instance)) { - return; - } - temperature = Analog_Input_Present_Value(Sensor_Instance); - change = -1.0f + 2.0f * ((float)rand()) / RAND_MAX; - temperature += change; - Analog_Input_Present_Value_Set(Sensor_Instance, temperature); - } + (void)context; + if (mstimer_expired(&Sensor_Update_Timer)) { + mstimer_reset(&Sensor_Update_Timer); + /* simulate a sensor reading, and update the BACnet object values */ + if (Analog_Input_Out_Of_Service(Sensor_Instance)) { + return; + } + temperature = Analog_Input_Present_Value(Sensor_Instance); + change = -1.0f + 2.0f * ((float)rand()) / RAND_MAX; + temperature += change; + Analog_Input_Present_Value_Set(Sensor_Instance, temperature); + } } int main(void) { - bool port_initialized = false; + bool port_initialized = false; - LOG_INF("BACnet Device: %s", Device_Name); - LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); - LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); - bacnet_basic_init_callback_set(BACnet_Smart_Sensor_Init_Handler, NULL); - bacnet_basic_task_callback_set(BACnet_Smart_Sensor_Task_Handler, NULL); - bacnet_basic_init(); - for (;;) { - k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); - bacnet_basic_task(); - if (port_initialized) { - bacnet_port_task(); - } else { - port_initialized = bacnet_port_init(); - } - } + LOG_INF("BACnet Device: %s", Device_Name); + LOG_INF("BACnet Stack Version " BACNET_VERSION_TEXT); + LOG_INF("BACnet Stack Max APDU: %d", MAX_APDU); + bacnet_basic_init_callback_set(BACnet_Smart_Sensor_Init_Handler, NULL); + bacnet_basic_task_callback_set(BACnet_Smart_Sensor_Task_Handler, NULL); + bacnet_basic_init(); + for (;;) { + k_sleep(K_MSEC(CONFIG_BACNET_BASIC_SERVER_KSLEEP)); + bacnet_basic_task(); + if (port_initialized) { + bacnet_port_task(); + } else { + port_initialized = bacnet_port_init(); + } + } - return 0; + return 0; } diff --git a/zephyr/subsys/bacnet_settings/CMakeLists.txt b/zephyr/subsys/bacnet_settings/CMakeLists.txt index 098104c..4712df0 100644 --- a/zephyr/subsys/bacnet_settings/CMakeLists.txt +++ b/zephyr/subsys/bacnet_settings/CMakeLists.txt @@ -13,5 +13,6 @@ zephyr_library_sources( ) zephyr_library_sources_ifdef(CONFIG_BACNET_SETTINGS_SHELL - bacnet_shell.c + bacnet_settings_shell.c + bacnet_storage_shell.c ) diff --git a/zephyr/subsys/bacnet_settings/bacnet_settings.c b/zephyr/subsys/bacnet_settings/bacnet_settings.c index 7f40931..411e4d4 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_settings.c +++ b/zephyr/subsys/bacnet_settings/bacnet_settings.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include "bacnet/bacdef.h" @@ -15,6 +16,7 @@ #include "bacnet/bacdcode.h" #include "bacnet/bacstr.h" #include "bacnet/bacint.h" +#include "bacnet/bactext.h" #include "bacnet/proplist.h" #include "bacnet/wp.h" @@ -31,15 +33,20 @@ static bacnet_settings_restore_callback Restore_Callback; * @param application_data_len - length of the data * @note Used directly with bacnet_basic_store_callback_set() function */ -void bacnet_settings_basic_store(BACNET_OBJECT_TYPE object_type, uint32_t object_instance, - BACNET_PROPERTY_ID object_property, BACNET_ARRAY_INDEX array_index, - uint8_t *application_data, int application_data_len) +void bacnet_settings_basic_store( + BACNET_OBJECT_TYPE object_type, + uint32_t object_instance, + BACNET_PROPERTY_ID object_property, + BACNET_ARRAY_INDEX array_index, + uint8_t *application_data, + int application_data_len) { - BACNET_STORAGE_KEY key = {0}; + BACNET_STORAGE_KEY key = { 0 }; - bacnet_storage_key_init(&key, object_type, object_instance, object_property, array_index); - /* store the data */ - (void)bacnet_storage_set(&key, application_data, application_data_len); + bacnet_storage_key_init( + &key, object_type, object_instance, object_property, array_index); + /* store the data */ + (void)bacnet_storage_set(&key, application_data, application_data_len); } /** @@ -50,34 +57,37 @@ void bacnet_settings_basic_store(BACNET_OBJECT_TYPE object_type, uint32_t object */ bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data) { - BACNET_STORAGE_KEY key = {0}; - BACNET_ARRAY_INDEX array_index; - int rv; - - if (property_list_bacnet_array_member(wp_data->object_type, wp_data->object_property)) { - array_index = wp_data->array_index; - } else if (wp_data->object_property == PROP_PRESENT_VALUE) { - /* indirect Priority_Array write */ - if (property_list_commandable_member(wp_data->object_type, - wp_data->object_property)) { - /* store the priority as an array index to be used on restore */ - array_index = wp_data->priority; - } else { - array_index = BACNET_ARRAY_ALL; - } - } else { - array_index = wp_data->array_index; - } - /* create the key */ - bacnet_storage_key_init(&key, wp_data->object_type, wp_data->object_instance, - wp_data->object_property, array_index); - /* store the data */ - rv = bacnet_storage_set(&key, wp_data->application_data, wp_data->application_data_len); - if (rv < 0) { - return false; - } - - return true; + BACNET_STORAGE_KEY key = { 0 }; + BACNET_ARRAY_INDEX array_index; + int rv; + + if (property_list_bacnet_array_member( + wp_data->object_type, wp_data->object_property)) { + array_index = wp_data->array_index; + } else if (wp_data->object_property == PROP_PRESENT_VALUE) { + /* indirect Priority_Array write */ + if (property_list_commandable_member( + wp_data->object_type, wp_data->object_property)) { + /* store the priority as an array index to be used on restore */ + array_index = wp_data->priority; + } else { + array_index = BACNET_ARRAY_ALL; + } + } else { + array_index = wp_data->array_index; + } + /* create the key */ + bacnet_storage_key_init( + &key, wp_data->object_type, wp_data->object_instance, + wp_data->object_property, array_index); + /* store the data */ + rv = bacnet_storage_set( + &key, wp_data->application_data, wp_data->application_data_len); + if (rv < 0) { + return false; + } + + return true; } /** @@ -93,69 +103,74 @@ bool bacnet_settings_write_property_store(BACNET_WRITE_PROPERTY_DATA *wp_data) * @param context [in] The context to pass to the WriteProperty function * @return 0 on success, negative on failure. */ -static int bacnet_settings_restore(uint16_t object_type, uint32_t object_instance, - uint32_t property_id, uint32_t array_index, const void *data, - size_t data_len, - bacnet_settings_restore_callback restore_function, void *context) +static int bacnet_settings_restore( + uint16_t object_type, + uint32_t object_instance, + uint32_t property_id, + uint32_t array_index, + const void *data, + size_t data_len, + bacnet_settings_restore_callback restore_function, + void *context) { - int err = -EINVAL; - bool status = false; - BACNET_WRITE_PROPERTY_DATA wp_data = {0}; - - if (data && (data_len > 0) && (data_len <= MAX_APDU)) { - wp_data.application_data_len = data_len; - memcpy(&wp_data.application_data[0], data, data_len); - wp_data.object_type = object_type; - wp_data.object_instance = object_instance; - wp_data.object_property = property_id; - if (property_list_commandable_member(object_type, property_id)) { - /* commandable: the priority is stored as an array index */ - wp_data.priority = array_index; - wp_data.array_index = BACNET_ARRAY_ALL; - } else { - wp_data.priority = BACNET_MAX_PRIORITY; - wp_data.array_index = array_index; - } - if (restore_function) { - status = restore_function(&wp_data, context); - if (status) { - err = 0; - } else { - /* map the BACnet Error to Zephyr Error */ - switch (wp_data.error_code) { - case ERROR_CODE_UNKNOWN_OBJECT: - case ERROR_CODE_UNKNOWN_PROPERTY: - err = -ENOENT; - break; - case ERROR_CODE_WRITE_ACCESS_DENIED: - err = -EACCES; - break; - case ERROR_CODE_DUPLICATE_NAME: - err = -EEXIST; - break; - case ERROR_CODE_VALUE_OUT_OF_RANGE: - err = -ERANGE; - break; - case ERROR_CODE_INVALID_DATA_TYPE: - case ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY: - err = -EINVAL; - break; - case ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY: - err = -ENOSPC; - break; - case ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED: - case ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED: - err = -ENOTSUP; - break; - default: - err = -EINVAL; - break; - } - } - } - } - - return err; + int err = -EINVAL; + bool status = false; + BACNET_WRITE_PROPERTY_DATA wp_data = { 0 }; + + if (data && (data_len > 0) && (data_len <= MAX_APDU)) { + wp_data.application_data_len = data_len; + memcpy(&wp_data.application_data[0], data, data_len); + wp_data.object_type = object_type; + wp_data.object_instance = object_instance; + wp_data.object_property = property_id; + if (property_list_commandable_member(object_type, property_id)) { + /* commandable: the priority is stored as an array index */ + wp_data.priority = array_index; + wp_data.array_index = BACNET_ARRAY_ALL; + } else { + wp_data.priority = BACNET_MAX_PRIORITY; + wp_data.array_index = array_index; + } + if (restore_function) { + status = restore_function(&wp_data, context); + if (status) { + err = 0; + } else { + /* map the BACnet Error to Zephyr Error */ + switch (wp_data.error_code) { + case ERROR_CODE_UNKNOWN_OBJECT: + case ERROR_CODE_UNKNOWN_PROPERTY: + err = -ENOENT; + break; + case ERROR_CODE_WRITE_ACCESS_DENIED: + err = -EACCES; + break; + case ERROR_CODE_DUPLICATE_NAME: + err = -EEXIST; + break; + case ERROR_CODE_VALUE_OUT_OF_RANGE: + err = -ERANGE; + break; + case ERROR_CODE_INVALID_DATA_TYPE: + case ERROR_CODE_PROPERTY_IS_NOT_AN_ARRAY: + err = -EINVAL; + break; + case ERROR_CODE_NO_SPACE_TO_WRITE_PROPERTY: + err = -ENOSPC; + break; + case ERROR_CODE_CHARACTER_SET_NOT_SUPPORTED: + case ERROR_CODE_OPTIONAL_FUNCTIONALITY_NOT_SUPPORTED: + err = -ENOTSUP; + break; + default: + err = -EINVAL; + break; + } + } + } + } + + return err; } /** @@ -165,20 +180,20 @@ static int bacnet_settings_restore(uint16_t object_type, uint32_t object_instanc * @param data_len [in] The length of the data * @return 0 on success, negative on failure. */ -static int bacnet_storage_restore_handler(BACNET_STORAGE_KEY *key, const void *data, - size_t data_len, void *context) +static int bacnet_storage_restore_handler( + BACNET_STORAGE_KEY *key, const void *data, size_t data_len, void *context) { - bool status; - int err = 0; + bool status; + int err = 0; - status = bacnet_settings_restore(key->object_type, key->object_instance, key->property_id, - key->array_index, data, data_len, Restore_Callback, - context); - if (!status) { - err = -EACCES; - } + status = bacnet_settings_restore( + key->object_type, key->object_instance, key->property_id, + key->array_index, data, data_len, Restore_Callback, context); + if (!status) { + err = -EACCES; + } - return err; + return err; } /** @@ -186,25 +201,28 @@ static int bacnet_storage_restore_handler(BACNET_STORAGE_KEY *key, const void *d * @param write_function [in] the WriteProperty function of the device object * @return true on success, false on failure. */ -bool bacnet_settings_write_property_restore(bacnet_settings_restore_callback cb, void *context) +bool bacnet_settings_write_property_restore( + bacnet_settings_restore_callback cb, void *context) { - int err; - - if (!cb) { - return false; - } - Restore_Callback = cb; - err = bacnet_storage_load_callback_set(bacnet_storage_restore_handler, context); - if (err) { - return false; - } - /* iterate over all stored settings and call the restore callback for each */ - err = bacnet_storage_load(); - if (err) { - return false; - } - - return true; + int err; + + if (!cb) { + return false; + } + Restore_Callback = cb; + err = bacnet_storage_load_callback_set( + bacnet_storage_restore_handler, context); + if (err) { + return false; + } + /* iterate over all stored settings and call the restore callback for each + */ + err = bacnet_storage_load(); + if (err) { + return false; + } + + return true; } /** @@ -216,25 +234,30 @@ bool bacnet_settings_write_property_restore(bacnet_settings_restore_callback cb, * @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) +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; + 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; } /** @@ -246,24 +269,236 @@ int bacnet_settings_value_get(uint16_t object_type, uint32_t object_instance, ui * @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) +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; + 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 Parse a BACnet application data value from a string + * @param value_string [in] The string to parse + * @param object_type [in] The BACnet object type + * @param property_id [in] The BACnet property id + * @param value [out] The parsed BACnet application data value + * @return true on success, false on failure. + * @note This is a helper function for bacnet_settings_shell.c module + * @note This is a simpler version of bacapp_parse_application_data() + */ +bool bacnet_settings_value_parse( + const char *value_string, + uint16_t object_type, + uint32_t property_id, + BACNET_APPLICATION_DATA_VALUE *value) +{ + unsigned enumerated_value = 0; + unsigned long unsigned_value = 0; + float real_value = 0.0f; + long signed_value = 0; + int scan_count = 0; + unsigned object_id_instance = 0, object_id_type = 0; + bool status = false; + + if (!value_string) { + return false; + } + if (!value) { + return false; + } + /* convert the string value into a tagged union value */ + if (isalpha(value_string[0])) { + if (property_list_commandable_member(object_type, property_id) && + (bacnet_strnicmp(value_string, "NULL", 4) == 0)) { + /* check for case insensitive NULL string */ + value->tag = BACNET_APPLICATION_TAG_NULL; + status = true; + } else if (bacnet_stricmp(value_string, "true") == 0) { + value->type.Boolean = true; + value->tag = BACNET_APPLICATION_TAG_BOOLEAN; + status = true; + } else if (bacnet_stricmp(value_string, "false") == 0) { + value->type.Boolean = false; + value->tag = BACNET_APPLICATION_TAG_BOOLEAN; + status = true; + } else { + status = bactext_object_property_strtoul( + (BACNET_OBJECT_TYPE)object_type, + (BACNET_PROPERTY_ID)property_id, value_string, + &enumerated_value); + if (status) { + value->tag = BACNET_APPLICATION_TAG_ENUMERATED; + value->type.Enumerated = (uint32_t)enumerated_value; + } + } + } + if (!status) { + switch (value->tag) { + case BACNET_APPLICATION_TAG_ENUMERATED: + if (bacnet_storage_strtoul(value_string, &unsigned_value)) { + value->type.Enumerated = (uint32_t)unsigned_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_UNSIGNED_INT: + if (bacnet_storage_strtoul(value_string, &unsigned_value)) { + value->type.Unsigned_Int = (uint32_t)unsigned_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_SIGNED_INT: + if (bacnet_storage_strtol(value_string, &signed_value)) { + value->type.Signed_Int = (int32_t)signed_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_REAL: + if (bacnet_storage_strtof(value_string, &real_value)) { + value->type.Real = (float)real_value; + status = true; + } + break; + case BACNET_APPLICATION_TAG_BIT_STRING: + status = + bitstring_init_ascii(&value->type.Bit_String, value_string); + break; + case BACNET_APPLICATION_TAG_OCTET_STRING: + status = octetstring_init_ascii_hex( + &value->type.Octet_String, value_string); + break; + case BACNET_APPLICATION_TAG_CHARACTER_STRING: + status = characterstring_init_ansi( + &value->type.Character_String, value_string); + break; + case BACNET_APPLICATION_TAG_DATE: + status = + datetime_date_init_ascii(&value->type.Date, value_string); + break; + case BACNET_APPLICATION_TAG_TIME: + status = + datetime_time_init_ascii(&value->type.Time, value_string); + break; + case BACNET_APPLICATION_TAG_OBJECT_ID: + scan_count = sscanf( + value_string, "%4u:%7u", &object_id_type, + &object_id_instance); + if (scan_count == 2) { + value->type.Object_Id.type = (uint16_t)object_id_type; + value->type.Object_Id.instance = + (uint32_t)object_id_instance; + } else { + status = false; + } + break; + default: + break; + } + } + + return status; +} + +/** + * @brief Parse a key for the BACnet storage subsystem + * @param argc Number of arguments + * @param argv Argument list + * @param object_type Pointer to the object type + * @param object_instance Pointer to the object instance + * @param property_id Pointer to the property id + * @param array_index Pointer to the array index + * @return 0 on success, negative on failure + * @note used by the shell to parse arguments + */ +int bacnet_settings_object_parse( + size_t argc, + char **argv, + uint16_t *object_type, + uint32_t *object_instance, + uint32_t *property_id, + uint32_t *array_index) +{ + long value = 0; + unsigned long unsigned_value = 0; + unsigned long array_value = 0; + int found_index = 0, scan_count = 0; + char property_name[80] = { 0 }; + + if (argc < 3) { + return -EINVAL; + } + if (bactext_object_type_strtol(argv[1], &found_index)) { + value = found_index; + } else { + return -EINVAL; + } + if ((value < 0) || (value >= UINT16_MAX)) { + return -EINVAL; + } + if (object_type) { + *object_type = (uint16_t)value; + } + if (!bacnet_storage_strtoul(argv[2], &unsigned_value)) { + return -EINVAL; + } + if (unsigned_value > 4194303) { + return -EINVAL; + } + if (object_instance) { + *object_instance = (uint32_t)unsigned_value; + } + /* property can have [] to denote priority or array */ + scan_count = sscanf(argv[3], "%lu[%lu]", &unsigned_value, &array_value); + if (scan_count < 1) { + scan_count = sscanf(argv[3], "%80s[%lu]", property_name, &array_value); + if (scan_count < 1) { + return -EINVAL; + } + if (bactext_property_strtol(property_name, &found_index)) { + value = found_index; + } else { + return -EINVAL; + } + if (value > UINT32_MAX) { + return -EINVAL; + } + if (property_id) { + *property_id = (uint32_t)value; + } + if (array_index) { + *array_index = (uint32_t)array_value; + } + } else if (scan_count < 1) { + return -EINVAL; + } else { + if (property_id) { + *property_id = (uint32_t)unsigned_value; + } + } + if (scan_count < 2) { + if (array_index) { + *array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; + } + } + + return 0; } /** @@ -275,25 +510,30 @@ bool bacnet_settings_value_set(uint16_t object_type, uint32_t object_instance, u * @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 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; + 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; } /** @@ -305,17 +545,21 @@ int bacnet_settings_real_get(uint16_t object_type, uint32_t object_instance, uin * @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) +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}; + BACNET_APPLICATION_DATA_VALUE bvalue = { 0 }; - bvalue.context_specific = false; - bvalue.tag = BACNET_APPLICATION_TAG_REAL; - bvalue.type.Real = value; + 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); + return bacnet_settings_value_set( + object_type, object_instance, property_id, array_index, &bvalue); } /** @@ -326,31 +570,35 @@ bool bacnet_settings_real_set(uint16_t object_type, uint32_t object_instance, ui * @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) +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; + 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; } /** @@ -361,18 +609,21 @@ int bacnet_settings_unsigned_get(uint16_t object_type, uint32_t object_instance, * @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) +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}; + BACNET_APPLICATION_DATA_VALUE bvalue = { 0 }; - bvalue.context_specific = false; - bvalue.tag = BACNET_APPLICATION_TAG_UNSIGNED_INT; - bvalue.type.Unsigned_Int = value; + 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); + return bacnet_settings_value_set( + object_type, object_instance, property_id, array_index, &bvalue); } /** @@ -384,29 +635,35 @@ bool bacnet_settings_unsigned_set(uint16_t object_type, uint32_t object_instance * @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) +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; + 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; } /** @@ -418,17 +675,21 @@ int bacnet_settings_signed_get(uint16_t object_type, uint32_t object_instance, u * @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) +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}; + BACNET_APPLICATION_DATA_VALUE bvalue = { 0 }; - bvalue.context_specific = false; - bvalue.tag = BACNET_APPLICATION_TAG_SIGNED_INT; - bvalue.type.Signed_Int = value; + 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); + return bacnet_settings_value_set( + object_type, object_instance, property_id, array_index, &bvalue); } /** @@ -440,26 +701,32 @@ bool bacnet_settings_signed_set(uint16_t object_type, uint32_t object_instance, * @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) +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; + 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; } /** @@ -471,22 +738,25 @@ int bacnet_settings_characterstring_get(uint16_t object_type, uint32_t object_in * @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) +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; + 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; } /** @@ -499,23 +769,29 @@ bool bacnet_settings_characterstring_ansi_set(uint16_t object_type, uint32_t obj * @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) +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; + 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; } /** @@ -527,19 +803,24 @@ int bacnet_settings_string_get(uint16_t object_type, uint32_t object_instance, u * @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) +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; + 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); + 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; + return rc == 0; } /** @@ -548,12 +829,12 @@ bool bacnet_settings_string_set(uint16_t object_type, uint32_t object_instance, */ bool bacnet_settings_init(void) { - int err; + int err; - err = bacnet_storage_init(); - if (err) { - return false; - } + err = bacnet_storage_init(); + if (err) { + return false; + } - return true; + return true; } diff --git a/zephyr/subsys/bacnet_settings/bacnet_settings_shell.c b/zephyr/subsys/bacnet_settings/bacnet_settings_shell.c new file mode 100644 index 0000000..d147217 --- /dev/null +++ b/zephyr/subsys/bacnet_settings/bacnet_settings_shell.c @@ -0,0 +1,226 @@ +/** + * @file + * @brief The BACnet shell commands for debugging and testing settings + * @author Steve Karg + * @date May 2024 + * @copyright SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; + +/** + * @brief Get or set a string using BACnet settings subsystem + * @param sh Shell + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + */ +static int cmd_value(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 }; + int rc, len, data_len; + bool status = false; + BACNET_APPLICATION_DATA_VALUE value = { 0 }; + BACNET_OBJECT_PROPERTY_VALUE object_value = { 0 }; + unsigned long unsigned_value = 0; + char value_name[80] = { 0 }; + char *value_string = NULL; + + rc = bacnet_storage_key_parse(&key, argc, argv); + if (rc < 0) { + return rc; + } + /* read the current value which also determines + the tag when setting the value */ + rc = bacnet_storage_get(&key, data, sizeof(data)); + if (rc < 0) { + shell_error(sh, "Unable to get %s", key_name); + return -EINVAL; + } + data_len = rc; + /* convert the key to a string for the shell to print */ + (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); + /* check for an assigned tag */ + if ((argc > 5) && bacnet_storage_strtoul(argv[4], &unsigned_value)) { + value.tag = unsigned_value; + value_string = argv[5]; + } else if (argc > 4) { + value_string = argv[4]; + } + if (value_string) { + len = bacapp_decode_application_data(data, data_len, &value); + status = bacnet_settings_value_parse( + value_string, key.object_type, key.property_id, &value); + if (status) { + shell_print( + sh, "Parsed %s = %s as tag=%u", key_name, value_string, + value.tag); + len = bacapp_encode_application_data(NULL, &value); + if (len <= 0) { + return -ENOTSUP; + } else if (len > sizeof(data)) { + return -EINVAL; + } + len = bacapp_encode_application_data(data, &value); + if (len <= 0) { + return -EINVAL; + } + rc = bacnet_storage_set(&key, data, len); + if (rc == 0) { + shell_print(sh, "Set %s = %s", key_name, value_string); + } else { + shell_error( + sh, "Unable to set %s = %s", key_name, value_string); + return -EINVAL; + } + } else { + shell_error( + sh, "Unable to parse value for %s = %s", key_name, + value_string); + return -EINVAL; + } + } else { + /* convert to printable value */ + len = bacapp_decode_known_property( + data, rc, &value, key.object_type, key.property_id); + if (len < 0) { + shell_error(sh, "Unable to decode value for %s", key_name); + return -EINVAL; + } + object_value.object_type = key.object_type; + object_value.object_instance = key.object_instance; + object_value.object_property = key.property_id; + object_value.array_index = key.array_index; + object_value.value = &value; + bacapp_snprintf_value(value_name, sizeof(value_name), &object_value); + shell_print(sh, "Get %s = %s", key_name, value_name); + } + + return 0; +} + +/** + * @brief Get or set a string using BACnet settings subsystem + * @param sh Shell + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + */ +static int cmd_delete(const struct shell *sh, size_t argc, char **argv) +{ + char key_name[BACNET_STORAGE_KEY_SIZE_MAX + 1] = { 0 }; + BACNET_STORAGE_KEY key = { 0 }; + int rc; + + rc = bacnet_storage_key_parse(&key, 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 > 3) { + rc = bacnet_storage_delete(&key); + if (rc == 0) { + shell_print(sh, "Deleted %s", key_name); + } else { + shell_error(sh, "Unable to delete %s", key_name); + return -EINVAL; + } + } + + return 0; +} + +/** + * @brief Callback from the Zephyr settings_restore iterator + * @param key [in] The BACnet object type + * @param data [in] The data to restore + * @param data_len [in] The length of the data + * @return 0 on success, negative on failure. + */ +static int print_storage_data( + BACNET_STORAGE_KEY *key, const void *data, size_t data_len, void *context) +{ + char data_string[80] = { 0 }; + char hex_string[3] = { 0 }; + unsigned i; + const struct shell *sh = context; + + for (i = 0; i < data_len && i < ((sizeof(data_string) / 2) - 1); i++) { + snprintf( + hex_string, sizeof(hex_string), "%02X", ((const uint8_t *)data)[i]); + strcat(data_string, hex_string); + } + if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) { + shell_print( + sh, "%s/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, data_string); + } else { + shell_print( + sh, "%s/%u/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, key->array_index, + data_string); + } + + return 0; +} + +/** + * @brief List all the BACnet stored settings + * @param sh Shell + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + */ +static int cmd_list(const struct shell *sh, size_t argc, char **argv) +{ + int err; + + err = bacnet_storage_load_callback_set(print_storage_data, (void *)sh); + if (err) { + shell_error(sh, "Failed to set settings load callback"); + return -EINVAL; + } + err = bacnet_storage_load(); + if (err) { + shell_error(sh, "Failed to load settings"); + return -EINVAL; + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_bacnet_settings_cmds, + SHELL_CMD(list, NULL, "list BACnet settings strings", cmd_list), + SHELL_CMD( + value, + NULL, + " [array index] [value]", + cmd_value), + SHELL_CMD( + delete, + NULL, + " [array index]", + cmd_delete), + SHELL_SUBCMD_SET_END); + +SHELL_SUBCMD_ADD( + (bacnet), + settings, + &sub_bacnet_settings_cmds, + "BACnet settings commands", + NULL, + 1, + 0); diff --git a/zephyr/subsys/bacnet_settings/bacnet_shell.c b/zephyr/subsys/bacnet_settings/bacnet_shell.c deleted file mode 100644 index 7fdbf0f..0000000 --- a/zephyr/subsys/bacnet_settings/bacnet_shell.c +++ /dev/null @@ -1,415 +0,0 @@ -/** - * @file - * @brief The BACnet shell commands for debugging and testing settings - * @author Steve Karg - * @date May 2024 - * @copyright SPDX-License-Identifier: Apache-2.0 - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; - -/** - * @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 = 0; - uint32_t object_instance = 0; - uint32_t property_id = 75; - uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; - long value = 0; - unsigned long unsigned_value = 0; - int found_index = 0, scan_count = 0; - char property_name[80] = {0}; - - if (argc < 3) { - shell_error(sh, "Usage: %s [value]", argv[0]); - return -EINVAL; - } - if (bactext_object_type_strtol(argv[1], &found_index)) { - value = found_index; - } else { - shell_error(sh, "Invalid object-type: %s.", argv[1]); - return -EINVAL; - } - 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; - if (!bacnet_storage_strtoul(argv[2], &unsigned_value)) { - shell_error(sh, "Invalid object-instance: %s.", argv[2]); - return -EINVAL; - } - if (unsigned_value > 4194303) { - shell_error(sh, "Invalid object-instance: %s. Must be 0-4194303.", argv[2]); - return -EINVAL; - } - object_instance = (uint32_t)unsigned_value; - /* property can have [] to denote priority or array */ - scan_count = sscanf(argv[3], "%lu[%u]", &unsigned_value, &array_index); - if (scan_count < 1) { - scan_count = sscanf(argv[3], "%80s[%u]", property_name, &array_index); - if (scan_count < 1) { - shell_error(sh, "Invalid property: %s.", argv[3]); - return -EINVAL; - } - if (bactext_property_strtol(property_name, &found_index)) { - value = found_index; - } else { - shell_error(sh, "Invalid property: %s.", property_name); - return -EINVAL; - } - if (value > UINT32_MAX) { - shell_error(sh, "Invalid property: %s. Must be 0-4294967295.", argv[3]); - return -EINVAL; - } - unsigned_value = (uint32_t)value; - } - if (scan_count < 2) { - array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; - } - property_id = (uint32_t)unsigned_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_value(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}; - int rc, len, data_len; - unsigned enumerated_value = 0; - unsigned long unsigned_value = 0; - float real_value = 0.0f; - long signed_value = 0; - int scan_count = 0; - unsigned object_type = 0; - unsigned instance = 0; - bool status = false; - BACNET_APPLICATION_DATA_VALUE value = {0}; - BACNET_OBJECT_PROPERTY_VALUE object_value = {0}; - char value_name[80] = {0}; - char *value_string = NULL; - - rc = cmd_key(&key, sh, argc, argv); - if (rc < 0) { - return rc; - } - /* read the current value which also determines - the tag when setting the value */ - rc = bacnet_storage_get(&key, data, sizeof(data)); - if (rc < 0) { - shell_error(sh, "Unable to get %s", key_name); - return -EINVAL; - } - data_len = rc; - /* convert the key to a string for the shell to print */ - (void)bacnet_storage_key_encode(key_name, sizeof(key_name), &key); - /* check for an assigned tag */ - if ((argc > 5) && bacnet_storage_strtoul(argv[4], &unsigned_value)) { - value.tag = unsigned_value; - value_string = argv[5]; - } else if (argc > 4) { - value_string = argv[4]; - } - if (value_string) { - len = bacapp_decode_application_data(data, data_len, &value); - /* convert the string value into a tagged union value */ - if (isalpha(value_string[0])) { - if (property_list_commandable_member(key.object_type, key.property_id) && - (bacnet_strnicmp(value_string, "NULL", 4) == 0)) { - /* check for case insensitive NULL string */ - value.tag = BACNET_APPLICATION_TAG_NULL; - status = true; - } else if (bacnet_stricmp(value_string, "true") == 0) { - value.type.Boolean = true; - value.tag = BACNET_APPLICATION_TAG_BOOLEAN; - status = true; - } else if (bacnet_stricmp(value_string, "false") == 0) { - value.type.Boolean = false; - value.tag = BACNET_APPLICATION_TAG_BOOLEAN; - status = true; - } else { - status = bactext_object_property_strtoul( - (BACNET_OBJECT_TYPE)key.object_type, - (BACNET_PROPERTY_ID)key.property_id, value_string, - &enumerated_value); - if (status) { - value.tag = BACNET_APPLICATION_TAG_ENUMERATED; - value.type.Enumerated = (uint32_t)enumerated_value; - } - } - } - if (!status) { - switch (value.tag) { - case BACNET_APPLICATION_TAG_ENUMERATED: - if (bacnet_storage_strtoul(value_string, &unsigned_value)) { - value.type.Enumerated = (uint32_t)unsigned_value; - status = true; - } - break; - case BACNET_APPLICATION_TAG_UNSIGNED_INT: - if (bacnet_storage_strtoul(value_string, &unsigned_value)) { - value.type.Unsigned_Int = (uint32_t)unsigned_value; - status = true; - } - break; - case BACNET_APPLICATION_TAG_SIGNED_INT: - if (bacnet_storage_strtol(value_string, &signed_value)) { - value.type.Signed_Int = (int32_t)signed_value; - status = true; - } - break; - case BACNET_APPLICATION_TAG_REAL: - if (bacnet_storage_strtof(value_string, &real_value)) { - value.type.Real = (float)real_value; - status = true; - } - break; - case BACNET_APPLICATION_TAG_BIT_STRING: - status = bitstring_init_ascii(&value.type.Bit_String, value_string); - break; - case BACNET_APPLICATION_TAG_OCTET_STRING: - status = octetstring_init_ascii_hex(&value.type.Octet_String, - value_string); - break; - case BACNET_APPLICATION_TAG_CHARACTER_STRING: - status = characterstring_init_ansi(&value.type.Character_String, - value_string); - break; - case BACNET_APPLICATION_TAG_DATE: - status = datetime_date_init_ascii(&value.type.Date, value_string); - break; - case BACNET_APPLICATION_TAG_TIME: - status = datetime_time_init_ascii(&value.type.Time, value_string); - break; - case BACNET_APPLICATION_TAG_OBJECT_ID: - scan_count = - sscanf(value_string, "%4d:%7u", &object_type, &instance); - if (scan_count == 2) { - value.type.Object_Id.type = (uint16_t)object_type; - value.type.Object_Id.instance = instance; - } else { - status = false; - } - break; - default: - break; - } - } - if (status) { - shell_print(sh, "Parsed %s = %s as tag=%u", key_name, value_string, - value.tag); - len = bacapp_encode_application_data(NULL, &value); - if (len <= 0) { - return false; - } else if (len > sizeof(data)) { - return false; - } - len = bacapp_encode_application_data(data, &value); - rc = bacnet_storage_set(&key, data, len); - if (rc == 0) { - shell_print(sh, "Set %s = %s", key_name, value_string); - } else { - shell_error(sh, "Unable to set %s = %s", key_name, value_string); - return -EINVAL; - } - } else { - shell_error(sh, "Unable to parse value for %s = %s", key_name, - value_string); - return -EINVAL; - } - } else { - /* convert to printable value */ - len = bacapp_decode_known_property(data, rc, &value, key.object_type, - key.property_id); - if (len < 0) { - shell_error(sh, "Unable to decode value for %s", key_name); - return -EINVAL; - } - object_value.object_type = key.object_type; - object_value.object_instance = key.object_instance; - object_value.object_property = key.property_id; - object_value.array_index = key.array_index; - object_value.value = &value; - bacapp_snprintf_value(value_name, sizeof(value_name), &object_value); - shell_print(sh, "Get %s = %s", key_name, value_name); - } - - 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}, test_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); - /* convert the key string to numbers for a test */ - if (bacnet_storage_key_decode(key_name, &test_key) == 0) { - shell_print(sh, "key=%s/%lu/%lu/%lu/%lu", Storage_Base_Name, - (unsigned long)test_key.object_type, - (unsigned long)test_key.object_instance, - (unsigned long)test_key.property_id, - (unsigned long)test_key.array_index); - } - 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; -} - -/** - * @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_delete(const struct shell *sh, size_t argc, char **argv) -{ - char key_name[BACNET_STORAGE_KEY_SIZE_MAX + 1] = {0}; - BACNET_STORAGE_KEY key = {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 > 3) { - rc = bacnet_storage_delete(&key); - if (rc == 0) { - shell_print(sh, "Deleted %s", key_name); - } else { - shell_error(sh, "Unable to delete %s", key_name); - return -EINVAL; - } - } - - return 0; -} - -/** - * @brief Callback from the Zephyr settings_restore iterator - * @param key [in] The BACnet object type - * @param data [in] The data to restore - * @param data_len [in] The length of the data - * @return 0 on success, negative on failure. - */ -static int print_storage_data(BACNET_STORAGE_KEY *key, const void *data, size_t data_len, - void *context) -{ - char data_string[80] = {0}; - char hex_string[3] = {0}; - unsigned i; - const struct shell *sh = context; - - for (i = 0; i < data_len && i < ((sizeof(data_string) / 2) - 1); i++) { - snprintf(hex_string, sizeof(hex_string), "%02X", ((const uint8_t *)data)[i]); - strcat(data_string, hex_string); - } - if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) { - shell_print(sh, "%s/%u/%u/%u=%s", Storage_Base_Name, key->object_type, - key->object_instance, key->property_id, data_string); - } else { - shell_print(sh, "%s/%u/%u/%u/%u=%s", Storage_Base_Name, key->object_type, - key->object_instance, key->property_id, key->array_index, data_string); - } - - return 0; -} - -/** - * @brief List all the BACnet stored settings - * @param sh Shell - * @param argc Number of arguments - * @param argv Argument list - * @return 0 on success, negative on failure - */ -static int cmd_list(const struct shell *sh, size_t argc, char **argv) -{ - int err; - - err = bacnet_storage_load_callback_set(print_storage_data, (void *)sh); - if (err) { - shell_error(sh, "Failed to set storage load callback"); - return -EINVAL; - } - err = bacnet_storage_load(); - if (err) { - shell_error(sh, "Failed to load storage"); - return -EINVAL; - } - - return 0; -} - -SHELL_STATIC_SUBCMD_SET_CREATE( - sub_bacnet_settings_cmds, SHELL_CMD(list, NULL, "list BACnet storage strings", cmd_list), - SHELL_CMD(value, NULL, " [array index] [value]", - cmd_value), - SHELL_CMD(string, NULL, - " [array index] [string]", - cmd_string), - SHELL_CMD(delete, NULL, " [array index]", - cmd_delete), - SHELL_SUBCMD_SET_END); - -SHELL_SUBCMD_ADD((bacnet), settings, &sub_bacnet_settings_cmds, "BACnet settings commands", NULL, 1, - 0); diff --git a/zephyr/subsys/bacnet_settings/bacnet_storage.c b/zephyr/subsys/bacnet_settings/bacnet_storage.c index db24854..f8dbfa5 100644 --- a/zephyr/subsys/bacnet_settings/bacnet_storage.c +++ b/zephyr/subsys/bacnet_settings/bacnet_storage.c @@ -35,7 +35,7 @@ LOG_MODULE_DECLARE(bacnet, CONFIG_BACNETSTACK_LOG_LEVEL); #define FAIL_MSG "fail (err %d)" -#define STORAGE_PARTITION storage_partition +#define STORAGE_PARTITION storage_partition #define STORAGE_PARTITION_ID FIXED_PARTITION_ID(STORAGE_PARTITION) static bacnet_storage_restore_callback BACnet_Storage_Restore_Callback; @@ -48,16 +48,17 @@ static void *BACnet_Storage_Restore_Context; * @param data_size - size of the data to restore * @return 0 on success, negative on failure */ -int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, size_t data_size) +int bacnet_storage_restore( + BACNET_STORAGE_KEY *key, const void *data, size_t data_size) { - int err = 0; + int err = 0; - if (BACnet_Storage_Restore_Callback) { - err = BACnet_Storage_Restore_Callback(key, data, data_size, - BACnet_Storage_Restore_Context); - } + if (BACnet_Storage_Restore_Callback) { + err = BACnet_Storage_Restore_Callback( + key, data, data_size, BACnet_Storage_Restore_Context); + } - return err; + return err; } /** @@ -69,28 +70,28 @@ int bacnet_storage_restore(BACNET_STORAGE_KEY *key, const void *data, size_t dat */ bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) { - char *endptr; - unsigned long value; - - value = strtoul(search_name, &endptr, 0); - if (endptr == search_name) { - /* No digits found */ - return false; - } - if (value == ULONG_MAX) { - /* If the correct value is outside the range of representable values, - {ULONG_MAX} shall be returned */ - return false; - } - if (*endptr != '\0') { - /* Extra text found */ - return false; - } - if (long_value) { - *long_value = value; - } - - return true; + char *endptr; + unsigned long value; + + value = strtoul(search_name, &endptr, 0); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if (value == ULONG_MAX) { + /* If the correct value is outside the range of representable values, + {ULONG_MAX} shall be returned */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (long_value) { + *long_value = value; + } + + return true; } /** @@ -102,28 +103,28 @@ bool bacnet_storage_strtoul(const char *search_name, unsigned long *long_value) */ bool bacnet_storage_strtol(const char *search_name, long *long_value) { - char *endptr; - long value; - - value = strtol(search_name, &endptr, 0); - if (endptr == search_name) { - /* No digits found */ - return false; - } - if (value == LONG_MAX || value == LONG_MIN) { - /* If the correct value is outside the range of representable values, - {LONG_MAX} or {LONG_MIN} shall be returned */ - return false; - } - if (*endptr != '\0') { - /* Extra text found */ - return false; - } - if (long_value) { - *long_value = value; - } - - return true; + char *endptr; + long value; + + value = strtol(search_name, &endptr, 0); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if (value == LONG_MAX || value == LONG_MIN) { + /* If the correct value is outside the range of representable values, + {LONG_MAX} or {LONG_MIN} shall be returned */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (long_value) { + *long_value = value; + } + + return true; } /** @@ -135,91 +136,93 @@ bool bacnet_storage_strtol(const char *search_name, long *long_value) */ bool bacnet_storage_strtof(const char *search_name, float *float_value) { - char *endptr; - float value; - - value = strtof(search_name, &endptr); - if (endptr == search_name) { - /* No digits found */ - return false; - } - if ((value == INFINITY) || (value == -INFINITY) || (value == NAN)) { - /* the correct value is outside the range of representable values */ - return false; - } - if (*endptr != '\0') { - /* Extra text found */ - return false; - } - if (float_value) { - *float_value = value; - } - - return true; + char *endptr; + float value; + + value = strtof(search_name, &endptr); + if (endptr == search_name) { + /* No digits found */ + return false; + } + if ((value == INFINITY) || (value == -INFINITY) || (value == NAN)) { + /* the correct value is outside the range of representable values */ + return false; + } + if (*endptr != '\0') { + /* Extra text found */ + return false; + } + if (float_value) { + *float_value = value; + } + + return true; } /* dynamic main tree handler */ struct settings_handler bacnet_storage_handler = { - .name = "bacnet", - /* This gets called when asking for a settings element value - by its name using settings_runtime_get() from the runtime backend.*/ - .h_get = NULL, - /* This gets called when the value is loaded from persisted storage - with settings_load(), or when using settings_runtime_set() from - the runtime backend.*/ - .h_set = bacnet_storage_handler_set, - /* This gets called after the settings have been loaded in full. - Sometimes you don’t want an individual setting value to take - effect right away, for example if there are multiple settings - which are interdependent.*/ - .h_commit = bacnet_storage_handler_commit, - /* This gets called to write all current settings. - This happens when settings_save() tries to save the settings - or transfer to any user-implemented back-end.*/ - .h_export = bacnet_storage_handler_export}; + .name = "bacnet", + /* This gets called when asking for a settings element value + by its name using settings_runtime_get() from the runtime backend.*/ + .h_get = NULL, + /* This gets called when the value is loaded from persisted storage + with settings_load(), or when using settings_runtime_set() from + the runtime backend.*/ + .h_set = bacnet_storage_handler_set, + /* This gets called after the settings have been loaded in full. + Sometimes you don’t want an individual setting value to take + effect right away, for example if there are multiple settings + which are interdependent.*/ + .h_commit = bacnet_storage_handler_commit, + /* This gets called to write all current settings. + This happens when settings_save() tries to save the settings + or transfer to any user-implemented back-end.*/ + .h_export = bacnet_storage_handler_export +}; /** * @brief Initialize the non-volatile data */ int bacnet_storage_init(void) { - int rc = 0; + int rc = 0; #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"); - } - } + 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_ERR("settings subsys initialization: fail (err %d)", rc); - return rc; - } - rc = settings_register(&bacnet_storage_handler); - if (rc) { - LOG_ERR("settings_register failed (err %d)", rc); - return rc; - } - - LOG_INF("settings subsys initialization: OK."); - - return rc; + rc = settings_subsys_init(); + if (rc) { + LOG_ERR("settings subsys initialization: fail (err %d)", rc); + return rc; + } + rc = settings_register(&bacnet_storage_handler); + if (rc) { + LOG_ERR("settings_register failed (err %d)", rc); + return rc; + } + + LOG_INF("settings subsys initialization: OK."); + + return rc; } /** @@ -227,14 +230,15 @@ int bacnet_storage_init(void) * @param restore_cb - pointer to the restore callback function * @return 0=success, negative on error */ -int bacnet_storage_load_callback_set(bacnet_storage_restore_callback restore_cb, void *context) +int bacnet_storage_load_callback_set( + bacnet_storage_restore_callback restore_cb, void *context) { - int rc = 0; + int rc = 0; - BACnet_Storage_Restore_Callback = restore_cb; - BACnet_Storage_Restore_Context = context; + BACnet_Storage_Restore_Callback = restore_cb; + BACnet_Storage_Restore_Context = context; - return rc; + return rc; } /** @@ -243,16 +247,85 @@ int bacnet_storage_load_callback_set(bacnet_storage_restore_callback restore_cb, */ int bacnet_storage_load(void) { - int rc = 0; + int rc = 0; + + rc = settings_load(); + if (rc) { + LOG_ERR("settings_load failed (err %d)", rc); + return rc; + } + LOG_INF("settings_load: OK."); - rc = settings_load(); - if (rc) { - LOG_ERR("settings_load failed (err %d)", rc); - return rc; - } - LOG_INF("settings_load: OK."); + return rc; +} - return rc; +/** + * @brief Parse a key for the BACnet storage subsystem + * @param key Pointer to the BACnet storage key + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + * @note used by the shell to parse arguments + */ +int bacnet_storage_key_parse(BACNET_STORAGE_KEY *key, size_t argc, char **argv) +{ + uint16_t object_type = 0; + uint32_t object_instance = 0; + uint32_t property_id = 75; + uint32_t array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; + unsigned long unsigned_value = 0; + int scan_count = 0; + + if (argc < 3) { + LOG_ERR( + "parse: %s [value]", argv[0]); + return -EINVAL; + } + if (!bacnet_storage_strtoul(argv[1], &unsigned_value)) { + LOG_ERR("parse: Invalid object-type: %s.", argv[1]); + return -EINVAL; + } + if ((unsigned_value < 0) || (unsigned_value >= UINT16_MAX)) { + LOG_ERR("parse: Invalid object-type: %s. Must be 0-65535.", argv[1]); + return -EINVAL; + } + object_type = (uint16_t)unsigned_value; + if (!bacnet_storage_strtoul(argv[2], &unsigned_value)) { + LOG_ERR("parse: Invalid object-instance: %s.", argv[2]); + return -EINVAL; + } + if (unsigned_value > 4194303) { + LOG_ERR( + "parse: Invalid object-instance: %s. Must be 0-4194303.", argv[2]); + return -EINVAL; + } + object_instance = (uint32_t)unsigned_value; + /* property can have [] to denote priority or array */ + scan_count = sscanf(argv[3], "%lu[%u]", &unsigned_value, &array_index); + if (scan_count < 1) { + LOG_ERR("parse: Invalid property: %s.", argv[3]); + return -EINVAL; + } + if (unsigned_value > UINT32_MAX) { + LOG_ERR("parse: Invalid property: %s. Must be 0-4294967295.", argv[3]); + return -EINVAL; + } + property_id = (uint32_t)unsigned_value; + if (scan_count < 2) { + array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; + } else { + if (array_index > UINT32_MAX) { + LOG_ERR( + "parse: Invalid array index: %s. Must be 0-4294967295.", + argv[3]); + return -EINVAL; + } + } + /* setup the storage key */ + bacnet_storage_key_init( + key, object_type, object_instance, property_id, array_index); + + return 0; } /** @@ -263,15 +336,19 @@ int bacnet_storage_load(void) * @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) +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; - } + if (key) { + key->object_type = object_type; + key->object_instance = object_instance; + key->property_id = property_id; + key->array_index = array_index; + } } /** @@ -281,28 +358,31 @@ void bacnet_storage_key_init(BACNET_STORAGE_KEY *key, uint16_t object_type, * @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 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; + 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; } /** @@ -313,90 +393,91 @@ int bacnet_storage_key_encode(char *buffer, size_t buffer_size, BACNET_STORAGE_K */ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) { - const char *next; - size_t next_len; - char object_type_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; - char object_instance_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; - char property_id_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; - char array_index_name[SETTINGS_MAX_DIR_DEPTH + 1] = {0}; - unsigned long long_value = 0; - const char base_name[] = CONFIG_BACNET_STORAGE_BASE_NAME; - - /* settings root name */ - if (settings_name_steq(path, base_name, &next) && next) { - /* OPTIONAL - called from shell */ - path = next; - } - /* object-type */ - next_len = settings_name_next(path, &next); - if (path) { - if (next_len + 1 > sizeof(object_type_name)) { - LOG_ERR("key: object-type name too long: %d", next_len); - return -EINVAL; - } - memcpy(object_type_name, path, next_len); - if (bacnet_storage_strtoul(object_type_name, &long_value)) { - key->object_type = long_value; - } else { - return -EINVAL; - } - } else { - return -EINVAL; - } - /* object-instance */ - path = next; - next_len = settings_name_next(path, &next); - if (path) { - if (next_len + 1 > sizeof(object_instance_name)) { - LOG_ERR("key: object-instance name too long: %d", next_len); - return -EINVAL; - } - memcpy(object_instance_name, path, next_len); - if (bacnet_storage_strtoul(object_instance_name, &long_value)) { - key->object_instance = long_value; - } else { - return -EINVAL; - } - } else { - return -EINVAL; - } - /* property-id */ - path = next; - next_len = settings_name_next(path, &next); - if (path) { - if (next_len + 1 > sizeof(property_id_name)) { - LOG_ERR("key: property-id name too long: %d", next_len); - return -EINVAL; - } - memcpy(property_id_name, path, next_len); - if (bacnet_storage_strtoul(property_id_name, &long_value)) { - key->property_id = long_value; - } else { - return -EINVAL; - } - } else { - return -EINVAL; - } - /* array-index - OPTIONAL */ - key->array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; - path = next; - next_len = settings_name_next(path, &next); - if (path) { - if (next_len + 1 > sizeof(array_index_name)) { - LOG_ERR("key: array-index name too long: %d", next_len); - return -EINVAL; - } - memcpy(array_index_name, path, next_len); - if (bacnet_storage_strtoul(array_index_name, &long_value)) { - key->array_index = long_value; - } else { - return -EINVAL; - } - } - LOG_INF("key: decoded:%u/%u/%u/%u", key->object_type, key->object_instance, - key->property_id, key->array_index); - - return 0; + const char *next; + size_t next_len; + char object_type_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char object_instance_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char property_id_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + char array_index_name[SETTINGS_MAX_DIR_DEPTH + 1] = { 0 }; + unsigned long long_value = 0; + const char base_name[] = CONFIG_BACNET_STORAGE_BASE_NAME; + + /* settings root name */ + if (settings_name_steq(path, base_name, &next) && next) { + /* OPTIONAL - called from shell */ + path = next; + } + /* object-type */ + next_len = settings_name_next(path, &next); + if (path) { + if (next_len + 1 > sizeof(object_type_name)) { + LOG_ERR("key: object-type name too long: %d", next_len); + return -EINVAL; + } + memcpy(object_type_name, path, next_len); + if (bacnet_storage_strtoul(object_type_name, &long_value)) { + key->object_type = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* object-instance */ + path = next; + next_len = settings_name_next(path, &next); + if (path) { + if (next_len + 1 > sizeof(object_instance_name)) { + LOG_ERR("key: object-instance name too long: %d", next_len); + return -EINVAL; + } + memcpy(object_instance_name, path, next_len); + if (bacnet_storage_strtoul(object_instance_name, &long_value)) { + key->object_instance = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* property-id */ + path = next; + next_len = settings_name_next(path, &next); + if (path) { + if (next_len + 1 > sizeof(property_id_name)) { + LOG_ERR("key: property-id name too long: %d", next_len); + return -EINVAL; + } + memcpy(property_id_name, path, next_len); + if (bacnet_storage_strtoul(property_id_name, &long_value)) { + key->property_id = long_value; + } else { + return -EINVAL; + } + } else { + return -EINVAL; + } + /* array-index - OPTIONAL */ + key->array_index = BACNET_STORAGE_ARRAY_INDEX_NONE; + path = next; + next_len = settings_name_next(path, &next); + if (path) { + if (next_len + 1 > sizeof(array_index_name)) { + LOG_ERR("key: array-index name too long: %d", next_len); + return -EINVAL; + } + memcpy(array_index_name, path, next_len); + if (bacnet_storage_strtoul(array_index_name, &long_value)) { + key->array_index = long_value; + } else { + return -EINVAL; + } + } + LOG_INF( + "key: decoded:%u/%u/%u/%u", key->object_type, key->object_instance, + key->property_id, key->array_index); + + return 0; } /** @@ -407,36 +488,36 @@ int bacnet_storage_key_decode(const char *path, BACNET_STORAGE_KEY *key) * @param cb_arg - callback argument * @return 0=success, negative on error */ -int bacnet_storage_handler_set(const char *path, size_t data_len, settings_read_cb read_cb, - void *cb_arg) +int bacnet_storage_handler_set( + const char *path, size_t data_len, settings_read_cb read_cb, void *cb_arg) { - int rc = -EINVAL; - BACNET_STORAGE_KEY key = {0}; - uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX] = {0}; - - if (bacnet_storage_key_decode(path, &key) == 0) { - /* get the data if there is any */ - if (data_len == 0) { - rc = 0; - } else { - rc = read_cb(cb_arg, &data, sizeof(data)); - if (rc < 0) { - /* On error returns -ERRNO code. */ - if (rc == -ENOENT) { - rc = 0; - } else { - LOG_ERR("Data restore error: %d", rc); - } - } else { - rc = bacnet_storage_restore(&key, data, data_len); - if (rc == 0) { - LOG_INF("Data restored:%s %d bytes", path, data_len); - } - } - } - } - - return rc; + int rc = -EINVAL; + BACNET_STORAGE_KEY key = { 0 }; + uint8_t data[BACNET_STORAGE_VALUE_SIZE_MAX] = { 0 }; + + if (bacnet_storage_key_decode(path, &key) == 0) { + /* get the data if there is any */ + if (data_len == 0) { + rc = 0; + } else { + rc = read_cb(cb_arg, &data, sizeof(data)); + if (rc < 0) { + /* On error returns -ERRNO code. */ + if (rc == -ENOENT) { + rc = 0; + } else { + LOG_ERR("Data restore error: %d", rc); + } + } else { + rc = bacnet_storage_restore(&key, data, data_len); + if (rc == 0) { + LOG_INF("Data restored:%s %d bytes", path, data_len); + } + } + } + } + + return rc; } /** @@ -445,8 +526,8 @@ int bacnet_storage_handler_set(const char *path, size_t data_len, settings_read_ */ int bacnet_storage_handler_commit(void) { - LOG_INF("Restored all settings"); - return 0; + LOG_INF("Restored all settings"); + return 0; } /** @@ -456,10 +537,11 @@ int bacnet_storage_handler_commit(void) * @param cb - callback function to receive the settings * @return 0=success, negative on error */ -int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, size_t val_len)) +int bacnet_storage_handler_export( + int (*cb)(const char *name, const void *value, size_t val_len)) { - LOG_INF("FIXME: Export requested"); - return 0; + LOG_INF("FIXME: Export requested"); + return 0; } /** @@ -469,31 +551,32 @@ int bacnet_storage_handler_export(int (*cb)(const char *name, const void *value, * @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) +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; + 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; + size_t value_size; + size_t value_len; + void *value; + bool fetched; }; /** @@ -505,32 +588,37 @@ struct direct_immediate_value { * @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) +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; + 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; } /** @@ -540,24 +628,26 @@ static int direct_loader_immediate_value(const char *name, size_t len, settings_ * @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) +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; + 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; } /** @@ -569,23 +659,23 @@ static int load_immediate_value(const char *name, void *value, size_t value_size */ 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; + 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; } /** @@ -595,15 +685,15 @@ int bacnet_storage_get(BACNET_STORAGE_KEY *key, void *data, size_t data_size) */ int bacnet_storage_delete(BACNET_STORAGE_KEY *key) { - char name[SETTINGS_MAX_NAME_LEN + 1] = {0}; - int rc; + char name[SETTINGS_MAX_NAME_LEN + 1] = { 0 }; + int rc; - rc = bacnet_storage_key_encode(name, sizeof(name), key); - LOG_INF("Delete a key-value pair. Key=%s", name); - rc = settings_delete(name); - if (rc) { - LOG_INF(FAIL_MSG, rc); - } + rc = bacnet_storage_key_encode(name, sizeof(name), key); + LOG_INF("Delete a key-value pair. Key=%s", name); + rc = settings_delete(name); + if (rc) { + LOG_INF(FAIL_MSG, rc); + } - return rc; + return rc; } diff --git a/zephyr/subsys/bacnet_settings/bacnet_storage_shell.c b/zephyr/subsys/bacnet_settings/bacnet_storage_shell.c new file mode 100644 index 0000000..fbdda2b --- /dev/null +++ b/zephyr/subsys/bacnet_settings/bacnet_storage_shell.c @@ -0,0 +1,180 @@ +/** + * @file + * @brief The BACnet shell commands for debugging and testing storage + * @author Steve Karg + * @date May 2024 + * @copyright SPDX-License-Identifier: Apache-2.0 + */ +#include +#include +#include +#include +#include + +static const char Storage_Base_Name[] = CONFIG_BACNET_STORAGE_BASE_NAME; + +/** + * @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 }, test_key = { 0 }; + size_t arg_len = 0; + int rc; + + rc = bacnet_storage_key_parse(&key, 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); + /* convert the key string to numbers for a test */ + if (bacnet_storage_key_decode(key_name, &test_key) == 0) { + shell_print( + sh, "key=%s/%lu/%lu/%lu/%lu", Storage_Base_Name, + (unsigned long)test_key.object_type, + (unsigned long)test_key.object_instance, + (unsigned long)test_key.property_id, + (unsigned long)test_key.array_index); + } + 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; +} + +/** + * @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_delete(const struct shell *sh, size_t argc, char **argv) +{ + char key_name[BACNET_STORAGE_KEY_SIZE_MAX + 1] = { 0 }; + BACNET_STORAGE_KEY key = { 0 }; + int rc; + + rc = bacnet_storage_key_parse(&key, 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 > 3) { + rc = bacnet_storage_delete(&key); + if (rc == 0) { + shell_print(sh, "Deleted %s", key_name); + } else { + shell_error(sh, "Unable to delete %s", key_name); + return -EINVAL; + } + } + + return 0; +} + +/** + * @brief Callback from the Zephyr settings_restore iterator + * @param key [in] The BACnet object type + * @param data [in] The data to restore + * @param data_len [in] The length of the data + * @return 0 on success, negative on failure. + */ +static int print_storage_data( + BACNET_STORAGE_KEY *key, const void *data, size_t data_len, void *context) +{ + char data_string[80] = { 0 }; + char hex_string[3] = { 0 }; + unsigned i; + const struct shell *sh = context; + + for (i = 0; i < data_len && i < ((sizeof(data_string) / 2) - 1); i++) { + snprintf( + hex_string, sizeof(hex_string), "%02X", ((const uint8_t *)data)[i]); + strcat(data_string, hex_string); + } + if (key->array_index == BACNET_STORAGE_ARRAY_INDEX_NONE) { + shell_print( + sh, "%s/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, data_string); + } else { + shell_print( + sh, "%s/%u/%u/%u/%u=%s", Storage_Base_Name, key->object_type, + key->object_instance, key->property_id, key->array_index, + data_string); + } + + return 0; +} + +/** + * @brief List all the BACnet stored settings + * @param sh Shell + * @param argc Number of arguments + * @param argv Argument list + * @return 0 on success, negative on failure + */ +static int cmd_list(const struct shell *sh, size_t argc, char **argv) +{ + int err; + + err = bacnet_storage_load_callback_set(print_storage_data, (void *)sh); + if (err) { + shell_error(sh, "Failed to set storage load callback"); + return -EINVAL; + } + err = bacnet_storage_load(); + if (err) { + shell_error(sh, "Failed to load storage"); + return -EINVAL; + } + + return 0; +} + +SHELL_STATIC_SUBCMD_SET_CREATE( + sub_bacnet_storage_cmds, + SHELL_CMD(list, NULL, "list BACnet storage strings", cmd_list), + SHELL_CMD( + string, + NULL, + " [array index] [string]", + cmd_string), + SHELL_CMD( + delete, + NULL, + " [array index]", + cmd_delete), + SHELL_SUBCMD_SET_END); + +SHELL_SUBCMD_ADD( + (bacnet), + storage, + &sub_bacnet_storage_cmds, + "BACnet storage commands", + NULL, + 1, + 0);