Skip to content

Commit d45d0ee

Browse files
committed
First phase of conversion of library to modern C++
1 parent 721bfa2 commit d45d0ee

File tree

1 file changed

+205
-154
lines changed

1 file changed

+205
-154
lines changed

src/arduino-timer.h

Lines changed: 205 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -32,171 +32,222 @@
3232
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3333
*/
3434

35-
#ifndef _CM_ARDUINO_TIMER_H__
36-
#define _CM_ARDUINO_TIMER_H__
35+
#ifndef _KPF_ARDUINO_TIMER_H__
36+
#define _KPF_ARDUINO_TIMER_H__
3737

38-
#if defined(ARDUINO) && ARDUINO >= 100
3938
#include <Arduino.h>
40-
#else
41-
#include <WProgram.h>
42-
#endif
39+
40+
#undef max
41+
#undef min
42+
43+
#include <algorithm>
44+
#include <array>
45+
#include <functional>
46+
#include <limits>
47+
#include <optional>
48+
#include <tuple>
4349

4450
#ifndef TIMER_MAX_TASKS
4551
#define TIMER_MAX_TASKS 0x10
4652
#endif
4753

54+
#ifndef TIMERSET_DEFAULT_TIMERS
55+
#define TIMERSET_DEFAULT_TIMERS 0x10
56+
#endif
57+
58+
namespace Timers {
59+
60+
typedef unsigned long Timepoint;
61+
62+
enum class TimerStatus
63+
{
64+
complete,
65+
repeat,
66+
reschedule
67+
};
68+
69+
typedef std::tuple<TimerStatus, Timepoint> HandlerResult;
70+
71+
typedef std::function<HandlerResult (void)> Handler;
72+
73+
struct Timer {
74+
Handler handler;
75+
Timepoint start; // when timer was added (or repeat execution began)
76+
Timepoint expires; // when the timer expires
77+
Timepoint repeat; // default repeat interval
78+
79+
// ensure that these objects will never be copied or moved
80+
// (this could only happen by accident)
81+
Timer() = default;
82+
Timer(const Timer&) = delete;
83+
Timer& operator=(const Timer&) = delete;
84+
};
85+
86+
typedef std::optional<std::reference_wrapper<Timer>> TimerHandle;
87+
4888
template <
49-
size_t max_tasks = TIMER_MAX_TASKS, /* max allocated tasks */
50-
unsigned long (*time_func)() = millis, /* time function for timer */
51-
typename T = void * /* handler argument type */
52-
>
53-
class Timer {
54-
public:
55-
56-
typedef uintptr_t Task; /* public task handle */
57-
typedef bool (*handler_t)(T opaque); /* task handler func signature */
58-
59-
/* Calls handler with opaque as argument in delay units of time */
60-
Task
61-
in(unsigned long delay, handler_t h, T opaque = T())
62-
{
63-
return task_id(add_task(time_func(), delay, h, opaque));
64-
}
65-
66-
/* Calls handler with opaque as argument at time */
67-
Task
68-
at(unsigned long time, handler_t h, T opaque = T())
69-
{
70-
const unsigned long now = time_func();
71-
return task_id(add_task(now, time - now, h, opaque));
72-
}
73-
74-
/* Calls handler with opaque as argument every interval units of time */
75-
Task
76-
every(unsigned long interval, handler_t h, T opaque = T())
77-
{
78-
return task_id(add_task(time_func(), interval, h, opaque, interval));
79-
}
80-
81-
/* Cancel the timer task */
82-
void
83-
cancel(Task &task)
84-
{
85-
if (!task) return;
86-
87-
for (size_t i = 0; i < max_tasks; ++i) {
88-
struct task * const t = &tasks[i];
89-
90-
if (t->handler && (t->id ^ task) == (uintptr_t)t) {
91-
remove(t);
92-
break;
93-
}
94-
}
95-
96-
task = (Task)NULL;
97-
}
98-
99-
/* Ticks the timer forward - call this function in loop() */
100-
unsigned long
101-
tick()
102-
{
103-
unsigned long ticks = (unsigned long)-1;
104-
105-
for (size_t i = 0; i < max_tasks; ++i) {
106-
struct task * const task = &tasks[i];
107-
108-
if (task->handler) {
109-
const unsigned long t = time_func();
110-
const unsigned long duration = t - task->start;
111-
112-
if (duration >= task->expires) {
113-
task->repeat = task->handler(task->opaque) && task->repeat;
114-
115-
if (task->repeat) task->start = t;
116-
else remove(task);
117-
} else {
118-
const unsigned long remaining = task->expires - duration;
119-
ticks = remaining < ticks ? remaining : ticks;
120-
}
121-
}
122-
}
123-
124-
return ticks == (unsigned long)-1 ? 0 : ticks;
125-
}
126-
127-
private:
128-
129-
size_t ctr;
130-
131-
struct task {
132-
handler_t handler; /* task handler callback func */
133-
T opaque; /* argument given to the callback handler */
134-
unsigned long start,
135-
expires; /* when the task expires */
136-
size_t repeat, /* repeat task */
137-
id;
138-
} tasks[max_tasks];
139-
140-
inline
141-
void
142-
remove(struct task *task)
143-
{
144-
task->handler = NULL;
145-
task->opaque = T();
146-
task->start = 0;
147-
task->expires = 0;
148-
task->repeat = 0;
149-
task->id = 0;
150-
}
151-
152-
inline
153-
Task
154-
task_id(const struct task * const t)
155-
{
156-
const Task id = (Task)t;
157-
158-
return id ? id ^ t->id : id;
159-
}
160-
161-
inline
162-
struct task *
163-
next_task_slot()
164-
{
165-
for (size_t i = 0; i < max_tasks; ++i) {
166-
struct task * const slot = &tasks[i];
167-
if (slot->handler == NULL) return slot;
168-
}
169-
170-
return NULL;
171-
}
172-
173-
inline
174-
struct task *
175-
add_task(unsigned long start, unsigned long expires,
176-
handler_t h, T opaque, bool repeat = 0)
177-
{
178-
struct task * const slot = next_task_slot();
179-
180-
if (!slot) return NULL;
181-
182-
if (++ctr == 0) ++ctr; // overflow
183-
184-
slot->id = ctr;
185-
slot->handler = h;
186-
slot->opaque = opaque;
187-
slot->start = start;
188-
slot->expires = expires;
189-
slot->repeat = repeat;
190-
191-
return slot;
192-
}
89+
size_t max_timers = TIMERSET_DEFAULT_TIMERS, // max number of timers
90+
Timepoint (*time_func)() = millis, // time function for timer
91+
void (*delay_func)(Timepoint) = delay // delay function corresponding to time_func
92+
>
93+
class TimerSet {
94+
std::array<Timer, max_timers> timers;
95+
96+
inline
97+
void
98+
remove(TimerHandle handle)
99+
{
100+
if (!handle) {
101+
return;
102+
}
103+
104+
auto& timer = handle.value().get();
105+
106+
timer.handler = Handler();
107+
timer.start = 0;
108+
timer.expires = 0;
109+
timer.repeat = 0;
110+
}
111+
112+
inline
113+
auto
114+
next_timer_slot()
115+
{
116+
return std::find_if(timers.begin(), timers.end(), [](Timer& t){ return !t.handler; });
117+
}
118+
119+
inline
120+
TimerHandle
121+
add_timer(Timepoint start, Timepoint expires, Handler h, Timepoint repeat = 0)
122+
{
123+
if (auto timer = next_timer_slot(); timer != timers.end()) {
124+
timer->handler = h;
125+
timer->start = start;
126+
timer->expires = expires;
127+
timer->repeat = repeat;
128+
129+
return TimerHandle(*timer);
130+
}
131+
else {
132+
return TimerHandle();
133+
}
134+
}
135+
136+
public:
137+
// Calls handler in delay units of time
138+
TimerHandle
139+
in(Timepoint delay, Handler h)
140+
{
141+
return add_timer(time_func(), delay, h);
142+
}
143+
144+
// Calls handler at time
145+
TimerHandle
146+
at(Timepoint time, Handler h)
147+
{
148+
const Timepoint now = time_func();
149+
return add_timer(now, time - now, h);
150+
}
151+
152+
// Calls handler every interval units of time
153+
TimerHandle
154+
every(Timepoint interval, Handler h)
155+
{
156+
return add_timer(time_func(), interval, h, interval);
157+
}
158+
159+
// Calls handler immediately and every interval units of time
160+
TimerHandle
161+
now_and_every(Timepoint interval, Handler h)
162+
{
163+
const Timepoint now = time_func();
164+
return add_timer(now, now, h, interval);
165+
}
166+
167+
// Cancels timer
168+
void
169+
cancel(TimerHandle handle)
170+
{
171+
if (!handle) {
172+
return;
173+
}
174+
175+
auto timer = handle.value().get();
176+
177+
if (!timer.handler) {
178+
return;
179+
}
180+
181+
remove(timer);
182+
}
183+
184+
// Ticks the timerset forward - call this function in loop()
185+
// returns Timepoint of next timer expiration */
186+
Timepoint
187+
tick()
188+
{
189+
Timepoint next_expiration = std::numeric_limits<Timepoint>::max();
190+
191+
// execute handlers for any timers which have expired
192+
for (auto& timer: timers) {
193+
if (!timer.handler) {
194+
continue;
195+
}
196+
197+
Timepoint now = time_func();
198+
Timepoint elapsed = now - timer.start;
199+
200+
if (elapsed >= timer.expires) {
201+
auto [ status, next ] = timer.handler();
202+
203+
switch (status) {
204+
case TimerStatus::complete:
205+
remove(timer);
206+
break;
207+
case TimerStatus::repeat:
208+
timer.start = now;
209+
timer.expires = timer.repeat;
210+
break;
211+
case TimerStatus::reschedule:
212+
timer.start = now;
213+
timer.expires = next;
214+
break;
215+
}
216+
}
217+
}
218+
219+
// compute lowest remaining time after all handlers have been executed
220+
// (some timers may have expired during handler execution)
221+
const Timepoint now = time_func();
222+
223+
for (auto& timer: timers) {
224+
if (!timer.handler) {
225+
continue;
226+
}
227+
228+
Timepoint remaining = timer.expires - (now - timer.start);
229+
next_expiration = remaining < next_expiration ? remaining : next_expiration;
230+
}
231+
232+
return next_expiration == std::numeric_limits<Timepoint>::max() ? 0 : next_expiration;
233+
}
234+
235+
// Ticks the timerset forward, then delays until next timer is due
236+
void
237+
tick_and_delay()
238+
{
239+
delay_func(tick());
240+
}
193241
};
194242

195-
/* create a timer with the default settings */
196-
inline Timer<>
197-
timer_create_default()
243+
244+
// create TimerSet with default settings
245+
inline TimerSet<>
246+
create_default()
198247
{
199-
return Timer<>();
248+
return TimerSet<>();
200249
}
201250

251+
};
252+
202253
#endif

0 commit comments

Comments
 (0)