Skip to content

Commit 1b95a46

Browse files
authored
Merge pull request #18 from pennam/timed-attempt
Add TimedAttempt
2 parents f8689d7 + 61690fd commit 1b95a46

File tree

9 files changed

+442
-1
lines changed

9 files changed

+442
-1
lines changed

.github/workflows/compile-examples.yml

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jobs:
2626
- examples/crc32
2727
- examples/crc16
2828
- examples/sha256
29+
- examples/timedBlink
2930
SKETCHES_REPORTS_PATH: sketches-reports
3031

3132
strategy:

examples/timedBlink/timedBlink.ino

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#include <Arduino_TimedAttempt.h>
12+
13+
#if !defined(LED_BUILTIN) && !defined(ARDUINO_NANO_ESP32)
14+
static int const LED_BUILTIN = 2;
15+
#endif
16+
17+
TimedAttempt blink(500, 1000);
18+
19+
void setup() {
20+
pinMode(LED_BUILTIN, OUTPUT);
21+
}
22+
23+
void loop() {
24+
if(blink.isExpired()) {
25+
blink.retry();
26+
digitalWrite(LED_BUILTIN, blink.getRetryCount() % 2);
27+
}
28+
29+
}

extras/test/CMakeLists.txt

+8-1
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,27 @@ set(CMAKE_CXX_STANDARD 11)
2020
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
2121

2222
include_directories(../../src)
23+
include_directories(src/arduino)
2324

2425
set(TEST_SRCS
2526
src/lzss/test_decoder.cpp
2627
src/crc32/test_crc32.cpp
2728
src/crc16/test_crc16.cpp
2829
src/sha256/test_sha256.cpp
2930
src/hex/test_hex.cpp
31+
src/time/test_TimedAttempt.cpp
3032
)
3133

3234
set(TEST_DUT_SRCS
3335
../../src/crc/crc32.cpp
3436
../../src/crc/crc16.cpp
3537
../../src/sha256/sha2.c
3638
../../src/hex/chex.h
39+
../../src/time/TimedAttempt.cpp
40+
)
41+
42+
set(TEST_STUB_SRCS
43+
src/arduino/Arduino.cpp
3744
)
3845

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

48-
add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} )
55+
add_executable( ${TEST_TARGET} ${TEST_SRCS} ${TEST_DUT_SRCS} ${TEST_STUB_SRCS})
4956
target_compile_definitions( ${TEST_TARGET} PUBLIC SOURCE_DIR="${CMAKE_SOURCE_DIR}" )
5057

5158
target_link_libraries( ${TEST_TARGET} Catch2WithMain )

extras/test/src/arduino/Arduino.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#include <Arduino.h>
12+
13+
static unsigned long current_millis = 0;
14+
15+
void set_millis(unsigned long const millis) {
16+
current_millis = millis;
17+
}
18+
19+
unsigned long millis() {
20+
return current_millis;
21+
}

extras/test/src/arduino/Arduino.h

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#pragma once
12+
13+
#ifndef min
14+
#define min(a,b) ((a)<(b)?(a):(b))
15+
#endif
16+
17+
void set_millis(unsigned long const millis);
18+
unsigned long millis();
+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
#include <catch2/catch_test_macros.hpp>
12+
13+
#include <time/TimedAttempt.h>
14+
#include <Arduino.h>
15+
#include <limits.h>
16+
17+
using namespace arduino::time;
18+
19+
SCENARIO("Test broker connection retries") {
20+
TimedAttempt _connection_attempt(0,0);
21+
22+
_connection_attempt.begin(1000UL, 32000UL);
23+
24+
/* 100000 retries are more or less 37 days without connection */
25+
while(_connection_attempt.getRetryCount() < 100000) {
26+
_connection_attempt.retry();
27+
28+
switch(_connection_attempt.getRetryCount()) {
29+
case 1:
30+
REQUIRE(_connection_attempt.getWaitTime() == 2000);
31+
break;
32+
case 2:
33+
REQUIRE(_connection_attempt.getWaitTime() == 4000);
34+
break;
35+
case 3:
36+
REQUIRE(_connection_attempt.getWaitTime() == 8000);
37+
break;
38+
case 4:
39+
REQUIRE(_connection_attempt.getWaitTime() == 16000);
40+
break;
41+
default:
42+
REQUIRE(_connection_attempt.getWaitTime() == 32000);
43+
break;
44+
}
45+
}
46+
}
47+
48+
SCENARIO("Test thing id request with no answer from the cloud") {
49+
TimedAttempt _attachAttempt(0,0);
50+
51+
_attachAttempt.begin(2000UL, 32000UL);
52+
53+
/* 100000 retries are more or less 37 days of requests */
54+
while(_attachAttempt.getRetryCount() < 100000) {
55+
_attachAttempt.retry();
56+
57+
switch(_attachAttempt.getRetryCount()) {
58+
case 1:
59+
REQUIRE(_attachAttempt.getWaitTime() == 4000);
60+
break;
61+
case 2:
62+
REQUIRE(_attachAttempt.getWaitTime() == 8000);
63+
break;
64+
case 3:
65+
REQUIRE(_attachAttempt.getWaitTime() == 16000);
66+
break;
67+
default:
68+
REQUIRE(_attachAttempt.getWaitTime() == 32000);
69+
break;
70+
}
71+
}
72+
}
73+
74+
SCENARIO("Test thing id request of a detached device") {
75+
TimedAttempt _attachAttempt(0,0);
76+
77+
_attachAttempt.begin(2000UL, 32000UL);
78+
79+
while(_attachAttempt.getRetryCount() < 100000) {
80+
_attachAttempt.retry();
81+
82+
switch(_attachAttempt.getRetryCount()) {
83+
case 1:
84+
REQUIRE(_attachAttempt.getWaitTime() == 4000);
85+
_attachAttempt.reconfigure(2000UL * 10UL, 32000UL * 40UL);
86+
break;
87+
case 2:
88+
REQUIRE(_attachAttempt.getWaitTime() == 80000);
89+
break;
90+
case 3:
91+
REQUIRE(_attachAttempt.getWaitTime() == 160000);
92+
break;
93+
case 4:
94+
REQUIRE(_attachAttempt.getWaitTime() == 320000);
95+
break;
96+
case 5:
97+
REQUIRE(_attachAttempt.getWaitTime() == 640000);
98+
break;
99+
default:
100+
REQUIRE(_attachAttempt.getWaitTime() == 1280000);
101+
break;
102+
}
103+
}
104+
}
105+
106+
SCENARIO("Test last value request") {
107+
TimedAttempt _syncAttempt(0,0);
108+
109+
_syncAttempt.begin(30000UL);
110+
111+
/* 100000 retries are more or less 37 days of requests */
112+
while(_syncAttempt.getRetryCount() < 100000) {
113+
_syncAttempt.retry();
114+
115+
switch(_syncAttempt.getRetryCount()) {
116+
default:
117+
REQUIRE(_syncAttempt.getWaitTime() == 30000);
118+
break;
119+
}
120+
}
121+
}
122+
123+
SCENARIO("Test isExpired() and isRetry()") {
124+
TimedAttempt attempt(0,0);
125+
126+
attempt.begin(1000UL, 32000UL);
127+
128+
/* Initial condition */
129+
set_millis(0);
130+
REQUIRE(attempt.isExpired() == false);
131+
REQUIRE(attempt.isRetry() == false);
132+
133+
/* Normal retry 2000ms */
134+
attempt.retry();
135+
REQUIRE(attempt.isRetry() == true);
136+
set_millis(1000);
137+
REQUIRE(attempt.isExpired() == false);
138+
set_millis(1999);
139+
REQUIRE(attempt.isExpired() == false);
140+
set_millis(2000);
141+
REQUIRE(attempt.isExpired() == false);
142+
set_millis(2001);
143+
REQUIRE(attempt.isExpired() == true);
144+
145+
/* Retry with rollover 4000ms */
146+
set_millis(ULONG_MAX - 1999);
147+
attempt.retry();
148+
REQUIRE(attempt.isRetry() == true);
149+
set_millis(0);
150+
REQUIRE(attempt.isExpired() == false);
151+
set_millis(1999);
152+
REQUIRE(attempt.isExpired() == false);
153+
set_millis(2000);
154+
REQUIRE(attempt.isExpired() == false);
155+
set_millis(2001);
156+
REQUIRE(attempt.isExpired() == true);
157+
158+
/* Normal retry 8000ms */
159+
set_millis(4000);
160+
attempt.retry();
161+
REQUIRE(attempt.isRetry() == true);
162+
set_millis(4000);
163+
REQUIRE(attempt.isExpired() == false);
164+
set_millis(11999);
165+
REQUIRE(attempt.isExpired() == false);
166+
set_millis(12000);
167+
REQUIRE(attempt.isExpired() == false);
168+
set_millis(12001);
169+
REQUIRE(attempt.isExpired() == true);
170+
171+
attempt.reset();
172+
REQUIRE(attempt.isRetry() == false);
173+
}

src/Arduino_TimedAttempt.h

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
#pragma once
11+
12+
#include "./time/TimedAttempt.h"
13+
14+
using namespace arduino::time;

src/time/TimedAttempt.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
This file is part of the Arduino_CloudUtils library.
3+
4+
Copyright (c) 2024 Arduino SA
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this
8+
file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
11+
12+
#include <Arduino.h>
13+
#include "TimedAttempt.h"
14+
15+
namespace arduino { namespace time {
16+
17+
TimedAttempt::TimedAttempt(unsigned long minDelay, unsigned long maxDelay)
18+
: _minDelay(minDelay)
19+
, _maxDelay(maxDelay) {
20+
}
21+
22+
void TimedAttempt::begin(unsigned long delay) {
23+
_retryCount = 0;
24+
_retryDelay = 0;
25+
_retryTick = 0;
26+
_minDelay = delay;
27+
_maxDelay = delay;
28+
}
29+
30+
void TimedAttempt::begin(unsigned long minDelay, unsigned long maxDelay) {
31+
_retryCount = 0;
32+
_retryDelay = 0;
33+
_retryTick = 0;
34+
_minDelay = minDelay;
35+
_maxDelay = maxDelay;
36+
}
37+
38+
unsigned long TimedAttempt::reconfigure(unsigned long minDelay, unsigned long maxDelay) {
39+
_minDelay = minDelay;
40+
_maxDelay = maxDelay;
41+
return reload();
42+
}
43+
44+
unsigned long TimedAttempt::retry() {
45+
_retryCount++;
46+
return reload();
47+
}
48+
49+
unsigned long TimedAttempt::reload() {
50+
unsigned long shift = _retryCount > 31 ? 31 : _retryCount;
51+
unsigned long delay = (1UL << shift) * _minDelay;
52+
/* delay should always increase */
53+
_retryDelay = (delay > _retryDelay) ? min(delay, _maxDelay): _maxDelay;
54+
_retryTick = millis();
55+
return _retryDelay;
56+
}
57+
58+
void TimedAttempt::reset() {
59+
_retryCount = 0;
60+
}
61+
62+
bool TimedAttempt::isRetry() {
63+
return _retryCount > 0;
64+
}
65+
66+
bool TimedAttempt::isExpired() {
67+
return millis() - _retryTick > _retryDelay;
68+
}
69+
70+
unsigned int TimedAttempt::getRetryCount() {
71+
return _retryCount;
72+
}
73+
74+
unsigned int TimedAttempt::getWaitTime() {
75+
return _retryDelay;
76+
}
77+
78+
}} // arduino::time

0 commit comments

Comments
 (0)