Skip to content

Add TimedAttempt #18

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/compile-examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
- examples/crc32
- examples/crc16
- examples/sha256
- examples/timedBlink
SKETCHES_REPORTS_PATH: sketches-reports

strategy:
Expand Down
29 changes: 29 additions & 0 deletions examples/timedBlink/timedBlink.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <Arduino_TimedAttempt.h>

#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32)
static int const LED_BUILTIN = 2;
#endif

TimedAttempt blink(500, 1000);

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
if(blink.isExpired()) {
blink.retry();
digitalWrite(LED_BUILTIN, blink.getRetryCount() % 2);
}

}
9 changes: 8 additions & 1 deletion extras/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,27 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

include_directories(../../src)
include_directories(src/arduino)

set(TEST_SRCS
src/lzss/test_decoder.cpp
src/crc32/test_crc32.cpp
src/crc16/test_crc16.cpp
src/sha256/test_sha256.cpp
src/hex/test_hex.cpp
src/time/test_TimedAttempt.cpp
)

set(TEST_DUT_SRCS
../../src/crc/crc32.cpp
../../src/crc/crc16.cpp
../../src/sha256/sha2.c
../../src/hex/chex.h
../../src/time/TimedAttempt.cpp
)

set(TEST_STUB_SRCS
src/arduino/Arduino.cpp
)

##########################################################################
Expand All @@ -45,7 +52,7 @@ add_compile_options(-Wno-cast-function-type)
set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} "--coverage")
set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "--coverage -Wno-deprecated-copy")

add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} )
add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} ${TEST_STUB_SRCS})
target_compile_definitions( ${TEST_TARGET} PUBLIC SOURCE_DIR="${CMAKE_SOURCE_DIR}" )

target_link_libraries( ${TEST_TARGET} Catch2WithMain )
21 changes: 21 additions & 0 deletions extras/test/src/arduino/Arduino.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <Arduino.h>

static unsigned long current_millis = 0;

void set_millis(unsigned long const millis) {
current_millis = millis;
}

unsigned long millis() {
return current_millis;
}
18 changes: 18 additions & 0 deletions extras/test/src/arduino/Arduino.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#pragma once

#ifndef min
#define min(a,b) ((a)<(b)?(a):(b))
#endif

void set_millis(unsigned long const millis);
unsigned long millis();
173 changes: 173 additions & 0 deletions extras/test/src/time/test_TimedAttempt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/

#include <catch2/catch_test_macros.hpp>

#include <time/TimedAttempt.h>
#include <Arduino.h>
#include <limits.h>

using namespace arduino::time;

SCENARIO("Test broker connection retries") {
TimedAttempt _connection_attempt(0,0);

_connection_attempt.begin(1000UL, 32000UL);

/* 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(2000UL, 32000UL);

/* 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(2000UL, 32000UL);

while(_attachAttempt.getRetryCount() < 100000) {
_attachAttempt.retry();

switch(_attachAttempt.getRetryCount()) {
case 1:
REQUIRE(_attachAttempt.getWaitTime() == 4000);
_attachAttempt.reconfigure(2000UL * 10UL, 32000UL * 40UL);
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(30000UL);

/* 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(1000UL, 32000UL);

/* 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);
}
14 changes: 14 additions & 0 deletions src/Arduino_TimedAttempt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#pragma once

#include "./time/TimedAttempt.h"

using namespace arduino::time;
78 changes: 78 additions & 0 deletions src/time/TimedAttempt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
This file is part of the Arduino_CloudUtils library.

Copyright (c) 2024 Arduino SA

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/


#include <Arduino.h>
#include "TimedAttempt.h"

namespace arduino { namespace time {

TimedAttempt::TimedAttempt(unsigned long minDelay, unsigned long maxDelay)
: _minDelay(minDelay)
, _maxDelay(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;
}

unsigned long TimedAttempt::reconfigure(unsigned long minDelay, unsigned long maxDelay) {
_minDelay = minDelay;
_maxDelay = maxDelay;
return reload();
}

unsigned long TimedAttempt::retry() {
_retryCount++;
return reload();
}

unsigned long TimedAttempt::reload() {
unsigned long shift = _retryCount > 31 ? 31 : _retryCount;
unsigned long delay = (1UL << shift) * _minDelay;
/* delay should always increase */
_retryDelay = (delay > _retryDelay) ? min(delay, _maxDelay): _maxDelay;
_retryTick = millis();
return _retryDelay;
}

void TimedAttempt::reset() {
_retryCount = 0;
}

bool TimedAttempt::isRetry() {
return _retryCount > 0;
}

bool TimedAttempt::isExpired() {
return millis() - _retryTick > _retryDelay;
}

unsigned int TimedAttempt::getRetryCount() {
return _retryCount;
}

unsigned int TimedAttempt::getWaitTime() {
return _retryDelay;
}

}} // arduino::time
Loading
Loading