cpptick: add timers
This commit is contained in:
parent
80a4f39f7b
commit
29ffd478ef
@ -1,10 +1,13 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
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)
|
add_library(skullc::cpptick ALIAS cpptick)
|
||||||
|
|
||||||
target_include_directories(cpptick
|
target_include_directories(cpptick
|
||||||
INTERFACE
|
PUBLIC
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
@ -16,7 +19,7 @@ set_target_properties(cpptick
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(cpptick
|
target_link_libraries(cpptick
|
||||||
INTERFACE
|
PUBLIC
|
||||||
skullc::utility
|
skullc::utility
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
namespace cpptick
|
namespace cpptick
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,5 +5,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "cpptick/argstore.hpp"
|
#include "cpptick/argstore.hpp"
|
||||||
|
#include "cpptick/timer.hpp"
|
||||||
#include "cpptick/scheduler.hpp"
|
#include "cpptick/scheduler.hpp"
|
||||||
#include "cpptick/slot.hpp"
|
#include "cpptick/slot.hpp"
|
||||||
|
|||||||
@ -5,21 +5,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
#include <array>
|
||||||
|
#include <algorithm>
|
||||||
#include <utility_function.hpp>
|
#include <utility_function.hpp>
|
||||||
#include <utility_ringbuffer.hpp>
|
#include <utility_ringbuffer.hpp>
|
||||||
|
|
||||||
#include "cpptick/argstore.hpp"
|
#include "cpptick/argstore.hpp"
|
||||||
|
#include "cpptick/timer.hpp"
|
||||||
|
|
||||||
namespace cpptick
|
namespace cpptick
|
||||||
{
|
{
|
||||||
|
|
||||||
struct Scheduler
|
struct BaseScheduler
|
||||||
{
|
{
|
||||||
using StoredCall = std::pair<Utility::IFunction<void (ArgStorage&)>*, ArgStorage>;
|
using StoredCall = std::pair<Utility::IFunction<void (ArgStorage&)>*, ArgStorage>;
|
||||||
Utility::Ringbuffer<StoredCall, 12> stored_calls;
|
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()
|
void tick()
|
||||||
{
|
{
|
||||||
|
if (auto start = stored_timers.begin();
|
||||||
|
start->first != -1u)
|
||||||
|
{
|
||||||
|
checkInvokeTimers();
|
||||||
|
}
|
||||||
|
|
||||||
if (!stored_calls.empty())
|
if (!stored_calls.empty())
|
||||||
{
|
{
|
||||||
auto* f = stored_calls.begin()->first;
|
auto* f = stored_calls.begin()->first;
|
||||||
@ -39,6 +58,66 @@ struct Scheduler
|
|||||||
storage.reset();
|
storage.reset();
|
||||||
return storage;
|
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...)>
|
struct Slot<R(Args...)>
|
||||||
{
|
{
|
||||||
std::array<Utility::IFunction<R (Args...)>*, 12> signals;
|
std::array<Utility::IFunction<R (Args...)>*, 12> signals;
|
||||||
Scheduler* scheduler;
|
BaseScheduler* scheduler;
|
||||||
|
|
||||||
Utility::FunctionOwned<Slot<R(Args...)>, void (ArgStorage&)> invoke_ptr;
|
Utility::FunctionOwned<Slot<R(Args...)>, void (ArgStorage&)> invoke_ptr;
|
||||||
|
|
||||||
Slot() = delete;
|
Slot() = delete;
|
||||||
explicit Slot(Scheduler* sched)
|
explicit Slot(BaseScheduler* sched)
|
||||||
: scheduler(sched)
|
: scheduler(sched)
|
||||||
, invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
, 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 "cpptick/cpptick.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -21,11 +26,27 @@ void callbackByN(int to_add)
|
|||||||
callback_count += 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]")
|
TEST_CASE("Slot calls function properly with void args.", "[cpptick]")
|
||||||
{
|
{
|
||||||
cpptick::Scheduler scheduler;
|
cpptick::Scheduler<TestHal> scheduler;
|
||||||
|
|
||||||
cpptick::Slot<void ()> slot(&scheduler);
|
cpptick::Slot<void ()> slot(&scheduler);
|
||||||
auto* f = slot.connect(callbackByOne);
|
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]")
|
TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
||||||
{
|
{
|
||||||
cpptick::Scheduler scheduler;
|
cpptick::Scheduler<TestHal> scheduler;
|
||||||
|
|
||||||
cpptick::Slot<void (int)> slot(&scheduler);
|
cpptick::Slot<void (int)> slot(&scheduler);
|
||||||
auto* f = slot.connect(callbackByN);
|
auto* f = slot.connect(callbackByN);
|
||||||
@ -96,3 +117,41 @@ TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
|||||||
|
|
||||||
delete f;
|
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