379 lines
13 KiB
C++
379 lines
13 KiB
C++
#pragma once
|
|
|
|
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
|
|
|
/* FINITE-STATE MACHINE IMPLEMENTATION */
|
|
|
|
|
|
#include <functional>
|
|
#include <string_view>
|
|
#include <typeindex>
|
|
#include <unordered_map>
|
|
#include <variant>
|
|
#include "mcc_fsm_utils.h"
|
|
|
|
|
|
namespace mcc::fsm
|
|
{
|
|
|
|
|
|
class MccFsmAbstractState;
|
|
|
|
namespace traits
|
|
{
|
|
|
|
template <typename T>
|
|
concept fsm_event_c =
|
|
std::is_default_constructible_v<T> && std::is_move_constructible_v<T> && std::movable<T> && requires {
|
|
// { T::ID } -> std::same_as<const std::string_view>; // static constant member of event identificator
|
|
requires std::same_as<const std::string_view, decltype(T::ID)>;
|
|
};
|
|
|
|
template <typename T>
|
|
concept fsm_event_state_pair_c = requires {
|
|
[]<traits::fsm_event_c EvT, typename StT>(std::type_identity<std::pair<EvT, StT>>) {
|
|
// []<traits::fsm_event_c EvT, std::derived_from<MccFsmAbstractState> StT>(std::type_identity<std::pair<EvT,
|
|
// StT>>) {
|
|
}(std::type_identity<T>());
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept fsm_event_state_pair_tuple_c =
|
|
requires { []<fsm_event_state_pair_c... PTs>(std::type_identity<std::tuple<PTs...>>) {}(std::type_identity<T>()); };
|
|
|
|
} // namespace traits
|
|
|
|
|
|
/*
|
|
A base FSM state class with implementation of "event -- new state" transition table
|
|
*/
|
|
class MccFsmAbstractState
|
|
{
|
|
static constexpr std::string_view ID = "MCC-FSM-STATE";
|
|
|
|
protected:
|
|
MccFsmAbstractState() = default;
|
|
|
|
// helpers
|
|
template <typename T, typename TpT>
|
|
struct in_tuple;
|
|
|
|
template <typename T, typename... Ts>
|
|
struct in_tuple<T, std::tuple<Ts...>> : std::disjunction<std::is_same<T, Ts>...> {
|
|
};
|
|
|
|
template <typename T, typename... Ts>
|
|
static constexpr bool in_tpl_v = in_tuple<T, Ts...>::value;
|
|
|
|
// template <typename T, typename... PTs>
|
|
// struct in_pair : std::disjunction<std::is_same<T, typename PTs::first_type>...> {
|
|
// };
|
|
|
|
template <typename T, typename... PTs>
|
|
static constexpr bool in_pair_v = std::disjunction<std::is_same<T, typename PTs::first_type>...>::value;
|
|
// static constexpr bool in_pair_v = in_pair<T, PTs...>::value;
|
|
|
|
/* transition table definition */
|
|
|
|
public:
|
|
template <traits::fsm_event_state_pair_c... PTs>
|
|
struct trans_table_t;
|
|
|
|
template <traits::fsm_event_state_pair_c PT>
|
|
struct trans_table_t<PT> {
|
|
using events_t = std::tuple<typename PT::first_type>;
|
|
using states_t = std::tuple<typename PT::second_type>;
|
|
using unique_states_t = states_t;
|
|
|
|
using evst_pairs_t = std::tuple<PT>;
|
|
|
|
static constexpr bool unique = true;
|
|
|
|
template <traits::fsm_event_c EvT>
|
|
using find_state_by_event_t =
|
|
std::conditional_t<std::same_as<EvT, typename PT::first_type>, typename PT::second_type, std::nullptr_t>;
|
|
};
|
|
|
|
template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
|
|
struct trans_table_t<PT, PTs...> {
|
|
private:
|
|
using ev_t = typename PT::first_type;
|
|
using st_t = typename PT::second_type;
|
|
|
|
// static constexpr bool non_unique = in_pair_v<ev_t, PTs...>;
|
|
static constexpr bool non_unique = (std::same_as<ev_t, typename PTs::first_type> || ...);
|
|
|
|
public:
|
|
using events_t =
|
|
std::conditional_t<non_unique,
|
|
typename trans_table_t<PTs...>::events_t,
|
|
decltype(std::tuple_cat(std::declval<std::tuple<ev_t>>(),
|
|
std::declval<typename trans_table_t<PTs...>::events_t>()))>;
|
|
using states_t =
|
|
std::conditional_t<non_unique,
|
|
typename trans_table_t<PTs...>::states_t,
|
|
decltype(std::tuple_cat(std::declval<std::tuple<st_t>>(),
|
|
std::declval<typename trans_table_t<PTs...>::states_t>()))>;
|
|
|
|
using unique_states_t = std::conditional_t<
|
|
non_unique,
|
|
typename trans_table_t<PTs...>::unique_states_t,
|
|
std::conditional_t<(std::same_as<st_t, typename PTs::second_type> || ...),
|
|
typename trans_table_t<PTs...>::unique_states_t,
|
|
decltype(std::tuple_cat(
|
|
std::declval<std::tuple<st_t>>(),
|
|
std::declval<typename trans_table_t<PTs...>::unique_states_t>()))>>;
|
|
|
|
using evst_pairs_t =
|
|
std::conditional_t<non_unique,
|
|
typename trans_table_t<PTs...>::evst_pairs_t,
|
|
decltype(std::tuple_cat(std::declval<std::tuple<PT>>(),
|
|
std::declval<typename trans_table_t<PTs...>::evst_pairs_t>()))>;
|
|
|
|
private:
|
|
template <traits::fsm_event_c EvT>
|
|
struct find_state_by_event {
|
|
template <size_t I = 0, typename T = void>
|
|
struct helper {
|
|
static constexpr size_t idx =
|
|
std::same_as<EvT, std::tuple_element_t<I, events_t>> ? I : helper<I + 1>::idx;
|
|
};
|
|
|
|
// to stop unwind the recursive 'helper' template
|
|
// in std::conditional_t trait
|
|
template <typename T>
|
|
struct helper<std::tuple_size_v<events_t> - 1, T> {
|
|
static constexpr size_t idx = std::tuple_size_v<events_t> - 1;
|
|
};
|
|
|
|
using res_t = std::
|
|
conditional_t<in_tpl_v<EvT, events_t>, std::tuple_element_t<helper<0>::idx, states_t>, std::nullptr_t>;
|
|
};
|
|
|
|
public:
|
|
template <traits::fsm_event_c EvT>
|
|
using find_state_by_event_t = find_state_by_event<EvT>::res_t;
|
|
}; // end of 'trans_table_t<PT, PTs...>'
|
|
|
|
using transition_t = trans_table_t<>;
|
|
}; // end of 'MccFsmAbstractState'
|
|
|
|
|
|
namespace traits
|
|
{
|
|
|
|
template <typename T>
|
|
concept fsm_trans_table_c = requires {
|
|
[]<fsm_event_state_pair_c... PTs>(std::type_identity<mcc::fsm::MccFsmAbstractState::trans_table_t<PTs...>>) {
|
|
}(std::type_identity<T>());
|
|
// requires true;
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept fsm_state_c =
|
|
std::is_default_constructible_v<T> && std::is_move_constructible_v<T> && std::movable<T> &&
|
|
std::derived_from<T, MccFsmAbstractState> && requires {
|
|
// state class must define a type 'transition_t' which must be a full specialization of
|
|
// MccFsmAbstractState::trans_table_t class
|
|
[]<fsm_trans_table_c TabT>(std::type_identity<TabT>) {}(std::type_identity<typename T::transition_t>());
|
|
};
|
|
|
|
} // namespace traits
|
|
|
|
|
|
|
|
template <traits::fsm_state_c InitStateT>
|
|
class MccFiniteStateMachine
|
|
{
|
|
public:
|
|
// template <traits::fsm_state_c ST>
|
|
// MccFiniteStateMachine(ST initial_state) : _currentStateID(ST::ID), _currentStateIDX(typeid(ST))
|
|
// {
|
|
// // setStateFunc<ST::transition_t::evst_pairs_t>();
|
|
// initFromInitial<ST::transition_t::evst_pairs_t>(typeid(ST));
|
|
// }
|
|
|
|
MccFiniteStateMachine() : _currentStateID(InitStateT::ID), _currentStateVariant(&std::get<InitStateT>(_states)) {}
|
|
|
|
template <traits::fsm_event_c EvT>
|
|
void dispatchEvent(EvT event)
|
|
{
|
|
enter_state_func_umap_t<EvT>[this][_currentStateIDX](std::move(event));
|
|
// state_func_umap_t<EvT>[this][_currentStateIDX](std::move(event));
|
|
}
|
|
|
|
protected:
|
|
template <traits::fsm_event_c EvT>
|
|
using state_func_t = std::unordered_map<std::type_index, std::function<void(EvT)>>;
|
|
|
|
template <traits::fsm_event_c EvT>
|
|
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> state_func_umap_t{};
|
|
|
|
template <traits::fsm_event_c EvT>
|
|
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> enter_state_func_umap_t{};
|
|
|
|
template <traits::fsm_event_c EvT>
|
|
inline static std::unordered_map<const MccFiniteStateMachine*, state_func_t<EvT>> exit_state_func_umap_t{};
|
|
|
|
std::string_view _currentStateID{InitStateT::ID};
|
|
std::type_index _currentStateIDX{typeid(InitStateT)};
|
|
|
|
std::function<void()> _exitCurrentStateFunc;
|
|
|
|
template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
|
|
consteval static void initFromInitial(std::type_index state_idx)
|
|
{
|
|
initFromInitial<PT>(state_idx);
|
|
|
|
if constexpr (sizeof...(PTs)) {
|
|
initFromInitial<PTs...>(state_idx);
|
|
}
|
|
}
|
|
|
|
template <traits::fsm_event_state_pair_c PT>
|
|
consteval static void initFromInitial(std::type_index state_idx)
|
|
{
|
|
using ev_t = typename PT::first_type;
|
|
using st_t = typename PT::second_type;
|
|
|
|
enter_state_func_umap_t<ev_t>[][state_idx] = [](ev_t event) {
|
|
st_t state;
|
|
|
|
initFromInitial<st_t::transition_t::evst_pairs_t>(st_t::ID);
|
|
|
|
if constexpr (requires(st_t inst) {
|
|
{ inst.enter(std::declval<ev_t>()) };
|
|
}) {
|
|
state.enter(std::move(event));
|
|
} else {
|
|
state.enter();
|
|
}
|
|
};
|
|
}
|
|
|
|
|
|
// template <traits::fsm_event_state_pair_c PT, traits::fsm_event_state_pair_c... PTs>
|
|
// void setStateFunc()
|
|
// {
|
|
// setStateFunc<PT>();
|
|
|
|
// if constexpr (sizeof...(PTs)) {
|
|
// setStateFunc<PTs...>();
|
|
// }
|
|
// }
|
|
|
|
|
|
// template <traits::fsm_event_state_pair_c PT>
|
|
// void setStateFunc()
|
|
// {
|
|
// using ev_t = typename PT::first_type;
|
|
// using st_t = typename PT::second_type;
|
|
|
|
// state_func_umap_t<ev_t>[this][_currentStateIDX] = [](ev_t event) {
|
|
// st_t state;
|
|
|
|
// if constexpr (requires(st_t inst) {
|
|
// { inst.enter(std::declval<ev_t>()) };
|
|
// }) {
|
|
// state.enter(std::move(event));
|
|
// } else {
|
|
// state.enter();
|
|
// }
|
|
// };
|
|
// }
|
|
|
|
|
|
// merge N std::tuple types with filtering dublicates
|
|
// (NOTE: the first std::tuple type must contain unique types!!!)
|
|
template <typename... TplTs>
|
|
struct merge_tuples_t;
|
|
|
|
// template <>
|
|
// struct merge_tuples_t<std::tuple<>> {
|
|
// using res_t = std::tuple<>;
|
|
// };
|
|
|
|
template <typename TplT>
|
|
struct merge_tuples_t<TplT> {
|
|
using res_t = TplT;
|
|
};
|
|
|
|
template <typename TplT1, typename TplT2>
|
|
struct merge_tuples_t<TplT1, TplT2> {
|
|
using res_t = TplT1;
|
|
};
|
|
|
|
template <typename... T1, typename T2, typename... T2s>
|
|
struct merge_tuples_t<std::tuple<T1...>, std::tuple<T2, T2s...>>
|
|
: std::conditional_t<(std::same_as<T1, T2> || ...),
|
|
merge_tuples_t<std::tuple<T1...>, std::tuple<T2s...>>,
|
|
merge_tuples_t<std::tuple<T1..., T2>, std::tuple<T2s...>>> {
|
|
};
|
|
|
|
template <typename TplT1, typename TplT2, typename TplT3, typename... TplTs>
|
|
struct merge_tuples_t<TplT1, TplT2, TplT3, TplTs...>
|
|
: merge_tuples_t<typename merge_tuples_t<TplT1, TplT2>::res_t, TplT3, TplTs...> {
|
|
};
|
|
|
|
template <typename... TplTs>
|
|
using merge_tuples_res_t = typename merge_tuples_t<TplTs...>::res_t;
|
|
|
|
template <typename TplT>
|
|
struct variant_maker_t;
|
|
|
|
template <typename... Ts>
|
|
struct variant_maker_t<std::tuple<Ts...>> {
|
|
using variant_t = std::variant<Ts*...>;
|
|
};
|
|
|
|
|
|
|
|
// template <typename InTplT, typename OutTplT = std::tuple<>>
|
|
// struct deduce_states_t;
|
|
|
|
// template <traits::fsm_state_c... StTs, typename OutTplT>
|
|
// struct deduce_states_t<std::tuple<StTs...>, OutTplT> {
|
|
// using curr_collection_t =
|
|
// merge_tuples_res_t<OutTplT, std::tuple<StTs...>, typename StTs::transition_t::unique_states_t...>;
|
|
|
|
// using states_t = std::conditional_t<
|
|
// std::tuple_size_v<OutTplT> == std::tuple_size_v<curr_collection_t>,
|
|
// curr_collection_t,
|
|
// merge_tuples_res_t<curr_collection_t,
|
|
// typename deduce_states_t<merge_tuples_res_t<
|
|
// typename StTs::transition_t::unique_states_t...>>::curr_collection_t>>;
|
|
// };
|
|
|
|
|
|
template <bool stop, typename ResTplT, typename... InTplTs>
|
|
struct deduce_states_t;
|
|
|
|
template <typename ResTplT, typename... InTplTs>
|
|
struct deduce_states_t<true, ResTplT, InTplTs...> {
|
|
using states_t = ResTplT;
|
|
};
|
|
|
|
template <typename ResTplT, traits::fsm_state_c... StTs, typename... InTplTs>
|
|
struct deduce_states_t<false, ResTplT, std::tuple<StTs...>, InTplTs...> {
|
|
using curr_collection_t =
|
|
merge_tuples_res_t<ResTplT, std::tuple<StTs...>, typename StTs::transition_t::unique_states_t...>;
|
|
|
|
using states_t = typename deduce_states_t<std::tuple_size_v<ResTplT> == std::tuple_size_v<curr_collection_t>,
|
|
curr_collection_t,
|
|
typename StTs::transition_t::unique_states_t...>::states_t;
|
|
};
|
|
|
|
|
|
public:
|
|
// using states_t = typename deduce_states_t<std::tuple<InitStateT>>::states_t;
|
|
using states_t = typename deduce_states_t<false, std::tuple<>, std::tuple<InitStateT>>::states_t;
|
|
|
|
protected:
|
|
states_t _states;
|
|
typename variant_maker_t<states_t>::variant_t _currentStateVariant;
|
|
};
|
|
|
|
} // namespace mcc::fsm
|