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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -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

View File

@@ -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

View 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

View 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

View 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