diff --git a/extras/test/CMakeLists.txt b/extras/test/CMakeLists.txt index 2aab9ab9..527fd755 100644 --- a/extras/test/CMakeLists.txt +++ b/extras/test/CMakeLists.txt @@ -45,6 +45,7 @@ set(TEST_SRCS src/test_writeOnly.cpp src/test_writeOnDemand.cpp src/test_writeOnChange.cpp + src/test_TimedAttempt.cpp ) set(TEST_UTIL_SRCS @@ -53,6 +54,7 @@ set(TEST_UTIL_SRCS ) set(TEST_DUT_SRCS + ../../src/utility/time/TimedAttempt.cpp ../../src/property/Property.cpp ../../src/property/PropertyContainer.cpp ../../src/cbor/CBORDecoder.cpp @@ -84,7 +86,7 @@ set(TEST_TARGET_SRCS ########################################################################## -add_compile_definitions(HOST) +add_compile_definitions(HOST HAS_TCP) add_compile_options(-Wall -Wextra -Wpedantic -Werror) add_compile_options(-Wno-cast-function-type) diff --git a/extras/test/include/Arduino.h b/extras/test/include/Arduino.h index 6e66167a..9e743f96 100644 --- a/extras/test/include/Arduino.h +++ b/extras/test/include/Arduino.h @@ -11,6 +11,13 @@ #include +/****************************************************************************** + DEFINES + ******************************************************************************/ +#ifndef min + #define min(a,b) ((a)<(b)?(a):(b)) +#endif + /****************************************************************************** TYPEDEF ******************************************************************************/ diff --git a/extras/test/src/test_TimedAttempt.cpp b/extras/test/src/test_TimedAttempt.cpp new file mode 100644 index 00000000..d1490a13 --- /dev/null +++ b/extras/test/src/test_TimedAttempt.cpp @@ -0,0 +1,186 @@ +/* + Copyright (c) 2024 Arduino. All rights reserved. +*/ + +/****************************************************************************** + INCLUDE + ******************************************************************************/ + +#include + +#include +#include +#include +#include + +/****************************************************************************** + TEST CODE + ******************************************************************************/ + +SCENARIO("Test broker connection retries") +{ + TimedAttempt _connection_attempt(0,0); + + _connection_attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); + + /* 100000 retries are more or less 37 days without connection */ + while(_connection_attempt.getRetryCount() < 100000) { + _connection_attempt.retry(); + + switch(_connection_attempt.getRetryCount()) { + case 1: + REQUIRE(_connection_attempt.getWaitTime() == 2000); + break; + case 2: + REQUIRE(_connection_attempt.getWaitTime() == 4000); + break; + case 3: + REQUIRE(_connection_attempt.getWaitTime() == 8000); + break; + case 4: + REQUIRE(_connection_attempt.getWaitTime() == 16000); + break; + default: + REQUIRE(_connection_attempt.getWaitTime() == 32000); + break; + } + } +} + +SCENARIO("Test thing id request with no answer from the cloud") +{ + TimedAttempt _attachAttempt(0,0); + + _attachAttempt.begin(AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms); + + /* 100000 retries are more or less 37 days of requests */ + while(_attachAttempt.getRetryCount() < 100000) { + _attachAttempt.retry(); + + switch(_attachAttempt.getRetryCount()) { + case 1: + REQUIRE(_attachAttempt.getWaitTime() == 4000); + break; + case 2: + REQUIRE(_attachAttempt.getWaitTime() == 8000); + break; + case 3: + REQUIRE(_attachAttempt.getWaitTime() == 16000); + break; + default: + REQUIRE(_attachAttempt.getWaitTime() == 32000); + break; + } + } +} + +SCENARIO("Test thing id request of a detached device") +{ + TimedAttempt _attachAttempt(0,0); + + _attachAttempt.begin(AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms); + + while(_attachAttempt.getRetryCount() < 100000) { + _attachAttempt.retry(); + + switch(_attachAttempt.getRetryCount()) { + case 1: + REQUIRE(_attachAttempt.getWaitTime() == 4000); + _attachAttempt.reconfigure(AIOT_CONFIG_THING_ID_REQUEST_RETRY_DELAY_ms * + AIOT_CONFIG_DEVICE_REGISTERED_RETRY_DELAY_k, + AIOT_CONFIG_MAX_THING_ID_REQUEST_RETRY_DELAY_ms * + AIOT_CONFIG_MAX_DEVICE_REGISTERED_RETRY_DELAY_k); + break; + case 2: + REQUIRE(_attachAttempt.getWaitTime() == 80000); + break; + case 3: + REQUIRE(_attachAttempt.getWaitTime() == 160000); + break; + case 4: + REQUIRE(_attachAttempt.getWaitTime() == 320000); + break; + case 5: + REQUIRE(_attachAttempt.getWaitTime() == 640000); + break; + default: + REQUIRE(_attachAttempt.getWaitTime() == 1280000); + break; + } + } +} + +SCENARIO("Test last value request") +{ + TimedAttempt _syncAttempt(0,0); + + _syncAttempt.begin(AIOT_CONFIG_TIMEOUT_FOR_LASTVALUES_SYNC_ms); + + /* 100000 retries are more or less 37 days of requests */ + while(_syncAttempt.getRetryCount() < 100000) { + _syncAttempt.retry(); + + switch(_syncAttempt.getRetryCount()) { + default: + REQUIRE(_syncAttempt.getWaitTime() == 30000); + break; + } + } +} + +SCENARIO("Test isExpired() and isRetry()") +{ + TimedAttempt attempt(0,0); + + attempt.begin(AIOT_CONFIG_RECONNECTION_RETRY_DELAY_ms, + AIOT_CONFIG_MAX_RECONNECTION_RETRY_DELAY_ms); + + /* Initial condition */ + set_millis(0); + REQUIRE(attempt.isExpired() == false); + REQUIRE(attempt.isRetry() == false); + + /* Normal retry 2000ms */ + attempt.retry(); + REQUIRE(attempt.isRetry() == true); + set_millis(1000); + REQUIRE(attempt.isExpired() == false); + set_millis(1999); + REQUIRE(attempt.isExpired() == false); + set_millis(2000); + REQUIRE(attempt.isExpired() == false); + set_millis(2001); + REQUIRE(attempt.isExpired() == true); + + /* Retry with rollover 4000ms */ + set_millis(ULONG_MAX - 1999); + attempt.retry(); + REQUIRE(attempt.isRetry() == true); + set_millis(0); + REQUIRE(attempt.isExpired() == false); + set_millis(1999); + REQUIRE(attempt.isExpired() == false); + set_millis(2000); + REQUIRE(attempt.isExpired() == false); + set_millis(2001); + REQUIRE(attempt.isExpired() == true); + + /* Normal retry 8000ms */ + set_millis(4000); + attempt.retry(); + REQUIRE(attempt.isRetry() == true); + set_millis(4000); + REQUIRE(attempt.isExpired() == false); + set_millis(11999); + REQUIRE(attempt.isExpired() == false); + set_millis(12000); + REQUIRE(attempt.isExpired() == false); + set_millis(12001); + REQUIRE(attempt.isExpired() == true); + + attempt.reset(); + REQUIRE(attempt.isRetry() == false); +} diff --git a/src/AIoTC_Config.h b/src/AIoTC_Config.h index 14137ae1..2efb7021 100644 --- a/src/AIoTC_Config.h +++ b/src/AIoTC_Config.h @@ -155,7 +155,7 @@ #define AIOT_CONFIG_THING_ID_REQUEST_MAX_RETRY_CNT (10UL) #define AIOT_CONFIG_DEVICE_REGISTERED_RETRY_DELAY_k (10UL) - #define AIOT_CONFIG_MAX_DEVICE_REGISTERED_RETRY_DELAY_k (4UL) + #define AIOT_CONFIG_MAX_DEVICE_REGISTERED_RETRY_DELAY_k (40UL) #define AIOT_CONFIG_TIMEOUT_FOR_LASTVALUES_SYNC_ms (30000UL) #define AIOT_CONFIG_LASTVALUES_SYNC_MAX_RETRY_CNT (10UL) diff --git a/src/utility/time/TimedAttempt.cpp b/src/utility/time/TimedAttempt.cpp index 4ff027fd..70b6d025 100644 --- a/src/utility/time/TimedAttempt.cpp +++ b/src/utility/time/TimedAttempt.cpp @@ -30,12 +30,16 @@ TimedAttempt::TimedAttempt(unsigned long minDelay, unsigned long maxDelay) void TimedAttempt::begin(unsigned long delay) { _retryCount = 0; + _retryDelay = 0; + _retryTick = 0; _minDelay = delay; _maxDelay = delay; } void TimedAttempt::begin(unsigned long minDelay, unsigned long maxDelay) { _retryCount = 0; + _retryDelay = 0; + _retryTick = 0; _minDelay = minDelay; _maxDelay = maxDelay; } @@ -52,9 +56,10 @@ unsigned long TimedAttempt::retry() { } unsigned long TimedAttempt::reload() { - unsigned long retryDelay = (1 << _retryCount) * _minDelay; - _retryDelay = min(retryDelay, _maxDelay); - _nextRetryTick = millis() + _retryDelay; + unsigned long shift = _retryCount > 31 ? 31 : _retryCount; + unsigned long delay = (1UL << shift) * _minDelay; + _retryDelay = min(delay, _maxDelay); + _retryTick = millis(); return _retryDelay; } @@ -67,7 +72,7 @@ bool TimedAttempt::isRetry() { } bool TimedAttempt::isExpired() { - return millis() > _nextRetryTick; + return millis() - _retryTick > _retryDelay; } unsigned int TimedAttempt::getRetryCount() { diff --git a/src/utility/time/TimedAttempt.h b/src/utility/time/TimedAttempt.h index 67a1931c..9a95e310 100644 --- a/src/utility/time/TimedAttempt.h +++ b/src/utility/time/TimedAttempt.h @@ -34,7 +34,7 @@ class TimedAttempt { private: unsigned long _minDelay; unsigned long _maxDelay; - unsigned long _nextRetryTick; + unsigned long _retryTick; unsigned long _retryDelay; unsigned int _retryCount; };