From 49febced854a7543cdc455f37213fc088599fd8c Mon Sep 17 00:00:00 2001 From: Daniel Adam Date: Wed, 27 Mar 2024 18:17:40 +0100 Subject: [PATCH] cloud: set/get function for timeouts of the default retry action --- api/cloud/oc_cloud_context.c | 9 - api/cloud/oc_cloud_manager.c | 110 +---------- api/cloud/oc_cloud_manager_internal.h | 3 - api/cloud/oc_cloud_schedule.c | 173 ++++++++++++++++++ api/cloud/oc_cloud_schedule_internal.h | 51 ++++++ api/cloud/unittest/cloud_manager_test.cpp | 6 +- port/esp32/main/CMakeLists.txt | 1 + port/windows/vs2015/IoTivity-lite.vcxproj | 11 +- .../vs2015/IoTivity-lite.vcxproj.filters | 38 +++- 9 files changed, 270 insertions(+), 132 deletions(-) create mode 100644 api/cloud/oc_cloud_schedule.c create mode 100644 api/cloud/oc_cloud_schedule_internal.h diff --git a/api/cloud/oc_cloud_context.c b/api/cloud/oc_cloud_context.c index d944c9185..5b1ff3ff5 100644 --- a/api/cloud/oc_cloud_context.c +++ b/api/cloud/oc_cloud_context.c @@ -330,15 +330,6 @@ oc_cloud_set_keepalive( ctx->keepalive.user_data = user_data; } -void -oc_cloud_set_schedule_action(oc_cloud_context_t *ctx, - oc_cloud_schedule_action_cb_t on_schedule_action, - void *user_data) -{ - ctx->schedule_action.on_schedule_action = on_schedule_action; - ctx->schedule_action.user_data = user_data; -} - oc_endpoint_address_t * oc_cloud_add_server_address(oc_cloud_context_t *ctx, const char *uri, size_t uri_len, oc_uuid_t sid) diff --git a/api/cloud/oc_cloud_manager.c b/api/cloud/oc_cloud_manager.c index fedf8ca86..5d134850c 100644 --- a/api/cloud/oc_cloud_manager.c +++ b/api/cloud/oc_cloud_manager.c @@ -29,6 +29,7 @@ #include "api/cloud/oc_cloud_log_internal.h" #include "api/cloud/oc_cloud_manager_internal.h" #include "api/cloud/oc_cloud_resource_internal.h" +#include "api/cloud/oc_cloud_schedule_internal.h" #include "api/cloud/oc_cloud_store_internal.h" #include "api/cloud/rd_client_internal.h" #include "api/oc_rep_internal.h" @@ -36,7 +37,6 @@ #include "oc_api.h" #include "oc_cloud_access.h" #include "oc_endpoint.h" -#include "port/oc_random.h" #include "util/oc_endpoint_address_internal.h" #include "util/oc_list.h" #include "util/oc_memb.h" @@ -49,10 +49,6 @@ #include #include -#define MILLISECONDS_PER_SECOND (1000) -#define MILLISECONDS_PER_MINUTE (60 * MILLISECONDS_PER_SECOND) -#define MILLISECONDS_PER_HOUR (60 * MILLISECONDS_PER_MINUTE) - static void cloud_start_process(oc_cloud_context_t *ctx); static oc_event_callback_retval_t cloud_manager_reconnect_async(void *data); static oc_event_callback_retval_t cloud_manager_register_async(void *data); @@ -60,30 +56,6 @@ static oc_event_callback_retval_t cloud_manager_login_async(void *data); static oc_event_callback_retval_t cloud_manager_refresh_token_async(void *data); static oc_event_callback_retval_t cloud_manager_send_ping_async(void *data); -#define OC_CLOUD_DEFAULT_RETRY_TIMEOUTS \ - { \ - 2 * MILLISECONDS_PER_SECOND, 4 * MILLISECONDS_PER_SECOND, \ - 8 * MILLISECONDS_PER_SECOND, 16 * MILLISECONDS_PER_SECOND, \ - 32 * MILLISECONDS_PER_SECOND, 64 * MILLISECONDS_PER_SECOND \ - } - -static uint16_t g_retry_timeout_ms[] = OC_CLOUD_DEFAULT_RETRY_TIMEOUTS; - -void -oc_cloud_manager_set_retry_timeouts(const uint16_t *timeouts, size_t size) -{ - if (timeouts == NULL) { - uint16_t def[] = OC_CLOUD_DEFAULT_RETRY_TIMEOUTS; - memcpy(g_retry_timeout_ms, def, sizeof(g_retry_timeout_ms)); - return; - } - - assert(size > 0); - assert(size <= OC_ARRAY_SIZE(g_retry_timeout_ms)); - memset(g_retry_timeout_ms, 0, sizeof(g_retry_timeout_ms)); - memcpy(g_retry_timeout_ms, timeouts, size * sizeof(uint16_t)); -} - static oc_event_callback_retval_t cloud_manager_callback_handler_async(void *data) { @@ -123,86 +95,6 @@ cloud_manager_reconnect_async(void *data) return OC_EVENT_DONE; } -static bool -cloud_retry_is_over(uint8_t retry_count) -{ - return retry_count >= OC_ARRAY_SIZE(g_retry_timeout_ms) || - g_retry_timeout_ms[retry_count] == 0; -} - -static bool -OC_NONNULL() - default_schedule_action(oc_cloud_context_t *ctx, uint8_t retry_count, - uint64_t *delay, uint16_t *timeout) -{ - if (cloud_retry_is_over(retry_count)) { - // we have made all attempts, try to select next server - OC_CLOUD_DBG("retry loop over, selecting next server"); - oc_endpoint_addresses_select_next(&ctx->store.ci_servers); - return false; - } - *timeout = (g_retry_timeout_ms[retry_count] / MILLISECONDS_PER_SECOND); - // for delay use timeout/2 value + random [0, timeout/2] - *delay = (uint64_t)(g_retry_timeout_ms[retry_count]) / 2; - // Include a random delay to prevent multiple devices from attempting to - // connect or make requests simultaneously. - *delay += oc_random_value() % *delay; - return true; -} - -static bool -on_action_response_set_retry(oc_cloud_context_t *ctx, oc_cloud_action_t action, - uint8_t retry_count, uint64_t *delay) -{ - bool ok = false; - if (ctx->schedule_action.on_schedule_action != NULL) { - ok = ctx->schedule_action.on_schedule_action( - action, retry_count, delay, &ctx->schedule_action.timeout, - ctx->schedule_action.user_data); - } else { - ok = default_schedule_action(ctx, retry_count, delay, - &ctx->schedule_action.timeout); - } - if (!ok) { - OC_CLOUD_DBG("for retry(%d), action(%s) is stopped", retry_count, - oc_cloud_action_to_str(action)); - return false; - } - OC_CLOUD_DBG( - "for retry(%d), action(%s) is delayed for %llu milliseconds with " - "and set with %u seconds timeout", - retry_count, oc_cloud_action_to_str(action), (long long unsigned)*delay, - ctx->schedule_action.timeout); - return true; -} - -static bool -cloud_schedule_action(oc_cloud_context_t *ctx, oc_cloud_action_t action, - oc_trigger_t callback, bool is_retry) -{ - uint64_t interval = 0; - uint8_t count = 0; - - if (action == OC_CLOUD_ACTION_REFRESH_TOKEN) { - if (is_retry) { - count = ++ctx->retry.refresh_token_count; - } else { - ctx->retry.refresh_token_count = 0; - } - } else { - if (is_retry) { - count = ++ctx->retry.count; - } else { - ctx->retry.count = 0; - } - } - if (!on_action_response_set_retry(ctx, action, count, &interval)) { - return false; - } - oc_reset_delayed_callback_ms(ctx, callback, interval); - return true; -} - static bool cloud_schedule_retry(oc_cloud_context_t *ctx, oc_cloud_action_t action, oc_trigger_t callback) diff --git a/api/cloud/oc_cloud_manager_internal.h b/api/cloud/oc_cloud_manager_internal.h index 50e31dee0..fd7dbf54f 100644 --- a/api/cloud/oc_cloud_manager_internal.h +++ b/api/cloud/oc_cloud_manager_internal.h @@ -36,9 +36,6 @@ extern "C" { #define USER_ID_KEY "uid" #define EXPIRESIN_KEY "expiresin" -/** Set timeout intervals for the default retry action */ -void oc_cloud_manager_set_retry_timeouts(const uint16_t *timeouts, size_t size); - /** * @brief Parse sign-up response retrieved from the server and store the data to * cloud context. diff --git a/api/cloud/oc_cloud_schedule.c b/api/cloud/oc_cloud_schedule.c new file mode 100644 index 000000000..f981c0fda --- /dev/null +++ b/api/cloud/oc_cloud_schedule.c @@ -0,0 +1,173 @@ +/**************************************************************************** + * + * Copyright (c) 2019 Intel Corporation + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#include "oc_config.h" + +#ifdef OC_CLOUD + +#include "api/cloud/oc_cloud_context_internal.h" +#include "api/cloud/oc_cloud_log_internal.h" +#include "api/cloud/oc_cloud_schedule_internal.h" +#include "api/oc_server_api_internal.h" +#include "oc_cloud.h" +#include "util/oc_endpoint_address_internal.h" +#include "util/oc_macros_internal.h" +#include "port/oc_random.h" + +#include + +#define OC_CLOUD_DEFAULT_RETRY_TIMEOUTS \ + { \ + 2 * MILLISECONDS_PER_SECOND, 4 * MILLISECONDS_PER_SECOND, \ + 8 * MILLISECONDS_PER_SECOND, 16 * MILLISECONDS_PER_SECOND, \ + 32 * MILLISECONDS_PER_SECOND, 64 * MILLISECONDS_PER_SECOND \ + } + +static uint16_t g_retry_timeout_ms[] = OC_CLOUD_DEFAULT_RETRY_TIMEOUTS; + +bool +oc_cloud_set_retry_timeouts(const uint16_t *timeouts, uint8_t size) +{ + if (timeouts == NULL) { + uint16_t def[] = OC_CLOUD_DEFAULT_RETRY_TIMEOUTS; + memcpy(g_retry_timeout_ms, def, sizeof(g_retry_timeout_ms)); + return true; + } + + if (size == 0 || size > OC_ARRAY_SIZE(g_retry_timeout_ms)) { + return false; + } + for (int i = 0; i < size; ++i) { + if (timeouts[i] == 0) { + return false; + } + } + + memset(g_retry_timeout_ms, 0, sizeof(g_retry_timeout_ms)); + memcpy(g_retry_timeout_ms, timeouts, size * sizeof(uint16_t)); + return true; +} + +int +oc_cloud_get_retry_timeouts(uint16_t *timeouts, uint8_t size) +{ + uint8_t count = 0; + for (uint8_t i = 0; i < OC_ARRAY_SIZE(g_retry_timeout_ms); ++i) { + if (g_retry_timeout_ms[i] == 0) { + count = i; + break; + } + } + if (size < count) { + return -1; + } + memcpy(timeouts, g_retry_timeout_ms, count * sizeof(uint16_t)); + return count; +} + +bool +cloud_retry_is_over(uint8_t retry_count) +{ + return retry_count >= OC_ARRAY_SIZE(g_retry_timeout_ms) || + g_retry_timeout_ms[retry_count] == 0; +} + +static bool +OC_NONNULL() + default_schedule_action(oc_cloud_context_t *ctx, uint8_t retry_count, + uint64_t *delay, uint16_t *timeout) +{ + if (cloud_retry_is_over(retry_count)) { + // we have made all attempts, try to select next server + OC_CLOUD_DBG("retry loop over, selecting next server"); + oc_endpoint_addresses_select_next(&ctx->store.ci_servers); + return false; + } + *timeout = (g_retry_timeout_ms[retry_count] / MILLISECONDS_PER_SECOND); + // for delay use timeout/2 value + random [0, timeout/2] + *delay = (uint64_t)(g_retry_timeout_ms[retry_count]) / 2; + // Include a random delay to prevent multiple devices from attempting to + // connect or make requests simultaneously. + *delay += oc_random_value() % *delay; + return true; +} + +static bool +on_action_response_set_retry(oc_cloud_context_t *ctx, oc_cloud_action_t action, + uint8_t retry_count, uint64_t *delay) +{ + bool ok = false; + if (ctx->schedule_action.on_schedule_action != NULL) { + ok = ctx->schedule_action.on_schedule_action( + action, retry_count, delay, &ctx->schedule_action.timeout, + ctx->schedule_action.user_data); + } else { + ok = default_schedule_action(ctx, retry_count, delay, + &ctx->schedule_action.timeout); + } + if (!ok) { + OC_CLOUD_DBG("for retry(%d), action(%s) is stopped", retry_count, + oc_cloud_action_to_str(action)); + return false; + } + OC_CLOUD_DBG( + "for retry(%d), action(%s) is delayed for %llu milliseconds with " + "and set with %u seconds timeout", + retry_count, oc_cloud_action_to_str(action), (long long unsigned)*delay, + ctx->schedule_action.timeout); + return true; +} + +bool +cloud_schedule_action(oc_cloud_context_t *ctx, oc_cloud_action_t action, + oc_trigger_t callback, bool is_retry) +{ + uint64_t interval = 0; + uint8_t count = 0; + + if (action == OC_CLOUD_ACTION_REFRESH_TOKEN) { + if (is_retry) { + count = ++ctx->retry.refresh_token_count; + } else { + ctx->retry.refresh_token_count = 0; + } + } else { + if (is_retry) { + count = ++ctx->retry.count; + } else { + ctx->retry.count = 0; + } + } + if (!on_action_response_set_retry(ctx, action, count, &interval)) { + return false; + } + oc_reset_delayed_callback_ms(ctx, callback, interval); + return true; +} + +void +oc_cloud_set_schedule_action(oc_cloud_context_t *ctx, + oc_cloud_schedule_action_cb_t on_schedule_action, + void *user_data) +{ + ctx->schedule_action.on_schedule_action = on_schedule_action; + ctx->schedule_action.user_data = user_data; +} + +#endif /* OC_CLOUD */ diff --git a/api/cloud/oc_cloud_schedule_internal.h b/api/cloud/oc_cloud_schedule_internal.h new file mode 100644 index 000000000..97fba761a --- /dev/null +++ b/api/cloud/oc_cloud_schedule_internal.h @@ -0,0 +1,51 @@ +/**************************************************************************** + * + * Copyright (c) 2024 plgd.dev s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"), + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + * + ****************************************************************************/ + +#ifndef OC_CLOUD_SCHEDULE_INTERNAL_H +#define OC_CLOUD_SCHEDULE_INTERNAL_H + +#include "oc_cloud.h" +#include "oc_ri.h" +#include "util/oc_compiler.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MILLISECONDS_PER_SECOND (1000) +#define MILLISECONDS_PER_MINUTE (60 * MILLISECONDS_PER_SECOND) +#define MILLISECONDS_PER_HOUR (60 * MILLISECONDS_PER_MINUTE) + +bool cloud_retry_is_over(uint8_t retry_count); + +/** Set timeout intervals for the default retry action */ +bool oc_cloud_set_retry_timeouts(const uint16_t *timeouts, uint8_t size); + +int oc_cloud_get_retry_timeouts(uint16_t *timeouts, uint8_t size) OC_NONNULL(); + +bool cloud_schedule_action(oc_cloud_context_t *ctx, oc_cloud_action_t action, + oc_trigger_t callback, bool is_retry) OC_NONNULL(); + +#ifdef __cplusplus +} +#endif + +#endif /* OC_CLOUD_SCHEDULE_INTERNAL_H */ diff --git a/api/cloud/unittest/cloud_manager_test.cpp b/api/cloud/unittest/cloud_manager_test.cpp index 2924f0386..ef3b0ebd5 100644 --- a/api/cloud/unittest/cloud_manager_test.cpp +++ b/api/cloud/unittest/cloud_manager_test.cpp @@ -62,7 +62,7 @@ class TestCloudManager : public testing::Test { // make the second timeout longer, so we can interrupt the retry std::chrono::duration_cast(5s).count(), }; - oc_cloud_manager_set_retry_timeouts(timeouts.data(), timeouts.size()); + oc_cloud_set_retry_timeouts(timeouts.data(), timeouts.size()); memset(&m_context, 0, sizeof(m_context)); m_context.cloud_ep = oc_new_endpoint(); @@ -89,7 +89,7 @@ class TestCloudManager : public testing::Test { void TearDown() override { oc_cloud_set_schedule_action(&m_context, nullptr, nullptr); - oc_cloud_manager_set_retry_timeouts(nullptr, 0); + oc_cloud_set_retry_timeouts(nullptr, 0); oc_free_endpoint(m_context.cloud_ep); oc_cloud_store_deinitialize(&m_context.store); @@ -341,7 +341,7 @@ TEST_F(TestCloudManager, cloud_manager_select_next_server_on_retry) // single try -> the cloud server endpoint should be changed after each try uint16_t timeout = std::chrono::duration_cast(kTimeout).count(); - oc_cloud_manager_set_retry_timeouts(&timeout, 1); + oc_cloud_set_retry_timeouts(&timeout, 1); oc_string_view_t uri = OC_STRING_VIEW("coap://13.3.7.187:5683"); oc_uuid_t sid; diff --git a/port/esp32/main/CMakeLists.txt b/port/esp32/main/CMakeLists.txt index 8a6a39742..69adee8db 100644 --- a/port/esp32/main/CMakeLists.txt +++ b/port/esp32/main/CMakeLists.txt @@ -136,6 +136,7 @@ if (CONFIG_CLOUD) ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud_manager.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud_rd.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud_resource.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud_schedule.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud_store.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/oc_cloud.c ${CMAKE_CURRENT_SOURCE_DIR}/../../../api/cloud/rd_client.c diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj b/port/windows/vs2015/IoTivity-lite.vcxproj index 1443878b0..679ee6a71 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj +++ b/port/windows/vs2015/IoTivity-lite.vcxproj @@ -213,8 +213,16 @@ + + + - + + + + + + @@ -339,6 +347,7 @@ + diff --git a/port/windows/vs2015/IoTivity-lite.vcxproj.filters b/port/windows/vs2015/IoTivity-lite.vcxproj.filters index c66217321..e1498a000 100644 --- a/port/windows/vs2015/IoTivity-lite.vcxproj.filters +++ b/port/windows/vs2015/IoTivity-lite.vcxproj.filters @@ -440,6 +440,9 @@ Core\cloud + + Core\cloud + Core\cloud @@ -479,6 +482,9 @@ Core + + Core + Core @@ -766,10 +772,34 @@ Headers + + Core\cloud + + + Core\cloud + + + Core\cloud + Core\cloud - + + Core\cloud + + + Core\cloud + + + Core\cloud + + + Core\cloud + + + Core\cloud + + Core\cloud @@ -814,12 +844,6 @@ Port - - Core - - - Core - Port