Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
31
tools/EVerest-main/lib/everest/fsm/CMakeLists.txt
Normal file
31
tools/EVerest-main/lib/everest/fsm/CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
#
|
||||
# options
|
||||
#
|
||||
option(BUILD_EXAMPLES "enable building of examples" OFF)
|
||||
|
||||
|
||||
#
|
||||
# library declaration
|
||||
#
|
||||
add_library(fsm INTERFACE)
|
||||
add_library(fsm::fsm ALIAS fsm)
|
||||
|
||||
target_include_directories(fsm INTERFACE
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
$<INSTALL_INTERFACE:include>
|
||||
)
|
||||
|
||||
#
|
||||
# examples
|
||||
#
|
||||
if (BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
#
|
||||
# tests
|
||||
#
|
||||
if(BUILD_TESTING)
|
||||
include(CTest)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
@@ -0,0 +1 @@
|
||||
add_subdirectory(light_switch)
|
||||
@@ -0,0 +1,16 @@
|
||||
add_executable(light_switch)
|
||||
|
||||
target_sources(light_switch
|
||||
PRIVATE
|
||||
light_switch.cpp
|
||||
states.cpp
|
||||
context.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(light_switch
|
||||
PRIVATE
|
||||
fsm::fsm
|
||||
)
|
||||
|
||||
# target_compile_definitions(light_switch PUBLIC HEAP_FREE_MODE)
|
||||
target_compile_features(light_switch PRIVATE cxx_std_14)
|
||||
@@ -0,0 +1,9 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include "fsm.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
void Context::set_brightness(int value) {
|
||||
printf("Set brightness to %d\n", value);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef FSM_HPP
|
||||
#define FSM_HPP
|
||||
|
||||
#include <fsm/buffer.hpp>
|
||||
#include <fsm/fsm.hpp>
|
||||
|
||||
struct Context {
|
||||
void set_brightness(int value);
|
||||
};
|
||||
|
||||
enum class Event {
|
||||
PRESSED_ON,
|
||||
PRESSED_OFF,
|
||||
ENTER_MOTION_MODE,
|
||||
MOTION_DETECT,
|
||||
MOTION_TIMEOUT,
|
||||
};
|
||||
|
||||
#ifdef HEAP_FREE_MODE
|
||||
using BufferType = fsm::buffer::SwapBuffer<24, 16, 2>;
|
||||
using FSM = fsm::FSM<Event, int, BufferType>;
|
||||
#else
|
||||
using FSM = fsm::FSM<Event, int>;
|
||||
#endif
|
||||
|
||||
using SimpleState = fsm::states::StateWithContext<FSM::SimpleStateType, Context>;
|
||||
using CompoundState = fsm::states::StateWithContext<FSM::CompoundStateType, Context>;
|
||||
|
||||
static const auto PASS_ON = FSM::StateAllocatorType::PASS_ON;
|
||||
static const auto HANDLED_INTERNALLY = FSM::StateAllocatorType::HANDLED_INTERNALLY;
|
||||
|
||||
#endif // FSM_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "fsm.hpp"
|
||||
#include "states.hpp"
|
||||
|
||||
auto delayed_timepoint(int delay_ms) {
|
||||
return std::chrono::steady_clock::now() + std::chrono::milliseconds(delay_ms);
|
||||
}
|
||||
|
||||
void feed_machine_until(FSM& machine, int delay_ms) {
|
||||
auto next_event_tp = delayed_timepoint(delay_ms);
|
||||
|
||||
while (true) {
|
||||
|
||||
auto feed_result = machine.feed();
|
||||
if (feed_result.transition()) {
|
||||
continue;
|
||||
} else if (feed_result.unhandled_event()) {
|
||||
break;
|
||||
} else if (feed_result.has_value() == false) {
|
||||
// returning no value means don't do anything
|
||||
break;
|
||||
}
|
||||
|
||||
// fall-through: got a value
|
||||
if (*feed_result == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_feed_tp = delayed_timepoint(*feed_result);
|
||||
if (next_feed_tp < next_event_tp) {
|
||||
std::this_thread::sleep_until(next_feed_tp);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_until(next_event_tp);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
Context ctx;
|
||||
|
||||
const int DEFAULT_DELAY = 200;
|
||||
|
||||
struct EventTodo {
|
||||
Event event;
|
||||
int delay;
|
||||
};
|
||||
|
||||
auto events = std::vector<EventTodo>({
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
{Event::ENTER_MOTION_MODE, DEFAULT_DELAY},
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
{Event::PRESSED_OFF, DEFAULT_DELAY},
|
||||
{Event::ENTER_MOTION_MODE, DEFAULT_DELAY},
|
||||
{Event::MOTION_DETECT, 6000},
|
||||
{Event::MOTION_TIMEOUT, DEFAULT_DELAY},
|
||||
{Event::MOTION_DETECT, DEFAULT_DELAY},
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
{Event::PRESSED_ON, DEFAULT_DELAY},
|
||||
});
|
||||
|
||||
#ifdef HEAP_FREE_MODE
|
||||
BufferType static_buffer{};
|
||||
FSM machine(static_buffer);
|
||||
#else
|
||||
FSM machine{};
|
||||
#endif
|
||||
|
||||
machine.reset<LightOff>(ctx);
|
||||
|
||||
for (const auto& todo : events) {
|
||||
machine.handle_event(todo.event);
|
||||
|
||||
feed_machine_until(machine, todo.delay);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 46 KiB |
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include "states.hpp"
|
||||
|
||||
SimpleState::HandleEventReturnType LightOn::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::PRESSED_ON) {
|
||||
current_brightness = (current_brightness % 3) + 1;
|
||||
ctx.set_brightness(current_brightness);
|
||||
return HANDLED_INTERNALLY;
|
||||
} else if (ev == Event::PRESSED_OFF) {
|
||||
return sa.create_simple<LightOff>(ctx);
|
||||
} else {
|
||||
return PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleState::HandleEventReturnType MotionMode::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::PRESSED_OFF) {
|
||||
return sa.create_simple<LightOff>(ctx);
|
||||
} else if (ev == Event::PRESSED_ON) {
|
||||
return sa.create_simple<LightOn>(ctx);
|
||||
} else {
|
||||
return PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleState::HandleEventReturnType MotionDetected::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::MOTION_TIMEOUT) {
|
||||
return sa.create_simple<MotionIdle>(ctx);
|
||||
} else {
|
||||
return PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleState::CallbackReturnType MotionDetected::callback() {
|
||||
if (timeout_started) {
|
||||
return Event::MOTION_TIMEOUT;
|
||||
}
|
||||
|
||||
timeout_started = true;
|
||||
|
||||
return TIMEOUT_MS;
|
||||
}
|
||||
|
||||
SimpleState::HandleEventReturnType LightOff::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::PRESSED_ON) {
|
||||
return sa.create_simple<LightOn>(ctx);
|
||||
} else if (ev == Event::ENTER_MOTION_MODE) {
|
||||
sa.create_compound<MotionMode>(ctx);
|
||||
return sa.create_simple<MotionIdle>(ctx);
|
||||
} else {
|
||||
return PASS_ON;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleState::HandleEventReturnType MotionIdle::handle_event(AllocatorType& sa, Event ev) {
|
||||
if (ev == Event::MOTION_DETECT) {
|
||||
return sa.create_simple<MotionDetected>(ctx);
|
||||
} else {
|
||||
return PASS_ON;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef STATES_HPP
|
||||
#define STATES_HPP
|
||||
|
||||
#include "fsm.hpp"
|
||||
|
||||
struct LightOff : public SimpleState {
|
||||
using SimpleState::SimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.set_brightness(0);
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
};
|
||||
|
||||
struct LightOn : public SimpleState {
|
||||
using SimpleState::SimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.set_brightness(current_brightness);
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
private:
|
||||
int current_brightness{1};
|
||||
};
|
||||
|
||||
struct MotionMode : public CompoundState {
|
||||
using CompoundState::CompoundState;
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
};
|
||||
|
||||
struct MotionIdle : public SimpleState {
|
||||
using SimpleState::SimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.set_brightness(0);
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
};
|
||||
|
||||
struct MotionDetected : public SimpleState {
|
||||
using SimpleState::SimpleState;
|
||||
|
||||
void enter() final {
|
||||
ctx.set_brightness(3);
|
||||
}
|
||||
|
||||
HandleEventReturnType handle_event(AllocatorType&, Event) final;
|
||||
|
||||
CallbackReturnType callback() final;
|
||||
|
||||
private:
|
||||
static const int TIMEOUT_MS = 3000;
|
||||
bool timeout_started{false};
|
||||
};
|
||||
|
||||
#endif // STATES_HPP
|
||||
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LIBFSM__IMPL_COMMON_HPP
|
||||
#define LIBFSM__IMPL_COMMON_HPP
|
||||
|
||||
namespace fsm::_impl {
|
||||
|
||||
template <template <typename, typename, typename> typename BaseStateType, typename DerivedStateType>
|
||||
struct is_base_state_of {
|
||||
using BaseType = BaseStateType<typename DerivedStateType::EventType, typename DerivedStateType::ReturnType,
|
||||
typename DerivedStateType::AllocatorType>;
|
||||
static const bool value = std::is_base_of<BaseType, DerivedStateType>::value;
|
||||
};
|
||||
|
||||
enum class FeedResultState {
|
||||
TRANSITION,
|
||||
UNHANDLED_EVENT,
|
||||
INTERNAL_ERROR,
|
||||
HAS_VALUE,
|
||||
NO_VALUE,
|
||||
};
|
||||
|
||||
} // namespace fsm::_impl
|
||||
|
||||
#endif // LIBFSM__IMPL_COMMON_HPP
|
||||
@@ -0,0 +1,209 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LIBFSM__IMPL_STATE_ALLOCATOR_HPP
|
||||
#define LIBFSM__IMPL_STATE_ALLOCATOR_HPP
|
||||
|
||||
namespace fsm::_impl {
|
||||
|
||||
template <typename SwapAllocatorBufferType> class StateAllocator;
|
||||
|
||||
class DynamicStateAllocator {
|
||||
public:
|
||||
enum class InternalState {
|
||||
READY_FOR_CREATION,
|
||||
FAILED_MULTIPLE_SIMPLE_CREATE,
|
||||
FAILED_MULTIPLE_COMPOUND_CREATE,
|
||||
};
|
||||
|
||||
DynamicStateAllocator() = default;
|
||||
DynamicStateAllocator(const DynamicStateAllocator& other) = delete;
|
||||
DynamicStateAllocator(DynamicStateAllocator&& other) = delete;
|
||||
DynamicStateAllocator& operator=(const DynamicStateAllocator& other) = delete;
|
||||
DynamicStateAllocator& operator=(DynamicStateAllocator&& other) = delete;
|
||||
|
||||
template <typename StateType, typename... Args> bool create_compound(Args&&... args) {
|
||||
if (state != InternalState::READY_FOR_CREATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compound_state) {
|
||||
state = InternalState::FAILED_MULTIPLE_COMPOUND_CREATE;
|
||||
return false;
|
||||
};
|
||||
|
||||
compound_state = new StateType(std::forward<Args>(args)...);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename StateType, typename... Args> bool create_simple(Args&&... args) {
|
||||
if (state != InternalState::READY_FOR_CREATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (simple_state) {
|
||||
state = InternalState::FAILED_MULTIPLE_SIMPLE_CREATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
simple_state = new StateType(std::forward<Args>(args)...);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename SimpleStateType> auto pull_simple_state() {
|
||||
auto retval = reinterpret_cast<SimpleStateType*>(simple_state);
|
||||
if (retval) {
|
||||
simple_state = nullptr;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
template <typename CompoundStateType> auto pull_compound_state() {
|
||||
auto retval = reinterpret_cast<CompoundStateType*>(compound_state);
|
||||
if (retval) {
|
||||
compound_state = nullptr;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void make_ready_for_allocation() {
|
||||
state = InternalState::READY_FOR_CREATION;
|
||||
}
|
||||
|
||||
bool has_staged_states() {
|
||||
return (simple_state != nullptr) || (compound_state != nullptr);
|
||||
}
|
||||
|
||||
template <typename SimpleStateType, typename CompoundStateType> void release_staged_states() {
|
||||
if (simple_state != nullptr) {
|
||||
reinterpret_cast<SimpleStateType*>(simple_state)->~SimpleStateType();
|
||||
}
|
||||
|
||||
if (compound_state != nullptr) {
|
||||
reinterpret_cast<CompoundStateType*>(compound_state)->~CompoundStateType();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void* compound_state{nullptr};
|
||||
void* simple_state{nullptr};
|
||||
|
||||
InternalState state{InternalState::READY_FOR_CREATION};
|
||||
};
|
||||
|
||||
template <typename SwapBufferType> class StateAllocator {
|
||||
public:
|
||||
enum class InternalState {
|
||||
READY_FOR_CREATION,
|
||||
FAILED_COMPOUND_OVERFLOW,
|
||||
FAILED_MULTIPLE_SIMPLE_CREATE,
|
||||
FAILED_MULTIPLE_COMPOUND_CREATE,
|
||||
};
|
||||
|
||||
StateAllocator(SwapBufferType& buffer_) : buffer(buffer_){};
|
||||
StateAllocator(const StateAllocator& other) = delete;
|
||||
StateAllocator(StateAllocator&& other) = delete;
|
||||
StateAllocator& operator=(const StateAllocator& other) = delete;
|
||||
StateAllocator& operator=(StateAllocator&& other) = delete;
|
||||
|
||||
template <typename StateType, typename... Args> bool create_compound(Args&&... args) {
|
||||
static_assert(sizeof(StateType) <= buffer.MAX_COMPOUND_STATE_SIZE,
|
||||
"Buffer too small for the supplied compound state type");
|
||||
// TODO (aw): move this prolog into a helper function
|
||||
if (state != InternalState::READY_FOR_CREATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_nested_level == SwapBufferType::MAX_NESTING_LEVEL) {
|
||||
state = InternalState::FAILED_COMPOUND_OVERFLOW;
|
||||
// FIXME (aw): overflow
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compound_state) {
|
||||
state = InternalState::FAILED_MULTIPLE_COMPOUND_CREATE;
|
||||
return false;
|
||||
};
|
||||
|
||||
auto& next_compound = buffer.compound[current_nested_level];
|
||||
auto next_buffer = next_compound.a_is_next ? next_compound.a : next_compound.b;
|
||||
next_compound.a_is_next = !next_compound.a_is_next;
|
||||
|
||||
compound_state = new (next_buffer) StateType(std::forward<Args>(args)...);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename StateType, typename... Args> bool create_simple(Args&&... args) {
|
||||
static_assert(sizeof(StateType) <= buffer.MAX_SIMPLE_STATE_SIZE,
|
||||
"Buffer too small for the supplied simple state type");
|
||||
// TODO (aw): move this prolog into a helper function
|
||||
if (state != InternalState::READY_FOR_CREATION) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (simple_state) {
|
||||
state = InternalState::FAILED_MULTIPLE_SIMPLE_CREATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto next_buffer = (buffer.simple.a_is_next) ? buffer.simple.a : buffer.simple.b;
|
||||
buffer.simple.a_is_next = !buffer.simple.a_is_next;
|
||||
|
||||
simple_state = new (next_buffer) StateType(std::forward<Args>(args)...);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename SimpleStateType> auto pull_simple_state() {
|
||||
auto retval = reinterpret_cast<SimpleStateType*>(simple_state);
|
||||
if (retval) {
|
||||
simple_state = nullptr;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
template <typename CompoundStateType> auto pull_compound_state() {
|
||||
auto retval = reinterpret_cast<CompoundStateType*>(compound_state);
|
||||
if (retval) {
|
||||
compound_state = nullptr;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
void make_ready_for_nesting_level(size_t level) {
|
||||
state = InternalState::READY_FOR_CREATION;
|
||||
current_nested_level = level;
|
||||
}
|
||||
|
||||
bool has_staged_states() {
|
||||
return (simple_state != nullptr) || (compound_state != nullptr);
|
||||
}
|
||||
|
||||
template <typename SimpleStateType, typename CompoundStateType> void release_staged_states() {
|
||||
if (simple_state != nullptr) {
|
||||
reinterpret_cast<SimpleStateType*>(simple_state)->~SimpleStateType();
|
||||
}
|
||||
|
||||
if (compound_state != nullptr) {
|
||||
reinterpret_cast<CompoundStateType*>(compound_state)->~CompoundStateType();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
SwapBufferType& buffer;
|
||||
|
||||
void* compound_state{nullptr};
|
||||
void* simple_state{nullptr};
|
||||
|
||||
InternalState state{InternalState::READY_FOR_CREATION};
|
||||
|
||||
size_t current_nested_level{0};
|
||||
};
|
||||
|
||||
} // namespace fsm::_impl
|
||||
|
||||
#endif // LIBFSM__IMPL_STATE_ALLOCATOR_HPP
|
||||
34
tools/EVerest-main/lib/everest/fsm/include/fsm/buffer.hpp
Normal file
34
tools/EVerest-main/lib/everest/fsm/include/fsm/buffer.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LIBFSM_BUFFER_HPP
|
||||
#define LIBFSM_BUFFER_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace fsm::buffer {
|
||||
template <size_t MaxSimpleStateSize, size_t MaxCompoundStateSize, size_t MaxNestingLevel> struct SwapBuffer {
|
||||
static const size_t MAX_SIMPLE_STATE_SIZE = MaxSimpleStateSize;
|
||||
static const size_t MAX_COMPOUND_STATE_SIZE = MaxCompoundStateSize;
|
||||
static const size_t MAX_NESTING_LEVEL = MaxNestingLevel;
|
||||
|
||||
struct SimpleStateBuffer {
|
||||
alignas(std::max_align_t) uint8_t a[MaxSimpleStateSize];
|
||||
alignas(std::max_align_t) uint8_t b[MaxSimpleStateSize];
|
||||
bool a_is_next{true};
|
||||
};
|
||||
|
||||
SimpleStateBuffer simple{};
|
||||
|
||||
struct CompoundStateBuffer {
|
||||
alignas(std::max_align_t) uint8_t a[MaxCompoundStateSize];
|
||||
alignas(std::max_align_t) uint8_t b[MaxCompoundStateSize];
|
||||
bool a_is_next{true};
|
||||
};
|
||||
|
||||
CompoundStateBuffer compound[MAX_NESTING_LEVEL]{};
|
||||
};
|
||||
|
||||
} // namespace fsm::buffer
|
||||
|
||||
#endif // LIBFSM_BUFFER_HPP
|
||||
343
tools/EVerest-main/lib/everest/fsm/include/fsm/fsm.hpp
Normal file
343
tools/EVerest-main/lib/everest/fsm/include/fsm/fsm.hpp
Normal file
@@ -0,0 +1,343 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LIBFSM_FSM_HPP
|
||||
#define LIBFSM_FSM_HPP
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "_impl/common.hpp"
|
||||
#include "_impl/state_allocator.hpp"
|
||||
|
||||
#include "states.hpp"
|
||||
|
||||
namespace fsm {
|
||||
|
||||
// FIXME (aw): we should treat internal unhandled events as errors because this doesn't make sense by design!
|
||||
enum class HandleEventResult {
|
||||
SUCCESS,
|
||||
UNHANDLED,
|
||||
INTERNAL_ERROR,
|
||||
};
|
||||
|
||||
// TODO (aw): would be good to know, if references or pointers can be passed as well
|
||||
template <typename ResultType> class FeedResult {
|
||||
private:
|
||||
using InternalState = _impl::FeedResultState;
|
||||
|
||||
public:
|
||||
FeedResult(InternalState state_) : state(state_){};
|
||||
FeedResult(ResultType value_) : value(value_), state(InternalState::HAS_VALUE){};
|
||||
|
||||
bool has_value() const {
|
||||
return (state == InternalState::HAS_VALUE);
|
||||
}
|
||||
|
||||
bool internal_error() const {
|
||||
return (state == InternalState::INTERNAL_ERROR);
|
||||
}
|
||||
|
||||
bool transition() const {
|
||||
return (state == InternalState::TRANSITION);
|
||||
}
|
||||
|
||||
bool unhandled_event() const {
|
||||
return (state == InternalState::UNHANDLED_EVENT);
|
||||
}
|
||||
|
||||
ResultType& operator*() {
|
||||
return value;
|
||||
}
|
||||
|
||||
ResultType* operator->() {
|
||||
return &value;
|
||||
}
|
||||
|
||||
private:
|
||||
ResultType value;
|
||||
InternalState state;
|
||||
};
|
||||
|
||||
template <typename EventType, typename ReturnType, typename AllocatorBufferType = void> class FSM;
|
||||
|
||||
template <typename EventType, typename ReturnType> class FSM<EventType, ReturnType, void> {
|
||||
public:
|
||||
using StateAllocatorType = states::StateAllocator<>;
|
||||
using SimpleStateType = states::SimpleStateBase<EventType, ReturnType, StateAllocatorType>;
|
||||
using CompoundStateType = states::CompoundStateBase<EventType, ReturnType, StateAllocatorType>;
|
||||
|
||||
FSM() = default;
|
||||
FSM(const FSM& other) = delete;
|
||||
FSM(FSM&& other) = delete;
|
||||
FSM& operator=(const FSM& other) = delete;
|
||||
FSM& operator=(FSM&& other) = delete;
|
||||
~FSM() = default;
|
||||
|
||||
template <typename StateType, typename... Args> void reset(Args&&... args) {
|
||||
reset();
|
||||
|
||||
state_allocator.make_ready_for_allocation();
|
||||
state_allocator.create_simple<StateType>(std::forward<Args>(args)...);
|
||||
current_state.reset(state_allocator.pull_simple_state<SimpleStateType>());
|
||||
current_state->enter();
|
||||
}
|
||||
|
||||
HandleEventResult handle_event(EventType ev) {
|
||||
if (current_state == nullptr) {
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
auto next_nesting_level_to_handle = compound_stack.size();
|
||||
auto state_allocator_wrapper = StateAllocatorType(state_allocator);
|
||||
|
||||
state_allocator.make_ready_for_allocation();
|
||||
|
||||
auto result = current_state->handle_event(state_allocator_wrapper, ev);
|
||||
|
||||
while (result.is_pass_on() && next_nesting_level_to_handle != 0) {
|
||||
next_nesting_level_to_handle--;
|
||||
state_allocator.make_ready_for_allocation();
|
||||
result = compound_stack[next_nesting_level_to_handle]->handle_event(state_allocator_wrapper, ev);
|
||||
}
|
||||
|
||||
if (result.is_pass_on()) {
|
||||
if (state_allocator.has_staged_states()) {
|
||||
state_allocator.release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
}
|
||||
return HandleEventResult::UNHANDLED;
|
||||
} else if (result.is_handled_internally()) {
|
||||
if (state_allocator.has_staged_states()) {
|
||||
state_allocator.release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
}
|
||||
return HandleEventResult::SUCCESS;
|
||||
} else if (result.is_allocation_error()) {
|
||||
state_allocator.release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const auto handled_at_nesting_level = next_nesting_level_to_handle;
|
||||
|
||||
// fall-though: event has been handled, clear current state and all states up to the handled level
|
||||
|
||||
// note: this will change current_nesting_level
|
||||
reset(handled_at_nesting_level, true);
|
||||
|
||||
auto const compound_state = state_allocator.pull_compound_state<CompoundStateType>();
|
||||
|
||||
if (compound_state != nullptr) {
|
||||
compound_stack.emplace_back(compound_state);
|
||||
compound_state->enter();
|
||||
}
|
||||
|
||||
auto const next_state = state_allocator.pull_simple_state<SimpleStateType>();
|
||||
if (next_state != nullptr) {
|
||||
next_state->enter();
|
||||
current_state.reset(next_state);
|
||||
|
||||
return HandleEventResult::SUCCESS;
|
||||
}
|
||||
|
||||
// this should never happen - i.e. when we come here that would mean, someone managed to return NEW_STATE from
|
||||
// the handle_event callback but didn't set the next state
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
FeedResult<ReturnType> feed() {
|
||||
using FeedResultState = _impl::FeedResultState;
|
||||
if (current_state == nullptr) {
|
||||
return FeedResultState::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const auto result = current_state->callback();
|
||||
|
||||
if (result.is_event) {
|
||||
switch (handle_event(result.event)) {
|
||||
case HandleEventResult::SUCCESS:
|
||||
return FeedResultState::TRANSITION;
|
||||
case HandleEventResult::UNHANDLED:
|
||||
return FeedResultState::UNHANDLED_EVENT;
|
||||
default:
|
||||
// NOTE: everything else should be an internal error
|
||||
return FeedResultState::INTERNAL_ERROR;
|
||||
}
|
||||
} else if (result.is_value_set) {
|
||||
return result.value;
|
||||
} else {
|
||||
return FeedResultState::NO_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void reset(size_t up_to_nested_level = 0, bool execute_leave = false) {
|
||||
// leave and destroy everything allocated
|
||||
if (current_state) {
|
||||
if (execute_leave) {
|
||||
current_state->leave();
|
||||
}
|
||||
current_state.reset();
|
||||
}
|
||||
|
||||
while (compound_stack.size() > up_to_nested_level) {
|
||||
if (execute_leave) {
|
||||
compound_stack.back()->leave();
|
||||
}
|
||||
compound_stack.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SimpleStateType> current_state{nullptr};
|
||||
std::vector<std::unique_ptr<CompoundStateType>> compound_stack{};
|
||||
|
||||
_impl::DynamicStateAllocator state_allocator;
|
||||
};
|
||||
|
||||
template <typename EventType, typename ReturnType, typename AllocatorBufferType> class FSM {
|
||||
public:
|
||||
using StateAllocatorType = states::StateAllocator<AllocatorBufferType>;
|
||||
using SimpleStateType = states::SimpleStateBase<EventType, ReturnType, StateAllocatorType>;
|
||||
using CompoundStateType = states::CompoundStateBase<EventType, ReturnType, StateAllocatorType>;
|
||||
|
||||
FSM(AllocatorBufferType& buffer) :
|
||||
state_allocator(buffer){
|
||||
|
||||
};
|
||||
|
||||
FSM(const FSM& other) = delete;
|
||||
FSM(FSM&& other) = delete;
|
||||
FSM& operator=(const FSM& other) = delete;
|
||||
FSM& operator=(FSM&& other) = delete;
|
||||
~FSM() {
|
||||
state_allocator.template release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
reset();
|
||||
}
|
||||
|
||||
template <typename StateType, typename... Args> void reset(Args&&... args) {
|
||||
reset();
|
||||
|
||||
state_allocator.make_ready_for_nesting_level(0);
|
||||
state_allocator.template create_simple<StateType>(std::forward<Args>(args)...);
|
||||
current_state = state_allocator.template pull_simple_state<SimpleStateType>();
|
||||
current_state->enter();
|
||||
}
|
||||
|
||||
HandleEventResult handle_event(EventType ev) {
|
||||
if (current_state == nullptr) {
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
auto next_nesting_level_to_handle = current_nesting_level;
|
||||
auto state_allocator_wrapper = StateAllocatorType(state_allocator);
|
||||
|
||||
state_allocator.make_ready_for_nesting_level(next_nesting_level_to_handle);
|
||||
|
||||
auto result = current_state->handle_event(state_allocator_wrapper, ev);
|
||||
|
||||
while (result.is_pass_on() && next_nesting_level_to_handle != 0) {
|
||||
next_nesting_level_to_handle--;
|
||||
state_allocator.make_ready_for_nesting_level(next_nesting_level_to_handle);
|
||||
result = compound_states[next_nesting_level_to_handle]->handle_event(state_allocator_wrapper, ev);
|
||||
}
|
||||
|
||||
if (result.is_pass_on()) {
|
||||
if (state_allocator.has_staged_states()) {
|
||||
state_allocator.template release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
}
|
||||
return HandleEventResult::UNHANDLED;
|
||||
} else if (result.is_handled_internally()) {
|
||||
if (state_allocator.has_staged_states()) {
|
||||
state_allocator.template release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
}
|
||||
return HandleEventResult::SUCCESS;
|
||||
} else if (result.is_allocation_error()) {
|
||||
state_allocator.template release_staged_states<SimpleStateType, CompoundStateType>();
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const auto handled_at_nesting_level = next_nesting_level_to_handle;
|
||||
|
||||
// fall-though: event has been handled, clear current state and all states up to the handled level
|
||||
|
||||
// note: this will change current_nesting_level
|
||||
reset(handled_at_nesting_level, true);
|
||||
|
||||
const auto compound_state = state_allocator.template pull_compound_state<CompoundStateType>();
|
||||
|
||||
if (compound_state != nullptr) {
|
||||
// compound state has been set
|
||||
current_nesting_level = handled_at_nesting_level + 1;
|
||||
compound_states[handled_at_nesting_level] = compound_state;
|
||||
|
||||
compound_state->enter();
|
||||
}
|
||||
|
||||
const auto next_state = state_allocator.template pull_simple_state<SimpleStateType>();
|
||||
if (next_state != nullptr) {
|
||||
next_state->enter();
|
||||
current_state = next_state;
|
||||
|
||||
return HandleEventResult::SUCCESS;
|
||||
}
|
||||
|
||||
// this should never happen - i.e. when we come here that would mean, someone managed to return NEW_STATE from
|
||||
// the handle_event callback but didn't set the next state
|
||||
return HandleEventResult::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
FeedResult<ReturnType> feed() {
|
||||
using FeedResultState = _impl::FeedResultState;
|
||||
if (current_state == nullptr) {
|
||||
return FeedResultState::INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
const auto result = current_state->callback();
|
||||
|
||||
if (result.is_event) {
|
||||
switch (handle_event(result.event)) {
|
||||
case HandleEventResult::SUCCESS:
|
||||
return FeedResultState::TRANSITION;
|
||||
case HandleEventResult::UNHANDLED:
|
||||
return FeedResultState::UNHANDLED_EVENT;
|
||||
default:
|
||||
// NOTE: everything else should be an internal error
|
||||
return FeedResultState::INTERNAL_ERROR;
|
||||
}
|
||||
} else if (result.is_value_set) {
|
||||
return result.value;
|
||||
} else {
|
||||
return FeedResultState::NO_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void reset(size_t up_to_nested_level = 0, bool execute_leave = false) {
|
||||
// leave and destroy everything allocated
|
||||
if (current_state) {
|
||||
if (execute_leave) {
|
||||
current_state->leave();
|
||||
}
|
||||
current_state->~SimpleStateBase();
|
||||
current_state = nullptr;
|
||||
}
|
||||
|
||||
while (current_nesting_level > up_to_nested_level) {
|
||||
auto& compound_state = compound_states[current_nesting_level - 1];
|
||||
if (execute_leave) {
|
||||
compound_state->leave();
|
||||
}
|
||||
compound_state->~CompoundStateBase();
|
||||
compound_state = nullptr;
|
||||
current_nesting_level--;
|
||||
}
|
||||
}
|
||||
|
||||
SimpleStateType* current_state{nullptr};
|
||||
std::array<CompoundStateType*, AllocatorBufferType::MAX_NESTING_LEVEL> compound_states{};
|
||||
size_t current_nesting_level{0};
|
||||
|
||||
_impl::StateAllocator<AllocatorBufferType> state_allocator;
|
||||
};
|
||||
|
||||
} // namespace fsm
|
||||
|
||||
#endif // LIBFSM_FSM_HPP
|
||||
152
tools/EVerest-main/lib/everest/fsm/include/fsm/states.hpp
Normal file
152
tools/EVerest-main/lib/everest/fsm/include/fsm/states.hpp
Normal file
@@ -0,0 +1,152 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef LIBFSM_STATES_HPP
|
||||
#define LIBFSM_STATES_HPP
|
||||
|
||||
#include "_impl/state_allocator.hpp"
|
||||
|
||||
namespace fsm::states {
|
||||
|
||||
// forward declaration so it can be made friend of states::HandleEventResult
|
||||
template <typename SwapBufferType> class StateAllocator;
|
||||
|
||||
class HandleEventResult {
|
||||
private:
|
||||
enum class InternalState {
|
||||
NEW_STATE,
|
||||
HANDLED_INTERNALY,
|
||||
ALLOCATION_ERROR,
|
||||
PASS_ON,
|
||||
};
|
||||
|
||||
constexpr HandleEventResult(InternalState state_) : state(state_){};
|
||||
|
||||
InternalState state;
|
||||
|
||||
template <typename SwapBufferType> friend class StateAllocator;
|
||||
|
||||
public:
|
||||
bool is_new_state() const {
|
||||
return InternalState::NEW_STATE == state;
|
||||
}
|
||||
|
||||
bool is_allocation_error() const {
|
||||
return InternalState::ALLOCATION_ERROR == state;
|
||||
}
|
||||
|
||||
bool is_pass_on() const {
|
||||
return InternalState::PASS_ON == state;
|
||||
}
|
||||
|
||||
bool is_handled_internally() const {
|
||||
return InternalState::HANDLED_INTERNALY == state;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename EventType, typename ReturnType> struct CallbackResult {
|
||||
CallbackResult() = default;
|
||||
CallbackResult(ReturnType value_) : value(value_), is_value_set(true){};
|
||||
CallbackResult(EventType event_) : event(event_), is_event(true){};
|
||||
|
||||
ReturnType value{0};
|
||||
EventType event;
|
||||
bool is_event{false};
|
||||
bool is_value_set{false};
|
||||
};
|
||||
|
||||
template <typename EventType_, typename ReturnType_, typename StateAllocatorType> struct SimpleStateBase {
|
||||
using EventType = EventType_;
|
||||
using ReturnType = ReturnType_;
|
||||
using AllocatorType = StateAllocatorType;
|
||||
|
||||
using CallbackReturnType = CallbackResult<EventType_, ReturnType>;
|
||||
using HandleEventReturnType = HandleEventResult;
|
||||
|
||||
virtual void enter(){};
|
||||
virtual HandleEventReturnType handle_event(AllocatorType&, EventType) = 0;
|
||||
virtual CallbackReturnType callback() {
|
||||
return {};
|
||||
};
|
||||
virtual void leave(){};
|
||||
virtual ~SimpleStateBase() = default;
|
||||
};
|
||||
|
||||
template <typename EventType_, typename ReturnType_, typename StateAllocatorType> struct CompoundStateBase {
|
||||
using EventType = EventType_;
|
||||
using ReturnType = ReturnType_;
|
||||
using AllocatorType = StateAllocatorType;
|
||||
|
||||
using HandleEventReturnType = HandleEventResult;
|
||||
|
||||
virtual void enter(){};
|
||||
virtual HandleEventReturnType handle_event(AllocatorType&, EventType) = 0;
|
||||
virtual void leave(){};
|
||||
virtual ~CompoundStateBase() = default;
|
||||
};
|
||||
|
||||
template <typename StateType, typename ContextType> struct StateWithContext : public StateType {
|
||||
StateWithContext(ContextType& context) : ctx(context){};
|
||||
|
||||
protected:
|
||||
ContextType& ctx;
|
||||
};
|
||||
|
||||
template <typename SwapAllocatorBufferType = void> class StateAllocator;
|
||||
|
||||
template <> class StateAllocator<void> {
|
||||
public:
|
||||
StateAllocator(_impl::DynamicStateAllocator& allocator_) : allocator(allocator_){};
|
||||
|
||||
template <typename StateType, typename... Args> void create_compound(Args&&... args) {
|
||||
static_assert(_impl::is_base_state_of<CompoundStateBase, StateType>::value,
|
||||
"StateType needs to be derived from CompoundStateBase");
|
||||
allocator.create_compound<StateType>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename StateType, typename... Args> HandleEventResult create_simple(Args&&... args) {
|
||||
static_assert(_impl::is_base_state_of<SimpleStateBase, StateType>::value,
|
||||
"StateType needs to be derived from SimpleStateBase");
|
||||
|
||||
using State = HandleEventResult::InternalState;
|
||||
const auto success = allocator.create_simple<StateType>(std::forward<Args>(args)...);
|
||||
return success ? State::NEW_STATE : State::ALLOCATION_ERROR;
|
||||
}
|
||||
|
||||
// NOTE (aw): this could also be a non-static function, which checks if any states have been set
|
||||
static constexpr HandleEventResult PASS_ON{HandleEventResult::InternalState::PASS_ON};
|
||||
static constexpr HandleEventResult HANDLED_INTERNALLY{HandleEventResult::InternalState::HANDLED_INTERNALY};
|
||||
|
||||
private:
|
||||
_impl::DynamicStateAllocator& allocator;
|
||||
};
|
||||
|
||||
template <typename SwapBufferType> class StateAllocator {
|
||||
public:
|
||||
StateAllocator(_impl::StateAllocator<SwapBufferType>& allocator_) : allocator(allocator_){};
|
||||
|
||||
template <typename StateType, typename... Args> void create_compound(Args&&... args) {
|
||||
static_assert(_impl::is_base_state_of<CompoundStateBase, StateType>::value,
|
||||
"StateType needs to be derived from CompoundStateBase");
|
||||
allocator.template create_compound<StateType>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename StateType, typename... Args> HandleEventResult create_simple(Args&&... args) {
|
||||
static_assert(_impl::is_base_state_of<SimpleStateBase, StateType>::value,
|
||||
"StateType needs to be derived from SimpleStateBase");
|
||||
|
||||
using State = HandleEventResult::InternalState;
|
||||
const auto success = allocator.template create_simple<StateType>(std::forward<Args>(args)...);
|
||||
return success ? State::NEW_STATE : State::ALLOCATION_ERROR;
|
||||
}
|
||||
|
||||
// NOTE (aw): this could also be a non-static function, which checks if any states have been set
|
||||
static constexpr HandleEventResult PASS_ON{HandleEventResult::InternalState::PASS_ON};
|
||||
static constexpr HandleEventResult HANDLED_INTERNALLY{HandleEventResult::InternalState::HANDLED_INTERNALY};
|
||||
|
||||
private:
|
||||
_impl::StateAllocator<SwapBufferType>& allocator;
|
||||
};
|
||||
|
||||
} // namespace fsm::states
|
||||
|
||||
#endif // LIBFSM_STATES_HPP
|
||||
24
tools/EVerest-main/lib/everest/fsm/readme.rst
Normal file
24
tools/EVerest-main/lib/everest/fsm/readme.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
========
|
||||
libfsm
|
||||
========
|
||||
--------------------------------------------------------------------
|
||||
a tiny c++ library for writing maintainable finite state machines
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Features
|
||||
========
|
||||
|
||||
* compound states
|
||||
* heap free mode of operation
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
See the example in ``examples/light_switch``
|
||||
|
||||
Todo
|
||||
====
|
||||
|
||||
- improve documentation
|
||||
|
||||
- structure
|
||||
11
tools/EVerest-main/lib/everest/fsm/tests/CMakeLists.txt
Normal file
11
tools/EVerest-main/lib/everest/fsm/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,11 @@
|
||||
set(TEST_TARGET_NAME ${PROJECT_NAME}_fsm_state_allocator_tests)
|
||||
|
||||
add_executable(${TEST_TARGET_NAME} state_allocator.cpp)
|
||||
target_link_libraries(${TEST_TARGET_NAME}
|
||||
PRIVATE
|
||||
fsm::fsm
|
||||
Catch2::Catch2WithMain
|
||||
)
|
||||
|
||||
catch_discover_tests(${TEST_TARGET_NAME})
|
||||
ev_register_test_target(${TEST_TARGET_NAME})
|
||||
62
tools/EVerest-main/lib/everest/fsm/tests/state_allocator.cpp
Normal file
62
tools/EVerest-main/lib/everest/fsm/tests/state_allocator.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2023 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
|
||||
#include <fsm/_impl/state_allocator.hpp>
|
||||
#include <fsm/buffer.hpp>
|
||||
|
||||
SCENARIO("Testing buffer allocator") {
|
||||
GIVEN("A state allocator with a defined swap buffer") {
|
||||
using BufferType = fsm::buffer::SwapBuffer<64, 64, 3>;
|
||||
using StateAllocatorType = fsm::_impl::StateAllocator<BufferType>;
|
||||
|
||||
BufferType buffer;
|
||||
|
||||
REQUIRE(buffer.MAX_SIMPLE_STATE_SIZE == 64);
|
||||
|
||||
StateAllocatorType state_allocator{buffer};
|
||||
|
||||
WHEN("The state_allocator is vanilla") {
|
||||
THEN("It shouldn not have any staged states") {
|
||||
REQUIRE(state_allocator.has_staged_states() == false);
|
||||
|
||||
auto simple_object = state_allocator.pull_simple_state<double>();
|
||||
REQUIRE(simple_object == nullptr);
|
||||
|
||||
auto compound_object = state_allocator.pull_compound_state<int>();
|
||||
REQUIRE(compound_object == nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
WHEN("Made ready for allocation and added a simple and compound object") {
|
||||
state_allocator.make_ready_for_nesting_level(0);
|
||||
const int SOME_INT_CONST = 42;
|
||||
const double SOME_DOUBLE_CONST = 3.14;
|
||||
auto compound_allocation_result = state_allocator.create_compound<int>(SOME_INT_CONST);
|
||||
auto simple_allocation_result = state_allocator.create_simple<double>(SOME_DOUBLE_CONST);
|
||||
|
||||
REQUIRE(compound_allocation_result == true);
|
||||
REQUIRE(simple_allocation_result == true);
|
||||
|
||||
THEN("The allocator should have staged states") {
|
||||
REQUIRE(state_allocator.has_staged_states() == true);
|
||||
}
|
||||
|
||||
THEN("The created objects should yield the correct values") {
|
||||
auto simple_object = state_allocator.pull_simple_state<double>();
|
||||
REQUIRE(state_allocator.has_staged_states() == true);
|
||||
|
||||
auto compound_object = state_allocator.pull_compound_state<int>();
|
||||
REQUIRE(state_allocator.has_staged_states() == false);
|
||||
|
||||
REQUIRE(*simple_object == SOME_DOUBLE_CONST);
|
||||
REQUIRE(*compound_object == SOME_INT_CONST);
|
||||
}
|
||||
|
||||
THEN("Creating a simple object again should fail") {
|
||||
simple_allocation_result = state_allocator.create_simple<double>(SOME_DOUBLE_CONST);
|
||||
REQUIRE(simple_allocation_result == false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user