Compare commits
4 Commits
feature/cp
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af9db5a1b0 | ||
|
|
470ad75376 | ||
| e6f5315dac | |||
| f64a92aa5c |
@ -4,15 +4,10 @@ on: [push, pull_request]
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Unit-Tests:
|
Unit-Tests:
|
||||||
runs-on: ubuntu-latest
|
runs-on: fedora-cpp
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Prepare environment
|
|
||||||
run: |
|
|
||||||
apt update
|
|
||||||
apt install -y --no-install-recommends build-essential cmake ninja-build
|
|
||||||
|
|
||||||
- name: Configure build
|
- name: Configure build
|
||||||
working-directory: ${{runner.workspace}}
|
working-directory: ${{runner.workspace}}
|
||||||
run: |
|
run: |
|
||||||
@ -28,3 +23,19 @@ jobs:
|
|||||||
- name: Run tests
|
- name: Run tests
|
||||||
working-directory: ${{runner.workspace}}/build
|
working-directory: ${{runner.workspace}}/build
|
||||||
run: ctest . --output-on-failure
|
run: ctest . --output-on-failure
|
||||||
|
Docs:
|
||||||
|
runs-on: fedora-cpp
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Configure build
|
||||||
|
working-directory: ${{runner.workspace}}
|
||||||
|
run: |
|
||||||
|
cmake . -B${{runner.workspace}}/build \
|
||||||
|
-G"Ninja" \
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DSKULLC_WITH_DOCS=ON
|
||||||
|
|
||||||
|
- name: Build docs
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: ninja Sphinx
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
set(SKULLC_VERSION 0.2.0)
|
set(SKULLC_VERSION 0.1.0)
|
||||||
|
|
||||||
project(skullc
|
project(skullc
|
||||||
VERSION ${SKULLC_VERSION}
|
VERSION ${SKULLC_VERSION}
|
||||||
@ -14,12 +14,14 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
|
|||||||
|
|
||||||
option(SKULLC_WITH_TESTS "Enable unit testing." OFF)
|
option(SKULLC_WITH_TESTS "Enable unit testing." OFF)
|
||||||
option(SKULLC_WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
option(SKULLC_WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
||||||
|
option(SKULLC_USE_HAL_ST "Enable the ST HAl when SKULLC_WITH_HAL is enabled." OFF)
|
||||||
|
option(SKULLC_USE_HAL_ESP "Enable the ESP HAL when SKULLC_WITH_HAL is enabled." OFF)
|
||||||
|
option(SKULLC_WITH_DOCS "Enable documentation building." OFF)
|
||||||
|
|
||||||
include(skullc-install)
|
include(skullc-install)
|
||||||
|
|
||||||
add_subdirectory(Peripherals)
|
add_subdirectory(Peripherals)
|
||||||
add_subdirectory(Utility)
|
add_subdirectory(Utility)
|
||||||
add_subdirectory(CppTick)
|
|
||||||
add_subdirectory(Messaging)
|
add_subdirectory(Messaging)
|
||||||
|
|
||||||
if(SKULLC_WITH_TESTS)
|
if(SKULLC_WITH_TESTS)
|
||||||
@ -27,6 +29,10 @@ if(SKULLC_WITH_TESTS)
|
|||||||
add_subdirectory(Tests)
|
add_subdirectory(Tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SKULLC_WITH_DOCS)
|
||||||
|
add_subdirectory(Docs)
|
||||||
|
endif()
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
configure_file(skullc-config.cmake
|
configure_file(skullc-config.cmake
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
|
||||||
|
|
||||||
add_library(cpptick STATIC
|
|
||||||
Src/timer.cpp
|
|
||||||
)
|
|
||||||
|
|
||||||
add_library(skullc::cpptick ALIAS cpptick)
|
|
||||||
|
|
||||||
target_include_directories(cpptick
|
|
||||||
PUBLIC
|
|
||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
|
||||||
$<INSTALL_INTERFACE:include>
|
|
||||||
)
|
|
||||||
|
|
||||||
set_target_properties(cpptick
|
|
||||||
PROPERTIES
|
|
||||||
CXX_STANDARD 20
|
|
||||||
CXX_STANDARD_REQUIRED TRUE
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(cpptick
|
|
||||||
PUBLIC
|
|
||||||
skullc::utility
|
|
||||||
)
|
|
||||||
|
|
||||||
skullc_install_packages(skullc cpptick ${SKULLC_VERSION})
|
|
||||||
@ -1,118 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 24/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace cpptick
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept TrivialClass = std::is_trivial_v<T> && std::is_trivially_destructible_v<T>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept CopyableClass = !TrivialClass<T> && std::copy_constructible<T>;
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
concept SlotArgument = TrivialClass<T> || CopyableClass<T>;
|
|
||||||
|
|
||||||
struct ArgStorage
|
|
||||||
{
|
|
||||||
std::array<char, sizeof(int) * 12> buffer;
|
|
||||||
std::array<char*, 12> pointer_buffer;
|
|
||||||
std::array<char*, 12>::iterator pointer_buffer_head;
|
|
||||||
std::size_t space_remaining;
|
|
||||||
|
|
||||||
ArgStorage() = default;
|
|
||||||
ArgStorage(const ArgStorage&) = default;
|
|
||||||
ArgStorage(ArgStorage&&) = default;
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
space_remaining = buffer.size();
|
|
||||||
pointer_buffer_head = pointer_buffer.begin();
|
|
||||||
*pointer_buffer_head = buffer.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TrivialClass T>
|
|
||||||
void pushArg(T&& arg)
|
|
||||||
{
|
|
||||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
|
||||||
std::exit(1);
|
|
||||||
|
|
||||||
void* memory_location = *pointer_buffer_head;
|
|
||||||
|
|
||||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
|
||||||
std::exit(2);
|
|
||||||
|
|
||||||
std::memcpy(memory_location, &arg, sizeof(T));
|
|
||||||
pointer_buffer_head++;
|
|
||||||
if (pointer_buffer_head != pointer_buffer.end())
|
|
||||||
{
|
|
||||||
*pointer_buffer_head = (char*) memory_location + sizeof(T);
|
|
||||||
space_remaining -= sizeof(T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<CopyableClass T>
|
|
||||||
void pushArg(T&& arg)
|
|
||||||
{
|
|
||||||
if (pointer_buffer_head == pointer_buffer.end() || space_remaining == 0)
|
|
||||||
std::exit(1);
|
|
||||||
|
|
||||||
void* memory_location = *pointer_buffer_head;
|
|
||||||
|
|
||||||
if (std::align(alignof(T), sizeof(T), memory_location, space_remaining) == nullptr)
|
|
||||||
std::exit(2);
|
|
||||||
|
|
||||||
new (memory_location) T{arg};
|
|
||||||
pointer_buffer_head++;
|
|
||||||
if (pointer_buffer_head != pointer_buffer.end())
|
|
||||||
{
|
|
||||||
*pointer_buffer_head = (char*) memory_location + sizeof(T);
|
|
||||||
space_remaining -= sizeof(T);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<TrivialClass T>
|
|
||||||
void cleanUp(const std::size_t)
|
|
||||||
{}
|
|
||||||
|
|
||||||
template<CopyableClass T>
|
|
||||||
void cleanUp(const std::size_t index)
|
|
||||||
{
|
|
||||||
T* item = reinterpret_cast<T*>(pointer_buffer[index]);
|
|
||||||
item->~T();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args, size_t... Is>
|
|
||||||
void cleanUp(std::index_sequence<Is...>)
|
|
||||||
{
|
|
||||||
(cleanUp<Args>(Is), ...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
void cleanUp()
|
|
||||||
{
|
|
||||||
cleanUp<Args...>(std::make_index_sequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
template<SlotArgument T>
|
|
||||||
T& at(const std::size_t idx)
|
|
||||||
{
|
|
||||||
return *((T*) pointer_buffer[idx]);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<SlotArgument T>
|
|
||||||
const T& at(const std::size_t idx) const
|
|
||||||
{
|
|
||||||
return *((T*) pointer_buffer[idx]);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace cpptick
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 24/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "cpptick/argstore.hpp"
|
|
||||||
#include "cpptick/scheduler.hpp"
|
|
||||||
#include "cpptick/slot.hpp"
|
|
||||||
#include "cpptick/timer.hpp"
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 24/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <array>
|
|
||||||
#include <tuple>
|
|
||||||
#include <utility_function.hpp>
|
|
||||||
#include <utility_ringbuffer.hpp>
|
|
||||||
|
|
||||||
#include "cpptick/argstore.hpp"
|
|
||||||
#include "cpptick/timer.hpp"
|
|
||||||
|
|
||||||
namespace cpptick
|
|
||||||
{
|
|
||||||
|
|
||||||
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;
|
|
||||||
auto& args = stored_calls.begin()->second;
|
|
||||||
(*f)(args);
|
|
||||||
stored_calls.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArgStorage& storeCall(Utility::IFunction<void(ArgStorage&)>* call)
|
|
||||||
{
|
|
||||||
// @todo: handle overflow...
|
|
||||||
|
|
||||||
// still constructs double.
|
|
||||||
stored_calls.emplace_back(call, ArgStorage{});
|
|
||||||
auto& storage = stored_calls.back().second;
|
|
||||||
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->type == Timer::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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace cpptick
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 12/09/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <cstring>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include <utility_function.hpp>
|
|
||||||
#include <utility_ringbuffer.hpp>
|
|
||||||
|
|
||||||
#include "cpptick/argstore.hpp"
|
|
||||||
#include "cpptick/scheduler.hpp"
|
|
||||||
|
|
||||||
namespace cpptick
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename>
|
|
||||||
struct Slot;
|
|
||||||
|
|
||||||
template<typename R, SlotArgument... Args>
|
|
||||||
struct Slot<R(Args...)>
|
|
||||||
{
|
|
||||||
std::array<Utility::IFunction<R(Args...)>*, 12> signals;
|
|
||||||
BaseScheduler* scheduler;
|
|
||||||
|
|
||||||
Utility::FunctionOwned<Slot<R(Args...)>, void(ArgStorage&)> invoke_ptr;
|
|
||||||
|
|
||||||
Slot() = delete;
|
|
||||||
explicit Slot(BaseScheduler* sched)
|
|
||||||
: scheduler(sched), invoke_ptr(*this, &Slot<R(Args...)>::callUp)
|
|
||||||
{
|
|
||||||
signals.fill(nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Slot(const Slot&) = delete;
|
|
||||||
Slot(Slot&&) = delete;
|
|
||||||
Slot& operator=(const Slot&) = delete;
|
|
||||||
Slot& operator=(Slot&&) = delete;
|
|
||||||
|
|
||||||
void connect(Utility::IFunction<R(Args...)>* signal)
|
|
||||||
{
|
|
||||||
for (auto*& callable : signals)
|
|
||||||
{
|
|
||||||
if (callable == nullptr)
|
|
||||||
{
|
|
||||||
callable = signal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Utility::IFunction<R(Args...)>* connect(R (*func)(Args...))
|
|
||||||
{
|
|
||||||
auto* f = new Utility::Function<R(Args...)>(func);
|
|
||||||
|
|
||||||
connect(f);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Source>
|
|
||||||
Utility::IFunction<R(Args...)>* connect(Source& src, R (Source::*func)(Args...))
|
|
||||||
{
|
|
||||||
auto* f = new Utility::FunctionOwned<Source, R(Args...)>(src, func);
|
|
||||||
|
|
||||||
connect(f);
|
|
||||||
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t... Is>
|
|
||||||
void callUpFunction(Utility::IFunction<R(Args...)>* func, ArgStorage& args, std::index_sequence<Is...>)
|
|
||||||
{
|
|
||||||
(*func)(std::forward<Args>(args.at<Args>(Is))...);
|
|
||||||
}
|
|
||||||
|
|
||||||
void callUpFunction(Utility::IFunction<R(Args...)>* func, ArgStorage& args)
|
|
||||||
{
|
|
||||||
callUpFunction(func, args, std::make_index_sequence<sizeof...(Args)>{});
|
|
||||||
}
|
|
||||||
|
|
||||||
void callUp(ArgStorage& args)
|
|
||||||
{
|
|
||||||
for (auto* f : signals)
|
|
||||||
{
|
|
||||||
if (f)
|
|
||||||
callUpFunction(f, args);
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.cleanUp<Args...>();
|
|
||||||
}
|
|
||||||
|
|
||||||
void invoke(Args... args)
|
|
||||||
{
|
|
||||||
ArgStorage& storage = scheduler->storeCall(&invoke_ptr);
|
|
||||||
(storage.pushArg(std::forward<Args>(args)), ...);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace cpptick
|
|
||||||
@ -1,68 +0,0 @@
|
|||||||
//
|
|
||||||
// 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
|
|
||||||
{
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
ONE_SHOT,
|
|
||||||
PERIODIC
|
|
||||||
};
|
|
||||||
|
|
||||||
Timer(BaseScheduler* scheduler, const std::uint32_t timeout_ms, const Type type)
|
|
||||||
: scheduler_(scheduler), period_ms(timeout_ms), type(type)
|
|
||||||
{}
|
|
||||||
|
|
||||||
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;
|
|
||||||
Type type;
|
|
||||||
|
|
||||||
private:
|
|
||||||
BaseScheduler* scheduler_;
|
|
||||||
Utility::IFunction<void(ArgStorage&)>* slot_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace cpptick
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 24/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "cpptick/timer.hpp"
|
|
||||||
|
|
||||||
#include "cpptick/scheduler.hpp"
|
|
||||||
|
|
||||||
namespace cpptick
|
|
||||||
{
|
|
||||||
|
|
||||||
void Timer::start()
|
|
||||||
{
|
|
||||||
scheduler_->storeTimer(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace cpptick
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
include(${CMAKE_CURRENT_LIST_DIR}/skullc-cpptick-config-version.cmake)
|
|
||||||
include(${CMAKE_CURRENT_LIST_DIR}/skullc-cpptick-targets.cmake)
|
|
||||||
53
Docs/CMakeLists.txt
Normal file
53
Docs/CMakeLists.txt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
find_package(Doxygen REQUIRED)
|
||||||
|
find_package(Sphinx REQUIRED)
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SKULLC_PUBLIC_HEADERS
|
||||||
|
${PROJECT_SOURCE_DIR}/Messaging/Inc/*
|
||||||
|
${PROJECT_SOURCE_DIR}/Threads/Inc/*
|
||||||
|
${PROJECT_SOURCE_DIR}/Utility/Inc/*
|
||||||
|
${PROJECT_SOURCE_DIR}/Peripherals/Inc/*
|
||||||
|
)
|
||||||
|
|
||||||
|
set(DOXYGEN_INPUT_DIRS "${PROJECT_SOURCE_DIR}/Messaging/Inc ${PROJECT_SOURCE_DIR}/Threads/Inc ${PROJECT_SOURCE_DIR}/Utility/Inc ${PROJECT_SOURCE_DIR}/Peripherals/Inc")
|
||||||
|
set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/docs/doxygen)
|
||||||
|
set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/xml/index.xml)
|
||||||
|
set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
|
||||||
|
set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
|
||||||
|
|
||||||
|
configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) #Doxygen won't create this for us
|
||||||
|
add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE}
|
||||||
|
DEPENDS ${SKULLC_PUBLIC_HEADERS}
|
||||||
|
COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT}
|
||||||
|
MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN}
|
||||||
|
COMMENT "Generating docs"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE})
|
||||||
|
|
||||||
|
set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/sphinx)
|
||||||
|
set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html)
|
||||||
|
file(GLOB SPHINX_RST_FILES ${SPHINX_SOURCE}/*.rst)
|
||||||
|
|
||||||
|
# Only regenerate Sphinx when:
|
||||||
|
# - Doxygen has rerun
|
||||||
|
# - Our doc files have been updated
|
||||||
|
# - The Sphinx config has been updated
|
||||||
|
add_custom_command(OUTPUT ${SPHINX_INDEX_FILE}
|
||||||
|
COMMAND
|
||||||
|
${SPHINX_EXECUTABLE} -b html
|
||||||
|
# Tell Breathe where to find the Doxygen output
|
||||||
|
-Dbreathe_projects.SkullCPeripheralsLibrary=${DOXYGEN_OUTPUT_DIR}/xml
|
||||||
|
${SPHINX_SOURCE} ${SPHINX_BUILD}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
DEPENDS
|
||||||
|
# Other docs files you want to track should go here (or in some variable)
|
||||||
|
${SPHINX_RST_FILES}
|
||||||
|
${DOXYGEN_INDEX_FILE}
|
||||||
|
MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py
|
||||||
|
COMMENT "Generating documentation with Sphinx")
|
||||||
|
|
||||||
|
# Nice named target so we can run the job easily
|
||||||
|
add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE})
|
||||||
11
Docs/Doxyfile.in
Normal file
11
Docs/Doxyfile.in
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// Every other setting ends up being default.
|
||||||
|
|
||||||
|
GENERATE_XML = YES
|
||||||
|
GENERATE_HTML = YES
|
||||||
|
|
||||||
|
JAVADOC_AUTOBRIEF = YES
|
||||||
|
|
||||||
|
INPUT = @DOXYGEN_INPUT_DIRS@
|
||||||
|
RECURSIVE = YES
|
||||||
|
|
||||||
|
OUTPUT_DIRECTORY = "@DOXYGEN_OUTPUT_DIR@"
|
||||||
31
Docs/conf.py
Normal file
31
Docs/conf.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
# Configuration file for the Sphinx documentation builder.
|
||||||
|
#
|
||||||
|
# For the full list of built-in configuration values, see the documentation:
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html
|
||||||
|
|
||||||
|
# -- Project information -----------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
|
||||||
|
|
||||||
|
project = 'SkullC Peripherals Library'
|
||||||
|
copyright = '2023, Rusted Skull'
|
||||||
|
author = 'Rusted Skull'
|
||||||
|
|
||||||
|
# -- General configuration ---------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||||
|
|
||||||
|
extensions = [ "breathe" ]
|
||||||
|
|
||||||
|
templates_path = ['_templates']
|
||||||
|
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# -- Options for HTML output -------------------------------------------------
|
||||||
|
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
|
||||||
|
|
||||||
|
html_theme = 'classic'
|
||||||
|
html_static_path = ['_static']
|
||||||
|
|
||||||
|
# -- Breathe Configuration"
|
||||||
|
breathe_default_project = "SkullCPeripheralsLibrary"
|
||||||
|
|
||||||
23
Docs/index.rst
Normal file
23
Docs/index.rst
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.. SkullC Peripherals Library documentation master file, created by
|
||||||
|
sphinx-quickstart on Thu Dec 28 23:16:21 2023.
|
||||||
|
You can adapt this file completely to your liking, but it should at least
|
||||||
|
contain the root `toctree` directive.
|
||||||
|
|
||||||
|
Welcome to SkullC Peripherals Library's documentation!
|
||||||
|
======================================================
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
:caption: Contents:
|
||||||
|
|
||||||
|
utility
|
||||||
|
peripherals
|
||||||
|
threads
|
||||||
|
messaging
|
||||||
|
|
||||||
|
Indices and tables
|
||||||
|
==================
|
||||||
|
|
||||||
|
* :ref:`genindex`
|
||||||
|
* :ref:`modindex`
|
||||||
|
* :ref:`search`
|
||||||
17
Docs/messaging.rst
Normal file
17
Docs/messaging.rst
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
|
||||||
|
Messaging Library
|
||||||
|
=================
|
||||||
|
|
||||||
|
A small Packet and Parser implementation for framing random message structures on the wire.
|
||||||
|
|
||||||
|
Use the :cpp:struct:`Packet` to copy in various data. Then send the serialized version of the packet
|
||||||
|
onto the wire.
|
||||||
|
|
||||||
|
:cpp:class:`Messaging::Parser` will help you put bytes coming in from the wire into coherent packets, and will serve you
|
||||||
|
these packets as you're ready for them.
|
||||||
|
|
||||||
|
.. doxygennamespace:: Messaging
|
||||||
|
:project: SkullCPeripheralsLibrary
|
||||||
|
:content-only:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
9
Docs/peripherals.rst
Normal file
9
Docs/peripherals.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
Peripherals Library
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. doxygennamespace:: Peripherals
|
||||||
|
:project: SkullCPeripheralsLibrary
|
||||||
|
:content-only:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
9
Docs/threads.rst
Normal file
9
Docs/threads.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
Threads Library
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. doxygennamespace:: Threads
|
||||||
|
:project: SkullCPeripheralsLibrary
|
||||||
|
:content-only:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
9
Docs/utility.rst
Normal file
9
Docs/utility.rst
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
Utility Library
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. doxygennamespace:: Utility
|
||||||
|
:project: SkullCPeripheralsLibrary
|
||||||
|
:content-only:
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
37
Peripherals/Inc/peripherals_hal_concepts.hpp
Normal file
37
Peripherals/Inc/peripherals_hal_concepts.hpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 11/02/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Peripherals::Hal
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept Gpio = requires (T t, const T ct) {
|
||||||
|
t.set(true);
|
||||||
|
t.toggle();
|
||||||
|
{ ct.read() } -> std::same_as<bool>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
concept SerialInterface = requires (T t, std::uint8_t* data, const std::uint32_t len) {
|
||||||
|
{ t.transmit(data, len) } -> std::same_as<bool>;
|
||||||
|
{ t.transmit(std::array<std::uint8_t, 10>{}) } -> std::same_as<bool>;
|
||||||
|
{ t.receive(data, len) } -> std::same_as<bool>;
|
||||||
|
{ t.receive(std::array<std::uint8_t, 10>{}) } -> std::same_as<bool>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename RegName>
|
||||||
|
concept RegisterInterface = requires (T t, RegName reg, std::uint8_t* data, const std::uint32_t len) {
|
||||||
|
t.writerRegister(reg, std::uint8_t{});
|
||||||
|
t.writeRegisterMultibyte(reg, data, len);
|
||||||
|
{ t.readRegister(reg, std::uint32_t{}) } -> std::same_as<std::uint8_t>;
|
||||||
|
t.readRegisterMultibyte(reg, data, len, std::uint32_t{});
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
51
Peripherals/Inc/peripherals_hal_esp.hpp
Normal file
51
Peripherals/Inc/peripherals_hal_esp.hpp
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 11/02/24.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef SKULLC_USE_HAL_ESP
|
||||||
|
|
||||||
|
#include <peripherals_hal_concepts.hpp>
|
||||||
|
|
||||||
|
#include <driver/gpio.h>
|
||||||
|
|
||||||
|
namespace Peripherals::Hal::Esp
|
||||||
|
{
|
||||||
|
|
||||||
|
struct Gpio
|
||||||
|
{
|
||||||
|
gpio_num_t gpio;
|
||||||
|
const bool inverted = false;
|
||||||
|
|
||||||
|
Gpio() = delete;
|
||||||
|
explicit Gpio(gpio_num_t gpio, const bool inverted)
|
||||||
|
: gpio(gpio), inverted(inverted)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
void set(const bool& state)
|
||||||
|
{
|
||||||
|
gpio_set_level(gpio, inverted ? !state : state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggle() { gpio_set_level(gpio, !gpio_get_level(gpio)); }
|
||||||
|
|
||||||
|
bool read() const
|
||||||
|
{
|
||||||
|
return inverted ? !bool(gpio_get_level(gpio)) : bool(gpio_get_level(gpio));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(Peripherals::Hal::Gpio<Gpio>);
|
||||||
|
|
||||||
|
#define CREATE_GPIO(name) \
|
||||||
|
Peripherals::Hal::Esp::Gpio{name, false}
|
||||||
|
|
||||||
|
#define CREATE_INV_GPIO(name) \
|
||||||
|
Peripherals::Hal::Esp::Gpio{name, true}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
# warning "ESP HAL included without SKULLC_USE_HAL_ESP being defined."
|
||||||
|
#endif /* SKULLC_USE_HAL_ESP */
|
||||||
@ -8,6 +8,8 @@
|
|||||||
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
|
|
||||||
|
#ifdef SKULLC_USE_HAL_ST
|
||||||
|
|
||||||
#include <main.h>
|
#include <main.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
@ -343,4 +345,8 @@ struct ItmSerialInterface
|
|||||||
}// namespace Hal
|
}// namespace Hal
|
||||||
}// namespace Peripherals
|
}// namespace Peripherals
|
||||||
|
|
||||||
|
#else
|
||||||
|
# warning "ESP HAL included without SKULLC_USE_HAL_ESP being defined."
|
||||||
|
#endif /* SKULLC_USE_HAL_ST */
|
||||||
|
|
||||||
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */
|
#endif /* SKULLC_PERIPHERALS_HAL_ST_HPP_ */
|
||||||
|
|||||||
@ -31,8 +31,6 @@ add_executable(tests
|
|||||||
enum_helpers.cpp
|
enum_helpers.cpp
|
||||||
bytes.cpp
|
bytes.cpp
|
||||||
filters.cpp
|
filters.cpp
|
||||||
cpptick.cpp
|
|
||||||
notnull.cpp
|
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(tests
|
target_link_libraries(tests
|
||||||
@ -41,12 +39,11 @@ target_link_libraries(tests
|
|||||||
skullc::messaging
|
skullc::messaging
|
||||||
skullc::peripherals
|
skullc::peripherals
|
||||||
Catch2::Catch2
|
Catch2::Catch2
|
||||||
skullc::cpptick
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set_target_properties(tests
|
set_target_properties(tests
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
CXX_STANDARD 20
|
CXX_STANDARD 17
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
||||||
|
|||||||
@ -1,268 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 12/09/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#include "cpptick/cpptick.hpp"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
int callback_count = 0;
|
|
||||||
|
|
||||||
void callbackByOne()
|
|
||||||
{
|
|
||||||
callback_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
|
|
||||||
TEST_CASE("Slot calls function properly with void args.", "[cpptick]")
|
|
||||||
{
|
|
||||||
cpptick::Scheduler<TestHal> scheduler;
|
|
||||||
|
|
||||||
cpptick::Slot<void()> slot(&scheduler);
|
|
||||||
auto* f = slot.connect(callbackByOne);
|
|
||||||
|
|
||||||
callback_count = 0;
|
|
||||||
REQUIRE(callback_count == 0);
|
|
||||||
|
|
||||||
SECTION("invoke() calls the slot appropriately.")
|
|
||||||
{
|
|
||||||
slot.invoke();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 1);
|
|
||||||
|
|
||||||
SECTION("Second tick doesn't invoke the slot again.")
|
|
||||||
{
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Second invoke() will call the slot again.")
|
|
||||||
{
|
|
||||||
slot.invoke();
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Multiple calls queue properly.")
|
|
||||||
{
|
|
||||||
const int count = 3;
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
slot.invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
CHECK(callback_count == i);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == i + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete f;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Slot calls function properly with args.", "[cpptick]")
|
|
||||||
{
|
|
||||||
cpptick::Scheduler<TestHal> scheduler;
|
|
||||||
|
|
||||||
cpptick::Slot<void(int)> slot(&scheduler);
|
|
||||||
auto* f = slot.connect(callbackByN);
|
|
||||||
|
|
||||||
callback_count = 0;
|
|
||||||
REQUIRE(callback_count == 0);
|
|
||||||
|
|
||||||
SECTION("invoke() passes argument appropriately.")
|
|
||||||
{
|
|
||||||
const int to_add = 5;
|
|
||||||
slot.invoke(to_add);
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == to_add);
|
|
||||||
}
|
|
||||||
|
|
||||||
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, cpptick::Timer::ONE_SHOT);
|
|
||||||
timer.setSlot(slot);
|
|
||||||
|
|
||||||
SECTION("Timer will be invoked after 100 milliseconds.")
|
|
||||||
{
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
|
|
||||||
SECTION("Timer won't be executed prematurely.")
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(80ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Time will be executed after period elapsed.")
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("An unstarted timer does not run.")
|
|
||||||
{
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(110ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete f;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Periodic timers will be invoked repeatedly.", "[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, cpptick::Timer::PERIODIC);
|
|
||||||
timer.setSlot(slot);
|
|
||||||
|
|
||||||
SECTION("Timer will be invoked after 100 milliseconds.")
|
|
||||||
{
|
|
||||||
timer.start();
|
|
||||||
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
|
|
||||||
SECTION("Timer won't be executed prematurely.")
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(80ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTION("Time will be executed after period elapsed.")
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(110ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 1);
|
|
||||||
|
|
||||||
SECTION("Periodic timer is executed again.")
|
|
||||||
{
|
|
||||||
CHECK(callback_count == 1);
|
|
||||||
|
|
||||||
std::this_thread::sleep_for(110ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete f;
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Sequential timers operate appropriately.", "[cpptick]")
|
|
||||||
{
|
|
||||||
cpptick::Scheduler<TestHal> scheduler;
|
|
||||||
|
|
||||||
cpptick::Slot<void()> slot_a(&scheduler);
|
|
||||||
auto* f = slot_a.connect(callbackByOne);
|
|
||||||
|
|
||||||
cpptick::Timer timer_a(&scheduler, 100, cpptick::Timer::PERIODIC);
|
|
||||||
timer_a.setSlot(slot_a);
|
|
||||||
|
|
||||||
auto callback_by_two = []() -> void {
|
|
||||||
callback_count += 2;
|
|
||||||
};
|
|
||||||
cpptick::Slot<void()> slot_b(&scheduler);
|
|
||||||
auto* ff = slot_b.connect(callback_by_two);
|
|
||||||
|
|
||||||
cpptick::Timer timer_b(&scheduler, 50, cpptick::Timer::ONE_SHOT);
|
|
||||||
timer_b.setSlot(slot_b);
|
|
||||||
|
|
||||||
callback_count = 0;
|
|
||||||
REQUIRE(callback_count == 0);
|
|
||||||
|
|
||||||
timer_a.start();
|
|
||||||
timer_b.start();
|
|
||||||
|
|
||||||
SECTION("Shorter timer is called first.", "[cpptick]")
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(60ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 2);
|
|
||||||
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 2);
|
|
||||||
|
|
||||||
SECTION("Longer timer is executed afterwards.", "[cpptick]")
|
|
||||||
{
|
|
||||||
std::this_thread::sleep_for(50ms);
|
|
||||||
scheduler.tick();
|
|
||||||
CHECK(callback_count == 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
delete f;
|
|
||||||
delete ff;
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 25/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <catch2/catch.hpp>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#undef NDEBUG
|
|
||||||
#define NDEBUG_WAS_DEFINED
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utility_assert.hpp"
|
|
||||||
#include "utility_notnull.hpp"
|
|
||||||
|
|
||||||
#ifdef NDEBUG_WAS_DEFINED
|
|
||||||
#define NDEBUG
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace
|
|
||||||
{
|
|
||||||
|
|
||||||
std::atomic<bool> assert_flag = false;
|
|
||||||
|
|
||||||
void assertCallback(const char*, const char*, const int)
|
|
||||||
{
|
|
||||||
assert_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace
|
|
||||||
|
|
||||||
TEST_CASE("Assert is tripped if nullptr passed.", "[utility],[notnull]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
REQUIRE(assert_flag == false);
|
|
||||||
|
|
||||||
auto call = [](Utility::NotNull<int> i) -> void {};
|
|
||||||
|
|
||||||
call(nullptr);
|
|
||||||
CHECK(assert_flag == true);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("Assert is not tripped if valid pointer is passed.", "[utility],[notnull]")
|
|
||||||
{
|
|
||||||
assert_flag = false;
|
|
||||||
Utility::Assert::setHandler(assertCallback);
|
|
||||||
REQUIRE(assert_flag == false);
|
|
||||||
|
|
||||||
auto call = [](Utility::NotNull<int> i) -> int {
|
|
||||||
return *i;
|
|
||||||
};
|
|
||||||
|
|
||||||
int a = 5;
|
|
||||||
const int test = call(&a);
|
|
||||||
CHECK(assert_flag == false);
|
|
||||||
CHECK(a == test);
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by erki on 25/10/23.
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "utility_assert.hpp"
|
|
||||||
|
|
||||||
namespace Utility
|
|
||||||
{
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct NotNull
|
|
||||||
{
|
|
||||||
NotNull(T* ptr)
|
|
||||||
: ptr_(ptr)
|
|
||||||
{
|
|
||||||
SKULLC_ASSERT_DEBUG(ptr_ != nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
NotNull() = delete;
|
|
||||||
NotNull(const NotNull&) = delete;
|
|
||||||
NotNull(NotNull&&) = delete;
|
|
||||||
NotNull& operator=(const NotNull&) = delete;
|
|
||||||
NotNull& operator=(NotNull&&) = delete;
|
|
||||||
|
|
||||||
const T& operator*() const
|
|
||||||
{
|
|
||||||
return *ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
T& operator*()
|
|
||||||
{
|
|
||||||
return *ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* operator->() const noexcept
|
|
||||||
{
|
|
||||||
return ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
T* operator->() noexcept
|
|
||||||
{
|
|
||||||
return ptr_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
T* ptr_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Utility
|
|
||||||
11
cmake/FindSphinx.cmake
Normal file
11
cmake/FindSphinx.cmake
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#Look for an executable called sphinx-build
|
||||||
|
find_program(SPHINX_EXECUTABLE
|
||||||
|
NAMES sphinx-build
|
||||||
|
DOC "Path to sphinx-build executable")
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
|
||||||
|
#Handle standard arguments to find_package like REQUIRED and QUIET
|
||||||
|
find_package_handle_standard_args(Sphinx
|
||||||
|
"Failed to find sphinx-build executable"
|
||||||
|
SPHINX_EXECUTABLE)
|
||||||
Loading…
x
Reference in New Issue
Block a user