Compare commits
33 Commits
other/mous
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af9db5a1b0 | ||
|
|
470ad75376 | ||
| e6f5315dac | |||
| f64a92aa5c | |||
| 0698081d7b | |||
|
|
927b950dec | ||
|
|
47d7e87023 | ||
|
|
a488ba66f3 | ||
|
|
ea99a8a6ba | ||
|
|
500c2704bb | ||
|
|
8fbb0efd5d | ||
|
|
a0639ec3f1 | ||
|
|
d3b85b7f6c | ||
|
|
d10675e3ec | ||
|
|
6e22b02e92 | ||
|
|
458fd9e7f2 | ||
|
|
3aca35788b | ||
|
|
718b6705fd | ||
|
|
0ba9416a57 | ||
|
|
eef2e1318c | ||
|
|
c0a622c5e4 | ||
|
|
e554d30bf6 | ||
|
|
9924559fe0 | ||
|
|
54fd0e2332 | ||
|
|
bb220c9e92 | ||
|
|
69e1538cbb | ||
|
|
227af3c9fc | ||
|
|
b8c3e32fd6 | ||
|
|
d909651bf1 | ||
|
|
dc9e159377 | ||
|
|
88aa3087fe | ||
|
|
0d1ea1c1c2 | ||
|
|
43bc8d8265 |
@ -17,7 +17,7 @@ AlwaysBreakAfterReturnType: None
|
|||||||
AlwaysBreakTemplateDeclarations: Yes
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
AfterCaseLabel: false
|
AfterCaseLabel: true
|
||||||
AfterClass: true
|
AfterClass: true
|
||||||
AfterControlStatement: Always
|
AfterControlStatement: Always
|
||||||
AfterEnum: true
|
AfterEnum: true
|
||||||
@ -25,8 +25,8 @@ BraceWrapping:
|
|||||||
AfterNamespace: true
|
AfterNamespace: true
|
||||||
AfterUnion: true
|
AfterUnion: true
|
||||||
AfterStruct: true
|
AfterStruct: true
|
||||||
BeforeCatch: false
|
BeforeCatch: true
|
||||||
BeforeElse: false
|
BeforeElse: true
|
||||||
IndentBraces: false
|
IndentBraces: false
|
||||||
SplitEmptyFunction: false
|
SplitEmptyFunction: false
|
||||||
SplitEmptyRecord: true
|
SplitEmptyRecord: true
|
||||||
|
|||||||
29
.drone.yml
29
.drone.yml
@ -1,29 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: default
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: build and test
|
|
||||||
pull: never
|
|
||||||
image: erki/cpp-ubuntu:latest
|
|
||||||
commands:
|
|
||||||
- mkdir -p build
|
|
||||||
- cd build
|
|
||||||
- conan install .. --build=missing
|
|
||||||
- cmake .. -G "Ninja" -DCMAKE_BUILD_TYPE=Release -DWITH_TESTS=ON
|
|
||||||
- ninja
|
|
||||||
- ctest . --output-on-failure
|
|
||||||
- name: notify
|
|
||||||
image: plugins/matrix
|
|
||||||
settings:
|
|
||||||
homeserver:
|
|
||||||
from_secret: matrix_homeserver
|
|
||||||
roomid:
|
|
||||||
from_secret: matrix_roomid
|
|
||||||
username:
|
|
||||||
from_secret: matrix_username
|
|
||||||
password:
|
|
||||||
from_secret: matrix_password
|
|
||||||
when:
|
|
||||||
status:
|
|
||||||
- failure
|
|
||||||
41
.gitea/workflows/ci.yml
Normal file
41
.gitea/workflows/ci.yml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
name: CI & Unit Tests
|
||||||
|
run-name: CI & Unit Tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Unit-Tests:
|
||||||
|
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_TESTS=ON
|
||||||
|
|
||||||
|
- name: Build tests + lib
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
run: ninja
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
working-directory: ${{runner.workspace}}/build
|
||||||
|
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,22 +1,22 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
set(version 0.1.0)
|
set(SKULLC_VERSION 0.1.0)
|
||||||
|
|
||||||
project(skullc
|
project(skullc
|
||||||
VERSION ${version}
|
VERSION ${SKULLC_VERSION}
|
||||||
LANGUAGES
|
LANGUAGES
|
||||||
C
|
C
|
||||||
CXX
|
CXX
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
|
#list(APPEND CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR})
|
||||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
|
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
option(SKULLC_WITH_TESTS "Enable unit testing." OFF)
|
||||||
list(APPEND CMAKE_CXX_FLAGS "-Wall -Wextra")
|
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(WITH_TESTS "Enable unit testing." OFF)
|
option(SKULLC_USE_HAL_ESP "Enable the ESP HAL when SKULLC_WITH_HAL is enabled." OFF)
|
||||||
option(WITH_HAL "Enable the compiling and deployment of the HAL dependent sections." OFF)
|
option(SKULLC_WITH_DOCS "Enable documentation building." OFF)
|
||||||
|
|
||||||
include(skullc-install)
|
include(skullc-install)
|
||||||
|
|
||||||
@ -24,11 +24,15 @@ add_subdirectory(Peripherals)
|
|||||||
add_subdirectory(Utility)
|
add_subdirectory(Utility)
|
||||||
add_subdirectory(Messaging)
|
add_subdirectory(Messaging)
|
||||||
|
|
||||||
if(WITH_TESTS)
|
if(SKULLC_WITH_TESTS)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
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
|
||||||
|
|||||||
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:
|
||||||
@ -10,4 +10,4 @@ target_include_directories(messaging
|
|||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
|
||||||
skullc_install_packages(skullc messaging ${version})
|
skullc_install_packages(skullc messaging ${SKULLC_VERSION})
|
||||||
|
|||||||
@ -11,4 +11,4 @@ target_include_directories(peripherals
|
|||||||
)
|
)
|
||||||
|
|
||||||
## INSTALL
|
## INSTALL
|
||||||
skullc_install_packages(skullc peripherals ${version})
|
skullc_install_packages(skullc peripherals ${SKULLC_VERSION})
|
||||||
|
|||||||
@ -42,19 +42,24 @@ public:
|
|||||||
const bool is_pressed = sw.read();
|
const bool is_pressed = sw.read();
|
||||||
ButtonPress new_state = ButtonPress::NOT_PRESSED;
|
ButtonPress new_state = ButtonPress::NOT_PRESSED;
|
||||||
|
|
||||||
|
const std::uint32_t time_held = hal::getMillis() - time_pressed_down_;
|
||||||
|
|
||||||
if (is_pressed && !was_pressed_)
|
if (is_pressed && !was_pressed_)
|
||||||
{
|
{
|
||||||
time_pressed_down_ = hal::getMillis();
|
time_pressed_down_ = hal::getMillis();
|
||||||
} else if (!is_pressed && was_pressed_)
|
}
|
||||||
|
else if (!is_pressed && was_pressed_)
|
||||||
{
|
{
|
||||||
const std::uint32_t time_held = hal::getMillis() - time_pressed_down_;
|
if (time_held > TIMEOUT_SHORT_PRESS && time_held <= TIMEOUT_LONG_PRESS)
|
||||||
if (time_held > TIMEOUT_LONG_PRESS)
|
|
||||||
new_state = ButtonPress::LONG_PRESS;
|
|
||||||
else if (time_held > TIMEOUT_SHORT_PRESS)
|
|
||||||
new_state = ButtonPress::SHORT_PRESS;
|
new_state = ButtonPress::SHORT_PRESS;
|
||||||
|
|
||||||
time_pressed_down_ = 0;
|
time_pressed_down_ = 0;
|
||||||
}
|
}
|
||||||
|
else if (is_pressed && was_pressed_)
|
||||||
|
{
|
||||||
|
if (current_state_ == ButtonPress::NOT_PRESSED && time_held > TIMEOUT_LONG_PRESS)
|
||||||
|
new_state = ButtonPress::LONG_PRESS;
|
||||||
|
}
|
||||||
|
|
||||||
was_pressed_ = is_pressed;
|
was_pressed_ = is_pressed;
|
||||||
current_state_ = new_state;
|
current_state_ = new_state;
|
||||||
|
|||||||
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,9 +8,9 @@
|
|||||||
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#ifndef SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
#define SKULLC_PERIPHERALS_HAL_ST_HPP_
|
||||||
|
|
||||||
#include <main.h>
|
#ifdef SKULLC_USE_HAL_ST
|
||||||
|
|
||||||
#include "peripherals_utility.hpp"
|
#include <main.h>
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
@ -96,9 +96,15 @@ struct Gpio
|
|||||||
};
|
};
|
||||||
|
|
||||||
#define CREATE_GPIO(name) \
|
#define CREATE_GPIO(name) \
|
||||||
Peripherals::Hal::St::Gpio { name##_GPIO_Port, name##_Pin, false }
|
Peripherals::Hal::St::Gpio \
|
||||||
|
{ \
|
||||||
|
name##_GPIO_Port, name##_Pin, false \
|
||||||
|
}
|
||||||
#define CREATE_INV_GPIO(name) \
|
#define CREATE_INV_GPIO(name) \
|
||||||
Peripherals::Hal::St::Gpio { name##_GPIO_Port, name##_Pin, true }
|
Peripherals::Hal::St::Gpio \
|
||||||
|
{ \
|
||||||
|
name##_GPIO_Port, name##_Pin, true \
|
||||||
|
}
|
||||||
|
|
||||||
#endif// HAL_GPIO_MODULE_ENABLED
|
#endif// HAL_GPIO_MODULE_ENABLED
|
||||||
|
|
||||||
@ -280,7 +286,7 @@ inline HAL_StatusTypeDef uartTransmitDma(UART_HandleTypeDef *huart, std::uint8_t
|
|||||||
return HAL_UART_Transmit_DMA(huart, data, size);
|
return HAL_UART_Transmit_DMA(huart, data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}// namespace _Details
|
||||||
|
|
||||||
using UartInterface =
|
using UartInterface =
|
||||||
SerialInterface<UART_HandleTypeDef, _Details::uartTransmit, HAL_UART_Receive>;
|
SerialInterface<UART_HandleTypeDef, _Details::uartTransmit, HAL_UART_Receive>;
|
||||||
@ -339,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_ */
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "peripherals_imu.hpp"
|
#include "peripherals_imu.hpp"
|
||||||
#include "peripherals_utility.hpp"
|
|
||||||
|
|
||||||
namespace Peripherals
|
namespace Peripherals
|
||||||
{
|
{
|
||||||
|
|||||||
@ -54,7 +54,8 @@ public:
|
|||||||
{
|
{
|
||||||
left_.forward.setCompare(left);
|
left_.forward.setCompare(left);
|
||||||
left_.backward.setCompare(0);
|
left_.backward.setCompare(0);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
left_.forward.setCompare(0);
|
left_.forward.setCompare(0);
|
||||||
left_.backward.setCompare(-1 * left);
|
left_.backward.setCompare(-1 * left);
|
||||||
@ -64,7 +65,8 @@ public:
|
|||||||
{
|
{
|
||||||
right_.forward.setCompare(right);
|
right_.forward.setCompare(right);
|
||||||
right_.backward.setCompare(0);
|
right_.backward.setCompare(0);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
right_.forward.setCompare(0);
|
right_.forward.setCompare(0);
|
||||||
right_.backward.setCompare(-1 * right);
|
right_.backward.setCompare(-1 * right);
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* peripherals_utility.hpp
|
|
||||||
*
|
|
||||||
* Created on: Feb 24, 2021
|
|
||||||
* Author: erki
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SKULLC_PERIPHERALS_UTILITY_HPP_
|
|
||||||
#define SKULLC_PERIPHERALS_UTILITY_HPP_
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <cstring>
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
namespace Peripherals
|
|
||||||
{
|
|
||||||
|
|
||||||
#define SKULLC_CONCAT_IMPL(x, y) x##y
|
|
||||||
#define SKULLC_CONCAT(x, y) SKULLC_CONCAT_IMPL(x, y)
|
|
||||||
#define SKULLC_TAG struct SKULLC_CONCAT(SkullCTag_, __COUNTER__)
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
constexpr const T& clamp(const T& v, const T& lo, const T& hi)
|
|
||||||
{
|
|
||||||
if (v > hi)
|
|
||||||
return hi;
|
|
||||||
else if (v < lo)
|
|
||||||
return lo;
|
|
||||||
else
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
T byteToTypeBE(const std::uint8_t a[N])
|
|
||||||
{
|
|
||||||
T t(0);
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < N; i++)
|
|
||||||
{
|
|
||||||
t |= a[i] << (i * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, std::size_t N>
|
|
||||||
T byteToTypeLE(const std::uint8_t a[N])
|
|
||||||
{
|
|
||||||
T t(0);
|
|
||||||
|
|
||||||
for (std::size_t i = N; i >= 0; i--)
|
|
||||||
{
|
|
||||||
t |= a[i] << ((i - 1) * 8);
|
|
||||||
}
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
constexpr T zeroInitialized()
|
|
||||||
{
|
|
||||||
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
|
||||||
|
|
||||||
T t;
|
|
||||||
std::memset(&t, 0, sizeof(T));
|
|
||||||
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
}// namespace Peripherals
|
|
||||||
|
|
||||||
#endif /* SKULLC_PERIPHERALS_UTILITY_HPP_ */
|
|
||||||
@ -1,10 +1,16 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
option(TESTS_WITH_SANITIZERS "Enable sanitizers for tests." ON)
|
option(SKULLC_TESTS_WITH_SANITIZERS "Enable sanitizers for tests." ON)
|
||||||
|
|
||||||
find_package(Catch2 REQUIRED)
|
include(FetchContent)
|
||||||
|
|
||||||
if(TESTS_WITH_SANITIZERS)
|
FetchContent_Declare(Catch2
|
||||||
|
GIT_REPOSITORY https://github.com/catchorg/Catch2.git
|
||||||
|
GIT_TAG v2.13.8
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(Catch2)
|
||||||
|
|
||||||
|
if(SKULLC_TESTS_WITH_SANITIZERS)
|
||||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
||||||
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=address -fsanitize=undefined -fno-sanitize-recover")
|
||||||
endif()
|
endif()
|
||||||
@ -18,7 +24,14 @@ add_executable(tests
|
|||||||
rand.cpp
|
rand.cpp
|
||||||
fixedpoint.cpp
|
fixedpoint.cpp
|
||||||
pixelbuffer.cpp
|
pixelbuffer.cpp
|
||||||
pixelbuffer_effects.cpp function.cpp)
|
pixelbuffer_effects.cpp
|
||||||
|
function.cpp
|
||||||
|
assert_ndebug.cpp
|
||||||
|
assert.cpp
|
||||||
|
enum_helpers.cpp
|
||||||
|
bytes.cpp
|
||||||
|
filters.cpp
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(tests
|
target_link_libraries(tests
|
||||||
PUBLIC
|
PUBLIC
|
||||||
@ -28,6 +41,12 @@ target_link_libraries(tests
|
|||||||
Catch2::Catch2
|
Catch2::Catch2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_target_properties(tests
|
||||||
|
PROPERTIES
|
||||||
|
CXX_STANDARD 17
|
||||||
|
)
|
||||||
|
|
||||||
|
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/contrib)
|
||||||
include(CTest)
|
include(CTest)
|
||||||
include(Catch)
|
include(Catch)
|
||||||
catch_discover_tests(tests)
|
catch_discover_tests(tests)
|
||||||
68
Tests/assert.cpp
Normal file
68
Tests/assert.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 29.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#undef NDEBUG
|
||||||
|
#define NDEBUG_WAS_SET
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "utility_assert.hpp"
|
||||||
|
|
||||||
|
#ifdef NDEBUG_WAS_SET
|
||||||
|
#define NDEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bool assert_flag = false;
|
||||||
|
|
||||||
|
void assertCallback(const char*, const char*, const int)
|
||||||
|
{
|
||||||
|
assert_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
TEST_CASE("Assert debug without NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
SKULLC_ASSERT_DEBUG(false);
|
||||||
|
|
||||||
|
CHECK(assert_flag == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Assert debug without NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
SKULLC_ASSERT_DEBUG(true);
|
||||||
|
|
||||||
|
CHECK(assert_flag == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Assert safe without NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
SKULLC_ASSERT_SAFE(false);
|
||||||
|
|
||||||
|
CHECK(assert_flag == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Assert safe without NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
SKULLC_ASSERT_SAFE(true);
|
||||||
|
|
||||||
|
CHECK(assert_flag == false);
|
||||||
|
}
|
||||||
62
Tests/assert_ndebug.cpp
Normal file
62
Tests/assert_ndebug.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 29.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define NDEBUG
|
||||||
|
#define NDEBUG_WAS_NOT_DEFINED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "utility_assert.hpp"
|
||||||
|
|
||||||
|
#ifdef NDEBUG_WAS_NOT_DEFINED
|
||||||
|
#undef NDEBUG
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
bool assert_flag = false;
|
||||||
|
|
||||||
|
void assertCallback(const char*, const char*, const int)
|
||||||
|
{
|
||||||
|
assert_flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
TEST_CASE("Assert debug with NDEBUG is a null-opt.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
int check_number = 0;
|
||||||
|
SKULLC_ASSERT_DEBUG(check_number++ == 0);
|
||||||
|
|
||||||
|
CHECK(check_number == 0);
|
||||||
|
CHECK(assert_flag == false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Assert safe with NDEBUG gets triggered if check is false.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
int check_number = 0;
|
||||||
|
SKULLC_ASSERT_SAFE(check_number++ == 1);
|
||||||
|
|
||||||
|
CHECK(check_number == 1);
|
||||||
|
CHECK(assert_flag == true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Assert safe with NDEBUG doesn't get triggered if check is true.", "[utility],[assert]")
|
||||||
|
{
|
||||||
|
assert_flag = false;
|
||||||
|
Utility::Assert::setHandler(assertCallback);
|
||||||
|
|
||||||
|
SKULLC_ASSERT_SAFE(true);
|
||||||
|
|
||||||
|
CHECK(assert_flag == false);
|
||||||
|
}
|
||||||
@ -109,7 +109,6 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
|
|||||||
SECTION("Long press is identified properly.")
|
SECTION("Long press is identified properly.")
|
||||||
{
|
{
|
||||||
HAL::millis = Button::TIMEOUT_LONG_PRESS + 2;
|
HAL::millis = Button::TIMEOUT_LONG_PRESS + 2;
|
||||||
Gpio::set = false;
|
|
||||||
|
|
||||||
button.update();
|
button.update();
|
||||||
|
|
||||||
@ -121,6 +120,16 @@ TEST_CASE("Button reads presses properly.", "[peripherals],[button]")
|
|||||||
button.update();
|
button.update();
|
||||||
|
|
||||||
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
||||||
|
|
||||||
|
SECTION("Releasing keeps NOT_PRESSED state.")
|
||||||
|
{
|
||||||
|
HAL::millis += 1;
|
||||||
|
Gpio::set = false;
|
||||||
|
|
||||||
|
button.update();
|
||||||
|
|
||||||
|
REQUIRE(button.getState() == Peripherals::ButtonPress::NOT_PRESSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
68
Tests/bytes.cpp
Normal file
68
Tests/bytes.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 10/12/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "utility_bytes.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("zeroInitialized creates an appropriate struct.")
|
||||||
|
{
|
||||||
|
struct TestStruct
|
||||||
|
{
|
||||||
|
int a;
|
||||||
|
char b;
|
||||||
|
int c;
|
||||||
|
};
|
||||||
|
|
||||||
|
SECTION("Return by value variant returns a struct where all members are 0.")
|
||||||
|
{
|
||||||
|
const TestStruct test = Utility::zeroInitialized<TestStruct>();
|
||||||
|
REQUIRE(test.a == 0);
|
||||||
|
REQUIRE(test.b == 0);
|
||||||
|
REQUIRE(test.c == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Pass by reference variant initializes the struct to all 0's.")
|
||||||
|
{
|
||||||
|
TestStruct test;
|
||||||
|
Utility::zeroInitialized(test);
|
||||||
|
|
||||||
|
REQUIRE(test.a == 0);
|
||||||
|
REQUIRE(test.b == 0);
|
||||||
|
REQUIRE(test.c == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Pass by pointer variant initializes the struct to all 0's.")
|
||||||
|
{
|
||||||
|
TestStruct test;
|
||||||
|
Utility::zeroInitialized(&test);
|
||||||
|
|
||||||
|
REQUIRE(test.a == 0);
|
||||||
|
REQUIRE(test.b == 0);
|
||||||
|
REQUIRE(test.c == 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("arrayToType constructs a type appropriately.")
|
||||||
|
{
|
||||||
|
const std::uint32_t initial_value = 0x12345678;
|
||||||
|
|
||||||
|
SECTION("Using C-arrays the operation works appropriately.")
|
||||||
|
{
|
||||||
|
std::uint8_t raw_data[4] = {0};
|
||||||
|
std::memcpy(raw_data, &initial_value, 4);
|
||||||
|
|
||||||
|
const std::uint32_t output = Utility::arrayToType<std::uint32_t, 4>(raw_data);
|
||||||
|
REQUIRE(output == initial_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Using C-arrays the operation works appropriately.")
|
||||||
|
{
|
||||||
|
std::array<std::uint8_t, 4> raw_data = {0, 0, 0, 0};
|
||||||
|
std::memcpy(raw_data.data(), &initial_value, 4);
|
||||||
|
|
||||||
|
const std::uint32_t output = Utility::arrayToType<std::uint32_t>(raw_data);
|
||||||
|
REQUIRE(output == initial_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
41
Tests/enum_helpers.cpp
Normal file
41
Tests/enum_helpers.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 15.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "utility_enum_helpers.hpp"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class TestEnum : unsigned
|
||||||
|
{
|
||||||
|
FLAG_0 = 1,
|
||||||
|
FLAG_1 = 2,
|
||||||
|
FLAG_2 = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
SKULLC_ENUM_DECLARE_BITFLAG_OPERATORS(TestEnum)
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
TEST_CASE("OR operator works as expected.", "[utility],[enum_helpers]")
|
||||||
|
{
|
||||||
|
const TestEnum a = TestEnum::FLAG_0;
|
||||||
|
const TestEnum b = TestEnum::FLAG_1;
|
||||||
|
|
||||||
|
const TestEnum sum = a | b;
|
||||||
|
CHECK(unsigned(sum) == 3u);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("AND operator works as expected.", "[utility],[enum_helpers]")
|
||||||
|
{
|
||||||
|
const TestEnum a = TestEnum::FLAG_0 | TestEnum::FLAG_1;
|
||||||
|
|
||||||
|
const TestEnum masked_1 = a & TestEnum::FLAG_0;
|
||||||
|
CHECK(masked_1 == TestEnum::FLAG_0);
|
||||||
|
|
||||||
|
const TestEnum masked_2 = a & TestEnum::FLAG_2;
|
||||||
|
CHECK(masked_2 != TestEnum::FLAG_2);
|
||||||
|
}
|
||||||
191
Tests/filters.cpp
Normal file
191
Tests/filters.cpp
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 12/11/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
#include "utility_filters.hpp"
|
||||||
|
|
||||||
|
TEST_CASE("LowPassFilter works as expected.")
|
||||||
|
{
|
||||||
|
const int threshold = 50;
|
||||||
|
Utility::LowPassFilter<int> filter{threshold};
|
||||||
|
|
||||||
|
SECTION("Initial value is 0.")
|
||||||
|
{
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Passing a number higher than threshold gets filtered.")
|
||||||
|
{
|
||||||
|
filter.update(60);
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
|
||||||
|
SECTION("Filter updates state as expected.")
|
||||||
|
{
|
||||||
|
filter.update(40);
|
||||||
|
REQUIRE(filter.currentValue() == 40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Passing a number lower than threshold passes filter.")
|
||||||
|
{
|
||||||
|
filter.update(40);
|
||||||
|
REQUIRE(filter.currentValue() == 40);
|
||||||
|
|
||||||
|
SECTION("Filter retains state as expected.")
|
||||||
|
{
|
||||||
|
filter.update(60);
|
||||||
|
REQUIRE(filter.currentValue() == 40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("HighPassFilter works as expected.")
|
||||||
|
{
|
||||||
|
const int threshold = 50;
|
||||||
|
Utility::HighPassFilter<int> filter{threshold};
|
||||||
|
|
||||||
|
SECTION("Initial value is 0.")
|
||||||
|
{
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Passing a number higher than threshold passes filter.")
|
||||||
|
{
|
||||||
|
filter.update(60);
|
||||||
|
REQUIRE(filter.currentValue() == 60);
|
||||||
|
|
||||||
|
SECTION("Filter retains state as expected.")
|
||||||
|
{
|
||||||
|
filter.update(40);
|
||||||
|
REQUIRE(filter.currentValue() == 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Passing a number lower than threshold gets filtered.")
|
||||||
|
{
|
||||||
|
filter.update(40);
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
|
||||||
|
SECTION("Filter updates state as expected.")
|
||||||
|
{
|
||||||
|
filter.update(60);
|
||||||
|
REQUIRE(filter.currentValue() == 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Two filters can be linked together.")
|
||||||
|
{
|
||||||
|
const int threshold_low = 40;
|
||||||
|
const int threshold_high = 50;
|
||||||
|
|
||||||
|
Utility::LowPassFilter<int> low_pass{threshold_high};
|
||||||
|
Utility::HighPassFilter<int> high_pass{threshold_low};
|
||||||
|
|
||||||
|
high_pass.assignPrecedingFilter(low_pass);
|
||||||
|
|
||||||
|
SECTION("Initial value is 0.")
|
||||||
|
{
|
||||||
|
REQUIRE(high_pass.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Values outside of band are filtered out.")
|
||||||
|
{
|
||||||
|
high_pass.update(30);
|
||||||
|
REQUIRE(high_pass.currentValue() == 0);
|
||||||
|
|
||||||
|
high_pass.update(60);
|
||||||
|
REQUIRE(high_pass.currentValue() == 0);
|
||||||
|
|
||||||
|
SECTION("Filter updates with in-band value.")
|
||||||
|
{
|
||||||
|
high_pass.update(45);
|
||||||
|
REQUIRE(high_pass.currentValue() == 45);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Values inside of band are passed through.")
|
||||||
|
{
|
||||||
|
high_pass.update(44);
|
||||||
|
REQUIRE(high_pass.currentValue() == 44);
|
||||||
|
|
||||||
|
high_pass.update(46);
|
||||||
|
REQUIRE(high_pass.currentValue() == 46);
|
||||||
|
|
||||||
|
SECTION("Out of band values are filtered out.")
|
||||||
|
{
|
||||||
|
high_pass.update(30);
|
||||||
|
REQUIRE(high_pass.currentValue() == 46);
|
||||||
|
|
||||||
|
high_pass.update(60);
|
||||||
|
REQUIRE(high_pass.currentValue() == 46);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Decoupling filters works as expected.")
|
||||||
|
{
|
||||||
|
high_pass.update(60);
|
||||||
|
REQUIRE(high_pass.currentValue() == 0);
|
||||||
|
|
||||||
|
high_pass.clearPrecedingFilter();
|
||||||
|
high_pass.update(60);
|
||||||
|
REQUIRE(high_pass.currentValue() == 60);
|
||||||
|
|
||||||
|
high_pass.update(30);
|
||||||
|
REQUIRE(high_pass.currentValue() == 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Median filter works as expected.")
|
||||||
|
{
|
||||||
|
Utility::MedianFilter<int, 5> filter;
|
||||||
|
|
||||||
|
SECTION("Initial value is 0.")
|
||||||
|
{
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("The median is filtered appropriately.")
|
||||||
|
{
|
||||||
|
std::array<int, 5> data = {5, 1, 4, 3, 2};
|
||||||
|
std::array<int, 5> expected_median = {0, 0, 1, 3, 3};
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
{
|
||||||
|
const int x = data[i];
|
||||||
|
filter.update(x);
|
||||||
|
REQUIRE(filter.currentValue() == expected_median[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Rolling average filter works as expected.")
|
||||||
|
{
|
||||||
|
Utility::RollingAverageFilter<int> filter{5};
|
||||||
|
|
||||||
|
SECTION("Initial value is 0.")
|
||||||
|
{
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Value below step size is filtered out for int filter.")
|
||||||
|
{
|
||||||
|
filter.update(4);
|
||||||
|
REQUIRE(filter.currentValue() == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("Value is updated appropriately.")
|
||||||
|
{
|
||||||
|
filter.update(20);
|
||||||
|
REQUIRE(filter.currentValue() == 20 / 5);
|
||||||
|
|
||||||
|
SECTION("Saturation is reached appropriately")
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 5 * 2 - 1; i++)
|
||||||
|
filter.update(20);
|
||||||
|
|
||||||
|
REQUIRE(filter.currentValue() == 20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,7 @@
|
|||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
#include <utility_function.hpp>
|
#include <utility_function.hpp>
|
||||||
|
#include <utility_tag.hpp>
|
||||||
|
|
||||||
TEST_CASE("Function calls function appropriately.", "[utility],[function]")
|
TEST_CASE("Function calls function appropriately.", "[utility],[function]")
|
||||||
{
|
{
|
||||||
@ -38,6 +39,26 @@ TEST_CASE("Function passes arguments appropriately.", "[utility],[function]")
|
|||||||
REQUIRE(bool_to_set == true);
|
REQUIRE(bool_to_set == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Function works with toStaticFunction.", "[utility],[function]")
|
||||||
|
{
|
||||||
|
static int int_to_set = 0;
|
||||||
|
static bool bool_to_set = false;
|
||||||
|
|
||||||
|
auto func_to_call = [](const int i, const bool b) {
|
||||||
|
int_to_set = i;
|
||||||
|
bool_to_set = b;
|
||||||
|
};
|
||||||
|
|
||||||
|
Utility::Function<void(int, bool)> function(func_to_call);
|
||||||
|
using signature = void (*)(int, bool);
|
||||||
|
|
||||||
|
signature func_pointer = function.toStaticFunction<SKULLC_TAG>();
|
||||||
|
func_pointer(10, true);
|
||||||
|
|
||||||
|
REQUIRE(int_to_set == 10);
|
||||||
|
REQUIRE(bool_to_set == true);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("FunctionOwned calls function appropriately.", "[utility],[function]")
|
TEST_CASE("FunctionOwned calls function appropriately.", "[utility],[function]")
|
||||||
{
|
{
|
||||||
struct S
|
struct S
|
||||||
@ -82,3 +103,29 @@ TEST_CASE("FunctionOwned passes arguments appropriately.", "[utility],[function]
|
|||||||
REQUIRE(subject.int_to_set == 10);
|
REQUIRE(subject.int_to_set == 10);
|
||||||
REQUIRE(subject.bool_to_set == false);
|
REQUIRE(subject.bool_to_set == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("FunctionOwned works with toStaticFunction.", "[utility],[function]")
|
||||||
|
{
|
||||||
|
struct S
|
||||||
|
{
|
||||||
|
int int_to_set = 0;
|
||||||
|
bool bool_to_set = true;
|
||||||
|
|
||||||
|
void toCall(const int i, const bool b)
|
||||||
|
{
|
||||||
|
int_to_set = i;
|
||||||
|
bool_to_set = b;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
S subject;
|
||||||
|
|
||||||
|
Utility::FunctionOwned<S, void(int, bool)> function(subject, &S::toCall);
|
||||||
|
using signature = void (*)(int, bool);
|
||||||
|
|
||||||
|
signature func_pointer = function.toStaticFunction<SKULLC_TAG>();
|
||||||
|
func_pointer(10, true);
|
||||||
|
|
||||||
|
REQUIRE(subject.int_to_set == 10);
|
||||||
|
REQUIRE(subject.bool_to_set == true);
|
||||||
|
}
|
||||||
|
|||||||
@ -15,15 +15,15 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
|
|
||||||
#include "threads_primitivethread.hpp"
|
#include <threads_actor_thread.hpp>
|
||||||
#include "threads_signal.hpp"
|
#include <threads_iactor.hpp>
|
||||||
#include "threads_timer.hpp"
|
#include <threads_signal.hpp>
|
||||||
|
|
||||||
namespace Threads
|
namespace Threads
|
||||||
{
|
{
|
||||||
|
|
||||||
template<typename CRTP, typename... Ts>
|
template<typename CRTP, typename... Ts>
|
||||||
class Actor
|
class Actor : public IActor
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using value_type = std::variant<Ts...>;
|
using value_type = std::variant<Ts...>;
|
||||||
@ -31,14 +31,11 @@ public:
|
|||||||
static_assert(std::is_default_constructible_v<value_type>, "Ts must be default constructible.");
|
static_assert(std::is_default_constructible_v<value_type>, "Ts must be default constructible.");
|
||||||
static_assert(std::is_trivially_copyable_v<value_type>, "Ts must be trivially copyable.");
|
static_assert(std::is_trivially_copyable_v<value_type>, "Ts must be trivially copyable.");
|
||||||
|
|
||||||
Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
Actor() : IActor(sizeof(value_type)),
|
||||||
: thread_(&Actor<CRTP, Ts...>::threadHandler_, this, name, priority, stack_size),
|
signal_(this)
|
||||||
msg_queue_(xQueueCreate(queue_size, sizeof(value_type))), signal_(this)
|
{}
|
||||||
{
|
|
||||||
assert(msg_queue_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Actor() {}
|
~Actor() override {}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct Signal : Signallable<T>
|
struct Signal : Signallable<T>
|
||||||
@ -59,17 +56,13 @@ public:
|
|||||||
void emit(const T& data) override
|
void emit(const T& data) override
|
||||||
{
|
{
|
||||||
parent::value_type to_send = data;
|
parent::value_type to_send = data;
|
||||||
xQueueSend(q_->msg_queue_, &to_send, 0);
|
q_->dispatchEvent(to_send);
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseType_t emitFromIsr(const T& data) override
|
BaseType_t emitFromIsr(const T& data) override
|
||||||
{
|
{
|
||||||
parent::value_type to_send = data;
|
parent::value_type to_send = data;
|
||||||
BaseType_t was_awoken = pdFALSE;
|
return q_->dispatchEventFromIsr(to_send);
|
||||||
|
|
||||||
xQueueSendFromISR(q_->msg_queue_, &to_send, &was_awoken);
|
|
||||||
|
|
||||||
return was_awoken;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -97,13 +90,12 @@ public:
|
|||||||
return &signal_;
|
return &signal_;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
void dispatchSignal(const char* data) final
|
||||||
virtual void init() = 0;
|
{
|
||||||
|
dispatchImpl_(data);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PrimitiveThread thread_;
|
|
||||||
QueueHandle_t msg_queue_;
|
|
||||||
|
|
||||||
struct Visitor_
|
struct Visitor_
|
||||||
{
|
{
|
||||||
using parent = Actor<CRTP, Ts...>;
|
using parent = Actor<CRTP, Ts...>;
|
||||||
@ -121,113 +113,15 @@ private:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void operator()()
|
void dispatchImpl_(const char* data)
|
||||||
{
|
{
|
||||||
init();
|
const value_type* signal_data = reinterpret_cast<const value_type*>(data);
|
||||||
|
std::visit(Visitor_{this}, *signal_data);
|
||||||
value_type data{};
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY);
|
|
||||||
|
|
||||||
if (got == pdTRUE)
|
|
||||||
{
|
|
||||||
std::visit(Visitor_{this}, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void threadHandler_(void* d)
|
|
||||||
{
|
|
||||||
auto* dd = static_cast<Actor<CRTP, Ts...>*>(d);
|
|
||||||
(*dd)();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Signal<value_type> signal_;
|
Signal<value_type> signal_;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename CRTP>
|
|
||||||
class Actor<CRTP, void>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using value_type = void;
|
|
||||||
|
|
||||||
Actor(const std::uint32_t queue_size, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
|
||||||
: thread_([](void* d) {
|
|
||||||
auto* dd = static_cast<Actor<CRTP, value_type>*>(d);
|
|
||||||
(*dd)();
|
|
||||||
},
|
|
||||||
this, name, priority, stack_size),
|
|
||||||
msg_queue_(xQueueCreate(queue_size, sizeof(int))), signal_(*this)
|
|
||||||
{
|
|
||||||
assert(msg_queue_);
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~Actor() {}
|
|
||||||
|
|
||||||
Signallable<value_type>* getSignal()
|
|
||||||
{
|
|
||||||
return &signal_;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void init() = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
PrimitiveThread thread_;
|
|
||||||
QueueHandle_t msg_queue_;
|
|
||||||
|
|
||||||
void operator()()
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
int data;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const BaseType_t got = xQueueReceive(msg_queue_, &data, portMAX_DELAY);
|
|
||||||
|
|
||||||
if (got == pdTRUE)
|
|
||||||
{
|
|
||||||
auto* crtp = static_cast<CRTP*>(this);
|
|
||||||
crtp->onSignal();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
friend struct Signal_;
|
|
||||||
|
|
||||||
struct Signal_ : Signallable<value_type>
|
|
||||||
{
|
|
||||||
using parent = Actor<CRTP, void>;
|
|
||||||
parent& q;
|
|
||||||
|
|
||||||
explicit Signal_(parent& q)
|
|
||||||
: q(q)
|
|
||||||
{}
|
|
||||||
|
|
||||||
void emit() override
|
|
||||||
{
|
|
||||||
const int data = 0;
|
|
||||||
|
|
||||||
xQueueSend(q.msg_queue_, &data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseType_t emitFromIsr() override
|
|
||||||
{
|
|
||||||
const int data = 0;
|
|
||||||
|
|
||||||
BaseType_t was_awoken = pdFALSE;
|
|
||||||
xQueueSendFromISR(q.msg_queue_, &data, &was_awoken);
|
|
||||||
|
|
||||||
return was_awoken;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Signal_ signal_;
|
|
||||||
};
|
|
||||||
|
|
||||||
}// namespace Threads
|
}// namespace Threads
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
50
Threads/Inc/threads_actor_thread.hpp
Normal file
50
Threads/Inc/threads_actor_thread.hpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* threads_actor_thread.hpp
|
||||||
|
*
|
||||||
|
* Created on: Nov 13, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKULLC_THREADS_ACTOR_THREAD_HPP_
|
||||||
|
#define SKULLC_THREADS_ACTOR_THREAD_HPP_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <freertos_os2.h>
|
||||||
|
#include <queue.h>
|
||||||
|
|
||||||
|
#include <threads_thread.hpp>
|
||||||
|
|
||||||
|
namespace Threads
|
||||||
|
{
|
||||||
|
|
||||||
|
class IActor;
|
||||||
|
|
||||||
|
class ActorThread : public Thread<ActorThread>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ActorThread(const std::size_t queue_length, const char* name, const osPriority_t priority, const std::uint32_t stack_size);
|
||||||
|
|
||||||
|
void operator()();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_();
|
||||||
|
void doMessageLoop_();
|
||||||
|
|
||||||
|
friend class IActor;
|
||||||
|
std::size_t acceptActor_(IActor* actor);
|
||||||
|
|
||||||
|
std::size_t queue_length_;
|
||||||
|
std::size_t queue_item_length_ = 0;
|
||||||
|
|
||||||
|
QueueHandle_t queue_ = nullptr;
|
||||||
|
char* rx_buffer_ = nullptr;
|
||||||
|
char* tx_buffer_ = nullptr;
|
||||||
|
std::array<IActor*, 32> actors_;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace Threads
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* SKULLC_THREADS_ACTOR_THREAD_HPP_ */
|
||||||
@ -65,7 +65,8 @@ public:
|
|||||||
if (!notified)
|
if (!notified)
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return current_value_;
|
return current_value_;
|
||||||
}
|
}
|
||||||
@ -78,7 +79,8 @@ public:
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return waiting_thread_->notify(0, eNoAction);
|
return waiting_thread_->notify(0, eNoAction);
|
||||||
}
|
}
|
||||||
@ -91,7 +93,8 @@ public:
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return {false, pdFALSE};
|
return {false, pdFALSE};
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
||||||
(void) discard;
|
(void) discard;
|
||||||
@ -158,7 +161,8 @@ struct ExclusiveSignal<void> : public Signallable<void>
|
|||||||
if (!waiting_thread_)
|
if (!waiting_thread_)
|
||||||
{
|
{
|
||||||
return pdFALSE;
|
return pdFALSE;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
auto const [discard, was_notified] = waiting_thread_->notifyFromIsr(0, eNoAction);
|
||||||
(void) discard;
|
(void) discard;
|
||||||
|
|||||||
68
Threads/Inc/threads_iactor.hpp
Normal file
68
Threads/Inc/threads_iactor.hpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* threads_iactor.hpp
|
||||||
|
*
|
||||||
|
* Created on: Nov 14, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SKULLC_THREADS_IACTOR_HPP_
|
||||||
|
#define SKULLC_THREADS_IACTOR_HPP_
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <freertos_os2.h>
|
||||||
|
|
||||||
|
namespace Threads
|
||||||
|
{
|
||||||
|
|
||||||
|
class ActorThread;
|
||||||
|
|
||||||
|
class IActor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IActor(const std::size_t signal_size)
|
||||||
|
: signal_size_(signal_size)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~IActor() {}
|
||||||
|
|
||||||
|
virtual void init() = 0;
|
||||||
|
virtual void dispatchSignal(const char* data) = 0;
|
||||||
|
|
||||||
|
void moveToThread(ActorThread* thread);
|
||||||
|
|
||||||
|
std::size_t actorIndex() const
|
||||||
|
{
|
||||||
|
return actor_index_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t signalSize() const
|
||||||
|
{
|
||||||
|
return signal_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template<typename T>
|
||||||
|
void dispatchEvent(const T& data)
|
||||||
|
{
|
||||||
|
dispatchEventImpl_(&data, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
BaseType_t dispatchEventFromIsr(const T& data)
|
||||||
|
{
|
||||||
|
return dispatchEventFromIsrImpl_(&data, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ActorThread* parent_thread_ = nullptr;
|
||||||
|
std::size_t actor_index_ = 0;
|
||||||
|
std::size_t signal_size_;
|
||||||
|
|
||||||
|
void dispatchEventImpl_(const void* data, const std::size_t data_size);
|
||||||
|
BaseType_t dispatchEventFromIsrImpl_(const void* data, const std::size_t data_size);
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace Threads
|
||||||
|
|
||||||
|
#endif /* SKULLC_THREADS_IACTOR_HPP_ */
|
||||||
@ -11,9 +11,9 @@
|
|||||||
#include <cmsis_os.h>
|
#include <cmsis_os.h>
|
||||||
#include <freertos_os2.h>
|
#include <freertos_os2.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace Threads
|
namespace Threads
|
||||||
{
|
{
|
||||||
|
|||||||
110
Threads/Src/threads_actor_thread.cpp
Normal file
110
Threads/Src/threads_actor_thread.cpp
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* threads_actor_thread.cpp
|
||||||
|
*
|
||||||
|
* Created on: Nov 13, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "threads_actor_thread.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <threads_iactor.hpp>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
struct SignalMessage
|
||||||
|
{
|
||||||
|
std::size_t actor_index;
|
||||||
|
const char* data;
|
||||||
|
|
||||||
|
static SignalMessage fromData(char* data)
|
||||||
|
{
|
||||||
|
SignalMessage message;
|
||||||
|
std::memcpy(&message.actor_index, data, sizeof(message.actor_index));
|
||||||
|
message.data = data + sizeof(message.actor_index);
|
||||||
|
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
namespace Threads
|
||||||
|
{
|
||||||
|
|
||||||
|
ActorThread::ActorThread(const std::size_t queue_length, const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
||||||
|
: Thread<ActorThread>(name, priority, stack_size), queue_length_(queue_length), queue_(nullptr)
|
||||||
|
{
|
||||||
|
actors_.fill(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorThread::operator()()
|
||||||
|
{
|
||||||
|
init_();
|
||||||
|
|
||||||
|
osThreadYield();
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
doMessageLoop_();
|
||||||
|
|
||||||
|
osThreadYield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorThread::init_()
|
||||||
|
{
|
||||||
|
for (IActor* actor : actors_)
|
||||||
|
{
|
||||||
|
if (actor != nullptr)
|
||||||
|
{
|
||||||
|
queue_item_length_ = std::max(queue_item_length_, actor->signalSize());
|
||||||
|
|
||||||
|
actor->init();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
queue_ = xQueueCreate(queue_length_, queue_item_length_ + sizeof(std::size_t));
|
||||||
|
assert(queue_ != nullptr);
|
||||||
|
|
||||||
|
rx_buffer_ = new char[queue_item_length_ + sizeof(std::size_t)];
|
||||||
|
tx_buffer_ = new char[queue_item_length_ + sizeof(std::size_t)];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ActorThread::doMessageLoop_()
|
||||||
|
{
|
||||||
|
const BaseType_t has_data = xQueueReceive(queue_, rx_buffer_, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (has_data == pdTRUE)
|
||||||
|
{
|
||||||
|
const SignalMessage message = SignalMessage::fromData(rx_buffer_);
|
||||||
|
assert(message.actor_index <= actors_.size());
|
||||||
|
|
||||||
|
IActor* actor = actors_[message.actor_index];
|
||||||
|
assert(actor != nullptr);
|
||||||
|
|
||||||
|
actor->dispatchSignal(message.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t ActorThread::acceptActor_(IActor* actor)
|
||||||
|
{
|
||||||
|
for (auto it = actors_.begin(); it != actors_.end(); ++it)
|
||||||
|
{
|
||||||
|
if (*it == nullptr)
|
||||||
|
{
|
||||||
|
*it = actor;
|
||||||
|
return std::distance(actors_.begin(), it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Threads
|
||||||
55
Threads/Src/threads_iactor.cpp
Normal file
55
Threads/Src/threads_iactor.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* threads_iactor.cpp
|
||||||
|
*
|
||||||
|
* Created on: Nov 14, 2021
|
||||||
|
* Author: erki
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "threads_iactor.hpp"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <queue.h>
|
||||||
|
|
||||||
|
#include "threads_actor_thread.hpp"
|
||||||
|
|
||||||
|
namespace Threads
|
||||||
|
{
|
||||||
|
|
||||||
|
void IActor::moveToThread(ActorThread* thread)
|
||||||
|
{
|
||||||
|
actor_index_ = thread->acceptActor_(this);
|
||||||
|
parent_thread_ = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IActor::dispatchEventImpl_(const void* data, const std::size_t data_size)
|
||||||
|
{
|
||||||
|
taskENTER_CRITICAL();
|
||||||
|
|
||||||
|
std::memcpy(parent_thread_->tx_buffer_, &actor_index_, sizeof(std::size_t));
|
||||||
|
char* data_dest = parent_thread_->tx_buffer_ + sizeof(std::size_t);
|
||||||
|
std::memcpy(data_dest, data, data_size);
|
||||||
|
|
||||||
|
xQueueSend(parent_thread_->queue_, parent_thread_->tx_buffer_, 0);
|
||||||
|
|
||||||
|
taskEXIT_CRITICAL();
|
||||||
|
}
|
||||||
|
|
||||||
|
BaseType_t IActor::dispatchEventFromIsrImpl_(const void* data, const std::size_t data_size)
|
||||||
|
{
|
||||||
|
const auto isr_data = taskENTER_CRITICAL_FROM_ISR();
|
||||||
|
|
||||||
|
std::memcpy(parent_thread_->tx_buffer_, &actor_index_, sizeof(std::size_t));
|
||||||
|
char* data_dest = parent_thread_->tx_buffer_ + sizeof(std::size_t);
|
||||||
|
std::memcpy(data_dest, data, data_size);
|
||||||
|
|
||||||
|
BaseType_t task_awoken = pdFALSE;
|
||||||
|
xQueueSendFromISR(parent_thread_->queue_, parent_thread_->tx_buffer_, &task_awoken);
|
||||||
|
|
||||||
|
taskEXIT_CRITICAL_FROM_ISR(isr_data);
|
||||||
|
|
||||||
|
return task_awoken;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Threads
|
||||||
@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
#include "threads_primitivethread.hpp"
|
#include "threads_primitivethread.hpp"
|
||||||
|
|
||||||
#include "peripherals_utility.hpp"
|
#include "utility_bytes.hpp"
|
||||||
|
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
@ -101,14 +101,14 @@ PrimitiveThread::PrimitiveThread(osThreadFunc_t function, void* runner_data, con
|
|||||||
|
|
||||||
|
|
||||||
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
|
PrimitiveThread::PrimitiveThread(TaskHandle_t threadHandle)
|
||||||
: thread_id(static_cast<osThreadId_t>(threadHandle)), attributes(Peripherals::zeroInitialized<osThreadAttr_t>())
|
: thread_id(static_cast<osThreadId_t>(threadHandle)), attributes(Utility::zeroInitialized<osThreadAttr_t>())
|
||||||
{
|
{
|
||||||
assert(thread_id != nullptr);
|
assert(thread_id != nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
osThreadAttr_t PrimitiveThread::constructThreadAttrs_(const char* name, const osPriority_t priority, const std::uint32_t stack_size)
|
||||||
{
|
{
|
||||||
auto attrs = Peripherals::zeroInitialized<osThreadAttr_t>();
|
auto attrs = Utility::zeroInitialized<osThreadAttr_t>();
|
||||||
|
|
||||||
attrs.name = name;
|
attrs.name = name;
|
||||||
attrs.priority = priority;
|
attrs.priority = priority;
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
|
||||||
|
|
||||||
#if(WITH_HAL)
|
|
||||||
# set(additional_sources
|
|
||||||
# )
|
|
||||||
#endif()
|
|
||||||
|
|
||||||
add_library(utility STATIC
|
add_library(utility STATIC
|
||||||
Src/utility_logging.cpp
|
Src/utility_logging.cpp
|
||||||
Src/utility_rand.cpp
|
Src/utility_rand.cpp
|
||||||
|
Src/utility_assert.cpp
|
||||||
${additional_sources}
|
${additional_sources}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,5 +14,9 @@ target_include_directories(utility
|
|||||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:include>
|
||||||
)
|
)
|
||||||
|
set_target_properties(utility
|
||||||
|
PROPERTIES
|
||||||
|
CXX_STANDARD 17
|
||||||
|
)
|
||||||
|
|
||||||
skullc_install_packages(skullc utility ${version})
|
skullc_install_packages(skullc utility ${SKULLC_VERSION})
|
||||||
|
|||||||
33
Utility/Inc/utility_assert.hpp
Normal file
33
Utility/Inc/utility_assert.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 29.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_ASSERT_HPP_
|
||||||
|
#define SKULLC_UTILITY_ASSERT_HPP_
|
||||||
|
|
||||||
|
namespace Utility::Assert::Detail
|
||||||
|
{
|
||||||
|
|
||||||
|
void assertImpl(const char* expression, const char* file, const int line);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
#define SKULLC_ASSERT_DEBUG(e) (!(e) ? Utility::Assert::Detail::assertImpl(#e, __FILE__, __LINE__) : ((void) 0))
|
||||||
|
#else
|
||||||
|
#define SKULLC_ASSERT_DEBUG(e)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SKULLC_ASSERT_SAFE(e) (!(e) ? Utility::Assert::Detail::assertImpl(#e, __FILE__, __LINE__) : ((void) 0))
|
||||||
|
|
||||||
|
namespace Utility::Assert
|
||||||
|
{
|
||||||
|
|
||||||
|
using assert_cb = void (*)(const char* expression, const char* file, const int line);
|
||||||
|
|
||||||
|
void setHandler(assert_cb callback);
|
||||||
|
assert_cb getHandler();
|
||||||
|
|
||||||
|
}// namespace Utility::Assert
|
||||||
|
|
||||||
|
#endif// SKULLC_UTILITY_ASSERT_HPP_
|
||||||
69
Utility/Inc/utility_bytes.hpp
Normal file
69
Utility/Inc/utility_bytes.hpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 10/12/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_BYTES_HPP_
|
||||||
|
#define SKULLC_UTILITY_BYTES_HPP_
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
T arrayToType(const std::uint8_t raw[N])
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
static_assert(sizeof(T) == N, "The raw data array is not the same size as T.");
|
||||||
|
|
||||||
|
T t;
|
||||||
|
std::memcpy(&t, raw, sizeof(T));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
T arrayToType(const std::array<std::uint8_t, N> raw)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
static_assert(sizeof(T) == N, "The raw data array is not the same size as T.");
|
||||||
|
|
||||||
|
T t;
|
||||||
|
std::memcpy(&t, raw.data(), sizeof(T));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr T zeroInitialized()
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
|
||||||
|
T t;
|
||||||
|
std::memset(&t, 0, sizeof(T));
|
||||||
|
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void zeroInitialized(T& t)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
|
||||||
|
std::memset(&t, 0, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void zeroInitialized(T* t)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_default_constructible<T>::value, "Struct is not trivially default constructible.");
|
||||||
|
|
||||||
|
std::memset(t, 0, sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Utility
|
||||||
|
|
||||||
|
#endif//SKULLC_UTILITY_BYTES_HPP_
|
||||||
22
Utility/Inc/utility_enum_helpers.hpp
Normal file
22
Utility/Inc/utility_enum_helpers.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 15.07.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
||||||
|
#define SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#define SKULLC_ENUM_DECLARE_BITFLAG_OPERATORS(E) \
|
||||||
|
inline E operator|(const E& lhs, const E& rhs) \
|
||||||
|
{ \
|
||||||
|
using T = std::underlying_type_t<E>; \
|
||||||
|
return static_cast<E>(static_cast<T>(lhs) | static_cast<T>(rhs)); \
|
||||||
|
} \
|
||||||
|
inline E operator&(const E& lhs, const E& rhs) \
|
||||||
|
{ \
|
||||||
|
using T = std::underlying_type_t<E>; \
|
||||||
|
return static_cast<E>(static_cast<T>(lhs) & static_cast<T>(rhs)); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif//SKULLC_UTILITY_ENUM_HELPERS_HPP_
|
||||||
124
Utility/Inc/utility_filters.hpp
Normal file
124
Utility/Inc/utility_filters.hpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 12/11/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_FILTERS_HPP_
|
||||||
|
#define SKULLC_UTILITY_FILTERS_HPP_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include "utility_ifilter.hpp"
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class RollingAverageFilter : public IFilter<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
RollingAverageFilter() = delete;
|
||||||
|
explicit RollingAverageFilter(const std::size_t step)
|
||||||
|
: step_size_(step)
|
||||||
|
{}
|
||||||
|
|
||||||
|
T currentValue() const override
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateImpl_(const T& data) override
|
||||||
|
{
|
||||||
|
value_ -= value_ / step_size_;
|
||||||
|
value_ += data / step_size_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::size_t step_size_;
|
||||||
|
T value_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, std::size_t N>
|
||||||
|
class MedianFilter : public IFilter<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MedianFilter()
|
||||||
|
{
|
||||||
|
data_sequence_.fill(T(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
T currentValue() const override
|
||||||
|
{
|
||||||
|
auto sorted = data_sequence_;
|
||||||
|
std::sort(sorted.begin(), sorted.end());
|
||||||
|
|
||||||
|
return sorted[N / 2];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateImpl_(const T& data) override
|
||||||
|
{
|
||||||
|
std::move(data_sequence_.begin() + 1, data_sequence_.end(), data_sequence_.begin());
|
||||||
|
data_sequence_[N - 1] = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::array<T, N> data_sequence_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class LowPassFilter : public IFilter<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LowPassFilter() = delete;
|
||||||
|
explicit LowPassFilter(const T threshold)
|
||||||
|
: threshold_(threshold)
|
||||||
|
{}
|
||||||
|
|
||||||
|
T currentValue() const override
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateImpl_(const T& data) override
|
||||||
|
{
|
||||||
|
if (data < threshold_)
|
||||||
|
value_ = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T threshold_;
|
||||||
|
T value_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class HighPassFilter : public IFilter<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HighPassFilter() = delete;
|
||||||
|
explicit HighPassFilter(const T threshold)
|
||||||
|
: threshold_(threshold)
|
||||||
|
{}
|
||||||
|
|
||||||
|
T currentValue() const override
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void updateImpl_(const T& data) override
|
||||||
|
{
|
||||||
|
if (data > threshold_)
|
||||||
|
value_ = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T threshold_;
|
||||||
|
T value_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace Utility
|
||||||
|
|
||||||
|
#endif//SKULLC_UTILITY_FILTERS_HPP_
|
||||||
@ -8,6 +8,8 @@
|
|||||||
#ifndef SKULLC_UTILITY_FUNCTION_HPP_
|
#ifndef SKULLC_UTILITY_FUNCTION_HPP_
|
||||||
#define SKULLC_UTILITY_FUNCTION_HPP_
|
#define SKULLC_UTILITY_FUNCTION_HPP_
|
||||||
|
|
||||||
|
#include <utility_assert.hpp>
|
||||||
|
|
||||||
namespace Utility
|
namespace Utility
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -19,11 +21,19 @@ class IFunction<R(Args...)>
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using result_type = R;
|
using result_type = R;
|
||||||
|
using static_signature = R (*)(Args...);
|
||||||
|
|
||||||
virtual ~IFunction()
|
virtual ~IFunction()
|
||||||
{}
|
{}
|
||||||
|
|
||||||
virtual result_type operator()(Args... args) = 0;
|
virtual result_type operator()(Args... args) = 0;
|
||||||
|
|
||||||
|
template<typename Tag>
|
||||||
|
static_signature toStaticFunction()
|
||||||
|
{
|
||||||
|
static decltype(this) h = this;
|
||||||
|
return +[](Args... args) { (*h)(args...); };
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class>
|
template<class>
|
||||||
@ -41,7 +51,7 @@ public:
|
|||||||
explicit Function(signature callable)
|
explicit Function(signature callable)
|
||||||
: callable_(callable)
|
: callable_(callable)
|
||||||
{
|
{
|
||||||
assert(callable_);
|
SKULLC_ASSERT_DEBUG(callable_);
|
||||||
}
|
}
|
||||||
|
|
||||||
~Function() override
|
~Function() override
|
||||||
@ -73,7 +83,7 @@ public:
|
|||||||
explicit FunctionOwned(source& src, signature callable)
|
explicit FunctionOwned(source& src, signature callable)
|
||||||
: src_(&src), callable_(callable)
|
: src_(&src), callable_(callable)
|
||||||
{
|
{
|
||||||
assert(callable_);
|
SKULLC_ASSERT_DEBUG(callable_);
|
||||||
}
|
}
|
||||||
|
|
||||||
~FunctionOwned() override
|
~FunctionOwned() override
|
||||||
|
|||||||
49
Utility/Inc/utility_ifilter.hpp
Normal file
49
Utility/Inc/utility_ifilter.hpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 12/11/22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_IFILTER_HPP_
|
||||||
|
#define SKULLC_UTILITY_IFILTER_HPP_
|
||||||
|
|
||||||
|
namespace Utility
|
||||||
|
{
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class IFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~IFilter() = default;
|
||||||
|
|
||||||
|
void assignPrecedingFilter(IFilter<T>& filter)
|
||||||
|
{
|
||||||
|
preceding_filter_ = &filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPrecedingFilter()
|
||||||
|
{
|
||||||
|
preceding_filter_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(T data)
|
||||||
|
{
|
||||||
|
if (preceding_filter_)
|
||||||
|
{
|
||||||
|
preceding_filter_->update(data);
|
||||||
|
data = preceding_filter_->currentValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImpl_(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual T currentValue() const = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void updateImpl_(const T& data) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
IFilter<T>* preceding_filter_ = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
}// namespace Utility
|
||||||
|
|
||||||
|
#endif//SKULLC_UTILITY_IFILTER_HPP_
|
||||||
@ -158,15 +158,18 @@ public:
|
|||||||
if (dx == 0 && dy == 0)
|
if (dx == 0 && dy == 0)
|
||||||
{
|
{
|
||||||
at(start) = color;
|
at(start) = color;
|
||||||
} else if (dx == 0)
|
}
|
||||||
|
else if (dx == 0)
|
||||||
{
|
{
|
||||||
for (; y < y_(stop); y++)
|
for (; y < y_(stop); y++)
|
||||||
at({x, y}) = color;
|
at({x, y}) = color;
|
||||||
} else if (dy == 0)
|
}
|
||||||
|
else if (dy == 0)
|
||||||
{
|
{
|
||||||
for (; x < x_(stop); x++)
|
for (; x < x_(stop); x++)
|
||||||
at({x, y}) = color;
|
at({x, y}) = color;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// Bresenham's algorithm.
|
// Bresenham's algorithm.
|
||||||
std::int32_t p = 2 * dy - dx;
|
std::int32_t p = 2 * dy - dx;
|
||||||
@ -179,7 +182,8 @@ public:
|
|||||||
{
|
{
|
||||||
y++;
|
y++;
|
||||||
p = p + 2 * dy - 2 * dx;
|
p = p + 2 * dy - 2 * dx;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
p = p + 2 * dy;
|
p = p + 2 * dy;
|
||||||
}
|
}
|
||||||
@ -217,7 +221,8 @@ public:
|
|||||||
{
|
{
|
||||||
y--;
|
y--;
|
||||||
d = d + 4 * (x - y) + 10;
|
d = d + 4 * (x - y) + 10;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
d = d + 4 * x + 6;
|
d = d + 4 * x + 6;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,7 +68,8 @@ public:
|
|||||||
if (naive < _arr_end)
|
if (naive < _arr_end)
|
||||||
{
|
{
|
||||||
return iterator(naive, _arr_begin, _arr_end);
|
return iterator(naive, _arr_begin, _arr_end);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const pointer remainder = pointer(naive - _arr_end);
|
const pointer remainder = pointer(naive - _arr_end);
|
||||||
return iterator(_arr_begin + difference_type(remainder), _arr_begin,
|
return iterator(_arr_begin + difference_type(remainder), _arr_begin,
|
||||||
@ -82,7 +83,8 @@ public:
|
|||||||
if (naive >= _arr_begin)
|
if (naive >= _arr_begin)
|
||||||
{
|
{
|
||||||
return iterator(naive, _arr_begin, _arr_end);
|
return iterator(naive, _arr_begin, _arr_end);
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
const pointer remainder = pointer(_arr_begin - naive);
|
const pointer remainder = pointer(_arr_begin - naive);
|
||||||
return iterator(_arr_end - difference_type(remainder), _arr_begin,
|
return iterator(_arr_end - difference_type(remainder), _arr_begin,
|
||||||
@ -202,7 +204,8 @@ public:
|
|||||||
if (distance > 0)
|
if (distance > 0)
|
||||||
{
|
{
|
||||||
return distance;
|
return distance;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return head_ - tail_ + 1;
|
return head_ - tail_ + 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,8 +8,11 @@
|
|||||||
#ifndef SKULLC_UTILITY_STATICPOINTER_HPP_
|
#ifndef SKULLC_UTILITY_STATICPOINTER_HPP_
|
||||||
#define SKULLC_UTILITY_STATICPOINTER_HPP_
|
#define SKULLC_UTILITY_STATICPOINTER_HPP_
|
||||||
|
|
||||||
|
#include <new>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include <utility_assert.hpp>
|
||||||
|
|
||||||
namespace Utility
|
namespace Utility
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -57,6 +60,18 @@ struct StaticPointer
|
|||||||
return initialized_;
|
return initialized_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
value_type* get()
|
||||||
|
{
|
||||||
|
SKULLC_ASSERT_DEBUG(initialized_);
|
||||||
|
return reinterpret_cast<value_type*>(storage);
|
||||||
|
}
|
||||||
|
|
||||||
|
const value_type* get() const
|
||||||
|
{
|
||||||
|
SKULLC_ASSERT_DEBUG(initialized_);
|
||||||
|
return reinterpret_cast<const value_type*>(storage);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
};
|
};
|
||||||
|
|||||||
12
Utility/Inc/utility_tag.hpp
Normal file
12
Utility/Inc/utility_tag.hpp
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 26.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef SKULLC_UTILITY_TAG_HPP_
|
||||||
|
#define SKULLC_UTILITY_TAG_HPP_
|
||||||
|
|
||||||
|
#define SKULLC_CONCAT_IMPL(x, y) x##y
|
||||||
|
#define SKULLC_CONCAT(x, y) SKULLC_CONCAT_IMPL(x, y)
|
||||||
|
#define SKULLC_TAG struct SKULLC_CONCAT(SkullCTag_, __COUNTER__)
|
||||||
|
|
||||||
|
#endif// SKULLC_UTILITY_TAG_HPP_
|
||||||
42
Utility/Src/utility_assert.cpp
Normal file
42
Utility/Src/utility_assert.cpp
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// Created by erki on 29.06.22.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "utility_assert.hpp"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
Utility::Assert::assert_cb INSTALLED_HANDLER = nullptr;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Utility::Assert
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace Detail
|
||||||
|
{
|
||||||
|
|
||||||
|
void assertImpl(const char* expression, const char* file, const int line)
|
||||||
|
{
|
||||||
|
if (INSTALLED_HANDLER)
|
||||||
|
INSTALLED_HANDLER(expression, file, line);
|
||||||
|
else
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Detail
|
||||||
|
|
||||||
|
void setHandler(assert_cb callback)
|
||||||
|
{
|
||||||
|
INSTALLED_HANDLER = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_cb getHandler()
|
||||||
|
{
|
||||||
|
return INSTALLED_HANDLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace Utility::Assert
|
||||||
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)
|
||||||
@ -1,5 +0,0 @@
|
|||||||
[requires]
|
|
||||||
catch2/[>=2.0.0,<3.0.0]
|
|
||||||
|
|
||||||
[generators]
|
|
||||||
cmake_find_package
|
|
||||||
Loading…
x
Reference in New Issue
Block a user