cpptick: add timers
This commit is contained in:
parent
80a4f39f7b
commit
29ffd478ef
@ -1,10 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||
|
||||
add_library(cpptick INTERFACE)
|
||||
add_library(cpptick STATIC
|
||||
Src/timer.cpp
|
||||
)
|
||||
|
||||
add_library(skullc::cpptick ALIAS cpptick)
|
||||
|
||||
target_include_directories(cpptick
|
||||
INTERFACE
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
@ -16,7 +19,7 @@ set_target_properties(cpptick
|
||||
)
|
||||
|
||||
target_link_libraries(cpptick
|
||||
INTERFACE
|
||||
PUBLIC
|
||||
skullc::utility
|
||||
)
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include <type_traits>
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
@ -5,5 +5,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
#include "cpptick/timer.hpp"
|
||||
#include "cpptick/scheduler.hpp"
|
||||
#include "cpptick/slot.hpp"
|
||||
|
||||
@ -5,21 +5,40 @@
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
#include <array>
|
||||
#include <algorithm>
|
||||
#include <utility_function.hpp>
|
||||
#include <utility_ringbuffer.hpp>
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
#include "cpptick/timer.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
struct Scheduler
|
||||
struct BaseScheduler
|
||||
{
|
||||
using StoredCall = std::pair<Utility::IFunction<void (ArgStorage&)>*, ArgStorage>;
|
||||
Utility::Ringbuffer<StoredCall, 12> stored_calls;
|
||||
|
||||
using StoredTimer = std::pair<std::uint32_t, Timer*>;
|
||||
std::array<StoredTimer, 12> stored_timers;
|
||||
|
||||
BaseScheduler()
|
||||
{
|
||||
stored_timers.fill({-1u, nullptr});
|
||||
}
|
||||
|
||||
virtual ~BaseScheduler() = default;
|
||||
|
||||
void tick()
|
||||
{
|
||||
if (auto start = stored_timers.begin();
|
||||
start->first != -1u)
|
||||
{
|
||||
checkInvokeTimers();
|
||||
}
|
||||
|
||||
if (!stored_calls.empty())
|
||||
{
|
||||
auto* f = stored_calls.begin()->first;
|
||||
@ -39,6 +58,66 @@ struct Scheduler
|
||||
storage.reset();
|
||||
return storage;
|
||||
}
|
||||
|
||||
virtual void storeTimer(Timer* timer) = 0;
|
||||
virtual void checkInvokeTimers() = 0;
|
||||
};
|
||||
|
||||
template<typename HAL>
|
||||
struct Scheduler : BaseScheduler
|
||||
{
|
||||
Scheduler() = default;
|
||||
|
||||
void storeTimer(Timer* timer) override
|
||||
{
|
||||
const std::uint32_t current_time = HAL::getMillis();
|
||||
const std::uint32_t time_to_run = timer->expirationTime(current_time);
|
||||
|
||||
auto empty_slot = std::find_if(stored_timers.begin(), stored_timers.end(),
|
||||
[](const StoredTimer& timer) -> bool {
|
||||
return timer.first == -1u;
|
||||
});
|
||||
|
||||
if (empty_slot == stored_timers.end())
|
||||
return;
|
||||
|
||||
*empty_slot = { time_to_run, timer };
|
||||
std::sort(stored_timers.begin(), stored_timers.end());
|
||||
}
|
||||
|
||||
void checkInvokeTimers() override
|
||||
{
|
||||
const std::uint32_t current_time = HAL::getMillis();
|
||||
|
||||
bool timers_executed = false;
|
||||
for (auto timer = stored_timers.begin(); timer->first != -1u; timer++)
|
||||
{
|
||||
if (timer->first <= current_time)
|
||||
{
|
||||
timers_executed = true;
|
||||
storeCall(timer->second->getSlot());
|
||||
|
||||
if (timer->second->is_one_shot)
|
||||
{
|
||||
timer->first = -1u;
|
||||
timer->second = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
timer->first = timer->second->expirationTime(current_time);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (timers_executed)
|
||||
{
|
||||
std::sort(stored_timers.begin(), stored_timers.end());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -24,12 +24,12 @@ template<typename R, SlotArgument... Args>
|
||||
struct Slot<R(Args...)>
|
||||
{
|
||||
std::array<Utility::IFunction<R (Args...)>*, 12> signals;
|
||||
Scheduler* scheduler;
|
||||
BaseScheduler* scheduler;
|
||||
|
||||
Utility::FunctionOwned<Slot<R(Args...)>, void (ArgStorage&)> invoke_ptr;
|
||||
|
||||
Slot() = delete;
|
||||
explicit Slot(Scheduler* sched)
|
||||
explicit Slot(BaseScheduler* sched)
|
||||
: scheduler(sched)
|
||||
, invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
||||
{
|
||||
|
||||
63
CppTick/Inc/cpptick/timer.hpp
Normal file
63
CppTick/Inc/cpptick/timer.hpp
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <utility_function.hpp>
|
||||
|
||||
#include "cpptick/argstore.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
struct BaseScheduler;
|
||||
|
||||
struct Timer
|
||||
{
|
||||
Timer(BaseScheduler* scheduler, const std::uint32_t timeout_ms, const bool one_shot = false)
|
||||
: scheduler_(scheduler)
|
||||
, period_ms(timeout_ms)
|
||||
, is_one_shot(one_shot)
|
||||
{ }
|
||||
|
||||
Timer() = delete;
|
||||
Timer(const Timer&) = delete;
|
||||
Timer(Timer&&) = delete;
|
||||
Timer& operator=(const Timer&) = delete;
|
||||
Timer& operator=(Timer&&) = delete;
|
||||
|
||||
void start();
|
||||
|
||||
template<typename T>
|
||||
void setSlot(T& slot)
|
||||
{
|
||||
setSlot(&slot.invoke_ptr);
|
||||
}
|
||||
|
||||
void setSlot(Utility::IFunction<void (ArgStorage&)>* slot)
|
||||
{
|
||||
slot_ = slot;
|
||||
}
|
||||
|
||||
Utility::IFunction<void (ArgStorage&)>* getSlot()
|
||||
{
|
||||
return slot_;
|
||||
}
|
||||
|
||||
std::uint32_t expirationTime(const std::uint32_t current_time) const
|
||||
{
|
||||
return period_ms + current_time;
|
||||
}
|
||||
|
||||
std::uint32_t period_ms;
|
||||
bool is_one_shot;
|
||||
private:
|
||||
BaseScheduler* scheduler_;
|
||||
Utility::IFunction<void (ArgStorage&)>* slot_ = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
17
CppTick/Src/timer.cpp
Normal file
17
CppTick/Src/timer.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by erki on 24/10/23.
|
||||
//
|
||||
|
||||
#include "cpptick/timer.hpp"
|
||||
|
||||
#include "cpptick/scheduler.hpp"
|
||||
|
||||
namespace cpptick
|
||||
{
|
||||
|
||||
void Timer::start()
|
||||
{
|
||||
scheduler_->storeTimer(this);
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,11 @@
|
||||
|
||||
#include "cpptick/cpptick.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
@ -21,11 +26,27 @@ void callbackByN(int to_add)
|
||||
callback_count += to_add;
|
||||
}
|
||||
|
||||
struct TestHal
|
||||
{
|
||||
static std::uint32_t getMillis()
|
||||
{
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::system_clock;
|
||||
|
||||
return std::uint32_t(
|
||||
duration_cast<milliseconds>(system_clock::now().time_since_epoch())
|
||||
.count()
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Slot calls function properly with void args.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler scheduler;
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void ()> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByOne);
|
||||
@ -76,7 +97,7 @@ TEST_CASE("Slot calls function properly with void args.", "[cpptick]")
|
||||
|
||||
TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler scheduler;
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void (int)> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByN);
|
||||
@ -96,3 +117,41 @@ TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
TEST_CASE("Timer will be invoked.", "[cpptick]")
|
||||
{
|
||||
cpptick::Scheduler<TestHal> scheduler;
|
||||
|
||||
cpptick::Slot<void ()> slot(&scheduler);
|
||||
auto* f = slot.connect(callbackByOne);
|
||||
|
||||
callback_count = 0;
|
||||
REQUIRE(callback_count == 0);
|
||||
|
||||
cpptick::Timer timer(&scheduler, 100, true);
|
||||
timer.setSlot(slot);
|
||||
|
||||
SECTION("Timer will be invoked after 100 milliseconds.")
|
||||
{
|
||||
timer.start();
|
||||
auto current = TestHal::getMillis();
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 0);
|
||||
|
||||
std::this_thread::sleep_for(110ms);
|
||||
|
||||
scheduler.tick();
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
SECTION("Single shot timer isn't executed again.")
|
||||
{
|
||||
CHECK(callback_count == 1);
|
||||
|
||||
std::this_thread::sleep_for(110ms);
|
||||
CHECK(callback_count == 1);
|
||||
}
|
||||
}
|
||||
|
||||
delete f;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user