...
This commit is contained in:
parent
0295d93cd3
commit
8b1873b40b
@ -24,7 +24,7 @@ public:
|
||||
|
||||
template <traits::mcc_range_of_input_char_range R = decltype(LOGGER_DEFAULT_FORMAT)>
|
||||
MccSpdlogLogger(std::shared_ptr<spdlog::logger> logger, const R& pattern_range = LOGGER_DEFAULT_FORMAT)
|
||||
: _loggerSPtr(logger), _currentLogPatternRange(), _currentLogPattern()
|
||||
: _currentLogPatternRange(), _currentLogPattern(), _loggerSPtr(logger)
|
||||
{
|
||||
if (std::distance(pattern_range.begin(), pattern_range.end())) {
|
||||
std::ranges::copy(
|
||||
@ -46,25 +46,52 @@ public:
|
||||
virtual ~MccSpdlogLogger() = default;
|
||||
|
||||
|
||||
void setLogLevel(loglevel_t log_level) { _loggerSPtr->set_level(log_level); }
|
||||
void setLogLevel(loglevel_t log_level)
|
||||
{
|
||||
_loggerSPtr->set_level(log_level);
|
||||
}
|
||||
|
||||
loglevel_t getLogLevel() const { return _loggerSPtr->level(); }
|
||||
loglevel_t getLogLevel() const
|
||||
{
|
||||
return _loggerSPtr->level();
|
||||
}
|
||||
|
||||
void logMessage(loglevel_t level, const std::string& msg) { _loggerSPtr->log(level, msg); }
|
||||
void logMessage(loglevel_t level, const std::string& msg)
|
||||
{
|
||||
_loggerSPtr->log(level, msg);
|
||||
}
|
||||
|
||||
// specialized for given level methods
|
||||
|
||||
void logCritical(const std::string& msg) { logMessage(spdlog::level::critical, msg); }
|
||||
void logCritical(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::critical, msg);
|
||||
}
|
||||
|
||||
void logError(const std::string& msg) { logMessage(spdlog::level::err, msg); }
|
||||
void logError(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::err, msg);
|
||||
}
|
||||
|
||||
void logWarn(const std::string& msg) { logMessage(spdlog::level::warn, msg); }
|
||||
void logWarn(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::warn, msg);
|
||||
}
|
||||
|
||||
void logInfo(const std::string& msg) { logMessage(spdlog::level::info, msg); }
|
||||
void logInfo(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::info, msg);
|
||||
}
|
||||
|
||||
void logDebug(const std::string& msg) { logMessage(spdlog::level::debug, msg); }
|
||||
void logDebug(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::debug, msg);
|
||||
}
|
||||
|
||||
void logTrace(const std::string& msg) { logMessage(spdlog::level::trace, msg); }
|
||||
void logTrace(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::trace, msg);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logMessage(spdlog::level::level_enum level, spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
@ -149,7 +176,10 @@ protected:
|
||||
_loggerSPtr->set_pattern(_currentLogPattern);
|
||||
}
|
||||
|
||||
void addMarkToPatternIdx(const char* mark, size_t after_idx = 1) { addMarkToPatternIdx(std::string_view{mark}); }
|
||||
void addMarkToPatternIdx(const char* mark, size_t after_idx = 1)
|
||||
{
|
||||
addMarkToPatternIdx(std::string_view{mark}, after_idx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mcc::utils
|
||||
|
||||
@ -69,7 +69,10 @@ include_directories(${BSPLINES_INCLUDE_DIR})
|
||||
|
||||
set(MCC_LIBRARY_SRC1 mcc_generics.h mcc_defaults.h mcc_traits.h mcc_utils.h
|
||||
mcc_ccte_iers.h mcc_ccte_iers_default.h mcc_ccte_erfa.h mcc_telemetry.h
|
||||
mcc_angle.h mcc_pzone.h mcc_pzone_container.h)
|
||||
mcc_angle.h mcc_pzone.h mcc_pzone_container.h mcc_finite_state_machine.h)
|
||||
|
||||
list(APPEND MCC_LIBRARY_SRC1 mcc_spdlog.h)
|
||||
|
||||
set(MCC_LIBRARY1 mcc1)
|
||||
add_library(${MCC_LIBRARY1} INTERFACE ${MCC_LIBRARY_SRC1})
|
||||
target_compile_features(${MCC_LIBRARY1} INTERFACE cxx_std_23)
|
||||
|
||||
568
mcc/mcc_finite_state_machine.h
Normal file
568
mcc/mcc_finite_state_machine.h
Normal file
@ -0,0 +1,568 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/* MOUNT CONTROL COMPONENTS LIBRARY */
|
||||
|
||||
|
||||
/* FINITE-STATE MACHINE IMPLEMENTATION */
|
||||
|
||||
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
#include "mcc_traits.h"
|
||||
|
||||
namespace mcc::fsm
|
||||
{
|
||||
|
||||
/* error codes enum definition */
|
||||
|
||||
enum class MccFiniteStateMachineErrorCode : int { ERROR_OK, ERROR_UNREGISTERED_EVENT_TYPE, ERROR_UNHANDLED_TRANSITION };
|
||||
|
||||
} // namespace mcc::fsm
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<mcc::fsm::MccFiniteStateMachineErrorCode> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
|
||||
namespace mcc::fsm
|
||||
{
|
||||
|
||||
|
||||
/* error category definition */
|
||||
|
||||
// error category
|
||||
struct MccFiniteStateMachineCategory : public std::error_category {
|
||||
MccFiniteStateMachineCategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "ADC_GENERIC_DEVICE";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
MccFiniteStateMachineErrorCode err = static_cast<MccFiniteStateMachineErrorCode>(ec);
|
||||
|
||||
switch (err) {
|
||||
case MccFiniteStateMachineErrorCode::ERROR_OK:
|
||||
return "OK";
|
||||
case MccFiniteStateMachineErrorCode::ERROR_UNREGISTERED_EVENT_TYPE:
|
||||
return "unregistered event type";
|
||||
case MccFiniteStateMachineErrorCode::ERROR_UNHANDLED_TRANSITION:
|
||||
return "unhandled transition";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const MccFiniteStateMachineCategory& get()
|
||||
{
|
||||
static const MccFiniteStateMachineCategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline std::error_code make_error_code(MccFiniteStateMachineErrorCode ec)
|
||||
{
|
||||
return std::error_code(static_cast<int>(ec), MccFiniteStateMachineCategory::get());
|
||||
}
|
||||
|
||||
namespace traits
|
||||
{
|
||||
|
||||
/*
|
||||
The only requirement to Event-class is public-accepted static constant 'ID'
|
||||
*/
|
||||
template <typename T>
|
||||
concept fsm_event_c = requires { requires std::same_as<const std::string_view, decltype(T::ID)>; };
|
||||
|
||||
|
||||
/*
|
||||
The only requirements to State-class is public-accepted static constant 'ID' and
|
||||
definition of type transition_t
|
||||
*/
|
||||
template <typename T>
|
||||
concept fsm_state_c = std::is_default_constructible_v<T> && requires {
|
||||
requires std::same_as<const std::string_view, decltype(T::ID)>;
|
||||
typename T::transition_t;
|
||||
};
|
||||
|
||||
|
||||
// concept for std::pair
|
||||
template <typename T>
|
||||
concept fsm_pair_of_types_c =
|
||||
requires { []<typename T1, typename T2>(std::type_identity<std::pair<T1, T2>>) {}(std::type_identity<T>()); };
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept fsm_tuple_of_pairs_c =
|
||||
requires { []<fsm_pair_of_types_c... PTs>(std::type_identity<std::tuple<PTs...>>) {}(std::type_identity<T>()); };
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept fsm_tuple_of_events_c =
|
||||
requires { []<fsm_event_c... EvTs>(std::type_identity<std::tuple<EvTs...>>) {}(std::type_identity<T>()); };
|
||||
} // namespace traits
|
||||
|
||||
|
||||
/*
|
||||
* Event-to-State transition table definition
|
||||
* (I do not use here concepts from the above traits to avoid possible recursive concept problem)
|
||||
*/
|
||||
|
||||
template <traits::fsm_pair_of_types_c... PTs>
|
||||
struct fsm_transition_table_t;
|
||||
|
||||
template <traits::fsm_pair_of_types_c PT>
|
||||
struct fsm_transition_table_t<PT> {
|
||||
using events_t = std::tuple<typename PT::first_type>;
|
||||
|
||||
using event_state_pair_t = std::tuple<PT>;
|
||||
|
||||
using unique_states_t = std::tuple<typename PT::second_type>;
|
||||
|
||||
template <typename EvT>
|
||||
using find_state_by_event_t =
|
||||
std::conditional_t<std::same_as<EvT, typename PT::first_type>, typename PT::second_type, void>;
|
||||
};
|
||||
|
||||
|
||||
template <traits::fsm_pair_of_types_c PT, traits::fsm_pair_of_types_c... PTs>
|
||||
struct fsm_transition_table_t<PT, PTs...> {
|
||||
private:
|
||||
static constexpr bool non_unique_event = (std::same_as<typename PT::first_type, typename PTs::first_type> || ...);
|
||||
|
||||
public:
|
||||
using events_t =
|
||||
std::conditional_t<non_unique_event,
|
||||
typename fsm_transition_table_t<PTs...>::events_t,
|
||||
decltype(std::tuple_cat(std::declval<std::tuple<typename PT::first_type>>(),
|
||||
std::declval<typename fsm_transition_table_t<PTs...>::events_t>()))>;
|
||||
|
||||
using event_state_pair_t =
|
||||
std::conditional_t<non_unique_event,
|
||||
typename fsm_transition_table_t<PTs...>::event_state_pair_t,
|
||||
decltype(std::tuple_cat(
|
||||
std::declval<std::tuple<PT>>(),
|
||||
std::declval<typename fsm_transition_table_t<PTs...>::event_state_pair_t>()))>;
|
||||
|
||||
using unique_states_t = std::conditional_t<
|
||||
non_unique_event,
|
||||
typename fsm_transition_table_t<PTs...>::unique_states_t,
|
||||
std::conditional_t<(std::same_as<typename PT::second_type, typename PTs::second_type> || ...),
|
||||
typename fsm_transition_table_t<PTs...>::unique_states_t,
|
||||
decltype(std::tuple_cat(
|
||||
std::declval<std::tuple<typename PT::second_type>>(),
|
||||
std::declval<typename fsm_transition_table_t<PTs...>::unique_states_t>()))>>;
|
||||
|
||||
private:
|
||||
template <typename EvT, traits::fsm_tuple_of_pairs_c TplT>
|
||||
struct find_state_by_event;
|
||||
|
||||
template <typename LookEvT, typename EvT, typename StT>
|
||||
struct find_state_by_event<LookEvT, std::tuple<std::pair<EvT, StT>>> {
|
||||
using state_t = std::conditional_t<std::same_as<LookEvT, EvT>, StT, void>;
|
||||
};
|
||||
|
||||
template <typename LookEvT, typename EvT, typename StT, traits::fsm_pair_of_types_c... PairTs>
|
||||
struct find_state_by_event<LookEvT, std::tuple<std::pair<EvT, StT>, PairTs...>> {
|
||||
using state_t = std::conditional_t<std::same_as<LookEvT, EvT>,
|
||||
StT,
|
||||
typename find_state_by_event<LookEvT, std::tuple<PairTs...>>::state_t>;
|
||||
};
|
||||
|
||||
public:
|
||||
// template <typename EvT, traits::fsm_tuple_of_pairs_c TplT>
|
||||
template <typename EvT>
|
||||
using find_state_by_event_t = typename find_state_by_event<EvT, event_state_pair_t>::state_t;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Finite-state machine definition
|
||||
* (an idea is from https://codeberg.org/cmargiotta/compile-time-fsm)
|
||||
*/
|
||||
|
||||
class MccFiniteStateMachine
|
||||
{
|
||||
protected:
|
||||
/* helper types definition */
|
||||
|
||||
// merge N std::tuple types with filtering dublicates
|
||||
// (NOTE: the first std::tuple must contain unique types!!!)
|
||||
template <typename... TplTs>
|
||||
struct merge_tuples;
|
||||
|
||||
template <typename TplT>
|
||||
struct merge_tuples<TplT> {
|
||||
using result_t = TplT;
|
||||
};
|
||||
|
||||
template <typename TplT1, typename TplT2>
|
||||
struct merge_tuples<TplT1, TplT2> {
|
||||
using result_t = TplT1;
|
||||
};
|
||||
|
||||
template <typename... T1, typename T2, typename... T2s>
|
||||
struct merge_tuples<std::tuple<T1...>, std::tuple<T2, T2s...>>
|
||||
: std::conditional_t<(std::same_as<T1, T2> || ...),
|
||||
merge_tuples<std::tuple<T1...>, std::tuple<T2s...>>,
|
||||
merge_tuples<std::tuple<T1..., T2>, std::tuple<T2s...>>> {
|
||||
};
|
||||
|
||||
template <typename TplT1, typename TplT2, typename TplT3, typename... TplTs>
|
||||
struct merge_tuples<TplT1, TplT2, TplT3, TplTs...>
|
||||
: merge_tuples<typename merge_tuples<TplT1, TplT2>::result_t, TplT3, TplTs...> {
|
||||
};
|
||||
|
||||
template <typename... TplTs>
|
||||
using merge_tuples_t = typename merge_tuples<TplTs...>::result_t;
|
||||
|
||||
|
||||
// deduce all unique states from the initial one
|
||||
template <bool stop, typename ResTplT, typename... InTplTs>
|
||||
struct deduce_states;
|
||||
|
||||
template <typename ResTplT, typename... InTplTs>
|
||||
struct deduce_states<true, ResTplT, InTplTs...> {
|
||||
using states_t = ResTplT;
|
||||
};
|
||||
|
||||
template <typename ResTplT, traits::fsm_state_c... StTs, typename... InTplTs>
|
||||
struct deduce_states<false, ResTplT, std::tuple<StTs...>, InTplTs...> {
|
||||
using curr_collection_t = merge_tuples_t<ResTplT, std::tuple<StTs...>>;
|
||||
// using curr_collection_t =
|
||||
// merge_tuples_t<ResTplT, std::tuple<StTs...>, typename StTs::transition_t::unique_states_t...>;
|
||||
|
||||
using states_t = typename deduce_states<std::tuple_size_v<ResTplT> == std::tuple_size_v<curr_collection_t>,
|
||||
curr_collection_t,
|
||||
merge_tuples_t<typename StTs::transition_t::unique_states_t...>,
|
||||
// typename StTs::transition_t::unique_states_t...,
|
||||
InTplTs...>::states_t;
|
||||
};
|
||||
|
||||
template <traits::fsm_state_c InitStateT>
|
||||
using deduce_states_t = typename deduce_states<false, std::tuple<>, std::tuple<InitStateT>>::states_t;
|
||||
|
||||
|
||||
// deduce all unique events from the initial state transition table
|
||||
|
||||
template <bool stop, typename ResTplT, typename InTplT>
|
||||
struct deduce_events;
|
||||
|
||||
template <typename ResTplT, typename InTplT>
|
||||
struct deduce_events<true, ResTplT, InTplT> {
|
||||
using events_t = ResTplT;
|
||||
};
|
||||
|
||||
template <traits::fsm_tuple_of_events_c ResTplT, traits::fsm_state_c... StTs>
|
||||
struct deduce_events<false, ResTplT, std::tuple<StTs...>> {
|
||||
using curr_collection_t = merge_tuples_t<ResTplT, typename StTs::transition_t::events_t...>;
|
||||
|
||||
using events_t =
|
||||
typename deduce_events<std::tuple_size_v<ResTplT> == std::tuple_size_v<curr_collection_t>,
|
||||
curr_collection_t,
|
||||
merge_tuples_t<typename StTs::transition_t::unique_states_t...>>::events_t;
|
||||
};
|
||||
|
||||
template <traits::fsm_state_c InitStateT>
|
||||
using deduce_events_t = typename deduce_events<false, std::tuple<>, std::tuple<InitStateT>>::events_t;
|
||||
|
||||
template <typename TplT>
|
||||
struct variant_from_tuple;
|
||||
|
||||
template <typename... Ts>
|
||||
struct variant_from_tuple<std::tuple<Ts...>> {
|
||||
using variant_t = std::variant<Ts*...>;
|
||||
};
|
||||
|
||||
template <typename TplT>
|
||||
using variant_from_tuple_t = typename variant_from_tuple<TplT>::variant_t;
|
||||
|
||||
|
||||
// check if given event-type is in std::tuple of event-types
|
||||
template <traits::fsm_event_c EvT, typename EvTplT>
|
||||
struct in_tuple;
|
||||
|
||||
template <traits::fsm_event_c EvT, traits::fsm_event_c... EvTplTs>
|
||||
struct in_tuple<EvT, std::tuple<EvTplTs...>> : std::disjunction<std::is_same<EvT, EvTplTs>...> {
|
||||
};
|
||||
|
||||
template <traits::fsm_event_c EvT, typename EvTplT>
|
||||
static constexpr bool in_tuple_v = in_tuple<EvT, EvTplT>::value;
|
||||
|
||||
|
||||
template <traits::fsm_event_c EvT>
|
||||
inline static std::unordered_map<const MccFiniteStateMachine*, std::function<void(EvT&)>> _dispatchEventFunc{};
|
||||
|
||||
std::vector<std::function<void(MccFiniteStateMachine*, MccFiniteStateMachine*)>> _moveFunc{};
|
||||
std::vector<std::function<void(const MccFiniteStateMachine*, MccFiniteStateMachine*)>> _copyFunc{};
|
||||
std::vector<std::function<void(const MccFiniteStateMachine*)>> _destroyFunc{};
|
||||
|
||||
std::string_view _currentStateID;
|
||||
std::vector<std::string_view> _stateID{};
|
||||
std::vector<std::string_view> _eventID{};
|
||||
|
||||
std::recursive_mutex _transitionMutex{};
|
||||
std::future<void> _currentStateThreadFuture;
|
||||
|
||||
static MccFiniteStateMachine& copyInstance(const MccFiniteStateMachine* from, MccFiniteStateMachine* to)
|
||||
{
|
||||
if (from != to) {
|
||||
for (auto& func : from->_copyFunc) {
|
||||
func(from, to);
|
||||
}
|
||||
|
||||
to->_currentStateID = from->_currentStateID;
|
||||
to->_moveFunc = from->_moveFunc;
|
||||
to->_copyFunc = from->_copyFunc;
|
||||
to->_destroyFunc = from->_destroyFunc;
|
||||
|
||||
to->_stateID = from->_stateID;
|
||||
to->_eventID = from->_eventID;
|
||||
|
||||
to->_currentStateID = from->_currentStateID;
|
||||
}
|
||||
|
||||
return *to;
|
||||
}
|
||||
|
||||
static MccFiniteStateMachine& moveInstance(MccFiniteStateMachine* from, MccFiniteStateMachine* to)
|
||||
{
|
||||
if (from != to) {
|
||||
for (auto& func : from->_moveFunc) {
|
||||
func(from, to);
|
||||
}
|
||||
|
||||
to->_currentStateID = std::move(from->_currentStateID);
|
||||
to->_moveFunc = std::move(from->_moveFunc);
|
||||
to->_copyFunc = std::move(from->_copyFunc);
|
||||
to->_destroyFunc = std::move(from->_destroyFunc);
|
||||
|
||||
to->_stateID = std::move(from->_stateID);
|
||||
to->_eventID = std::move(from->_eventID);
|
||||
|
||||
to->_currentStateID = std::move(from->_currentStateID);
|
||||
}
|
||||
|
||||
return *to;
|
||||
}
|
||||
|
||||
public:
|
||||
template <traits::fsm_state_c InitStateT>
|
||||
constexpr MccFiniteStateMachine(InitStateT) : _currentStateID(InitStateT::ID)
|
||||
{
|
||||
using states_t = deduce_states_t<InitStateT>;
|
||||
auto states = std::make_shared<states_t>();
|
||||
|
||||
auto currentState = std::make_shared<variant_from_tuple_t<states_t>>();
|
||||
*currentState = &std::get<InitStateT>(*states);
|
||||
|
||||
_stateID = []<typename... STs>(std::tuple<STs...>&) {
|
||||
return std::vector<std::string_view>({STs::ID...});
|
||||
}(*states);
|
||||
|
||||
|
||||
|
||||
// setup dispatch event functions
|
||||
|
||||
using all_events_t = deduce_events_t<InitStateT>;
|
||||
|
||||
[states, currentState, this]<size_t... Is>(std::index_sequence<Is...>) {
|
||||
((_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>[this] =
|
||||
[states, currentState, this]<traits::fsm_event_c EvT>(EvT& event) {
|
||||
// to avoid effects of possible compiler optimizations
|
||||
// (here one needs to be sure that inside the lambda 'event' is used by reference)
|
||||
const auto p_event = &event;
|
||||
|
||||
std::visit(
|
||||
[p_event, states, currentState, this]<traits::fsm_state_c curr_state_t>(curr_state_t*) {
|
||||
using to_state_t = curr_state_t::transition_t::template find_state_by_event_t<EvT>;
|
||||
|
||||
if constexpr (!std::is_void_v<to_state_t>) {
|
||||
std::lock_guard lock(_transitionMutex);
|
||||
|
||||
// exit from current
|
||||
if constexpr (requires(curr_state_t inst) {
|
||||
{ inst.exit(std::declval<EvT&>()) };
|
||||
}) {
|
||||
std::get<curr_state_t>(*states).exit(*p_event);
|
||||
} else if constexpr (requires(curr_state_t inst) {
|
||||
{ inst.exit() };
|
||||
}) {
|
||||
std::get<curr_state_t>(*states).exit();
|
||||
}
|
||||
|
||||
// transit ...
|
||||
if constexpr (requires(EvT inst) {
|
||||
{ inst.onTransit() };
|
||||
}) {
|
||||
p_event->onTransit();
|
||||
}
|
||||
|
||||
*currentState = &std::get<to_state_t>(*states);
|
||||
_currentStateID = to_state_t::ID;
|
||||
|
||||
// enter to new
|
||||
if constexpr (requires(to_state_t inst) {
|
||||
{ inst.enter(std::declval<EvT&>()) };
|
||||
}) {
|
||||
std::get<to_state_t>(*states).enter(*p_event);
|
||||
} else if constexpr (requires(to_state_t inst) {
|
||||
{ inst.enter() };
|
||||
}) {
|
||||
std::get<to_state_t>(*states).enter();
|
||||
}
|
||||
} else {
|
||||
throw std::system_error(MccFiniteStateMachineErrorCode::ERROR_UNHANDLED_TRANSITION);
|
||||
}
|
||||
},
|
||||
*currentState);
|
||||
}),
|
||||
...);
|
||||
|
||||
(_moveFunc.emplace_back([](MccFiniteStateMachine* from, MccFiniteStateMachine* to) {
|
||||
_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>[to] =
|
||||
std::move(_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>[from]);
|
||||
}),
|
||||
...);
|
||||
|
||||
(_copyFunc.emplace_back([](const MccFiniteStateMachine* from, MccFiniteStateMachine* to) {
|
||||
_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>[to] =
|
||||
_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>[from];
|
||||
}),
|
||||
...);
|
||||
|
||||
(_destroyFunc.emplace_back([](const MccFiniteStateMachine* inst) {
|
||||
_dispatchEventFunc<std::tuple_element_t<Is, all_events_t>>.erase(inst);
|
||||
}),
|
||||
...);
|
||||
|
||||
(_eventID.emplace_back(std::tuple_element_t<Is, all_events_t>::ID), ...);
|
||||
}(std::make_index_sequence<std::tuple_size_v<all_events_t>>());
|
||||
|
||||
// call enter() method (if it exists) of the initial state
|
||||
std::visit(
|
||||
[]<traits::fsm_state_c curr_state_t>(curr_state_t* cstate) {
|
||||
if constexpr (requires(curr_state_t inst) {
|
||||
{ inst.enter() };
|
||||
}) {
|
||||
cstate->enter();
|
||||
}
|
||||
},
|
||||
*currentState);
|
||||
}
|
||||
|
||||
MccFiniteStateMachine(const MccFiniteStateMachine& other)
|
||||
{
|
||||
copyInstance(&other, this);
|
||||
}
|
||||
|
||||
MccFiniteStateMachine(MccFiniteStateMachine&& other)
|
||||
{
|
||||
moveInstance(&other, this);
|
||||
}
|
||||
|
||||
MccFiniteStateMachine& operator=(const MccFiniteStateMachine& other)
|
||||
{
|
||||
return copyInstance(&other, this);
|
||||
}
|
||||
|
||||
MccFiniteStateMachine& operator=(MccFiniteStateMachine&& other)
|
||||
{
|
||||
return moveInstance(&other, this);
|
||||
}
|
||||
|
||||
virtual ~MccFiniteStateMachine()
|
||||
{
|
||||
for (auto& func : _destroyFunc) {
|
||||
func(this);
|
||||
}
|
||||
}
|
||||
|
||||
template <traits::fsm_event_c EvT>
|
||||
auto dispatchEvent(EvT& event)
|
||||
{
|
||||
if (!_dispatchEventFunc<EvT>[this]) {
|
||||
throw std::system_error(MccFiniteStateMachineErrorCode::ERROR_UNREGISTERED_EVENT_TYPE);
|
||||
}
|
||||
|
||||
_dispatchEventFunc<EvT>[this](event);
|
||||
}
|
||||
|
||||
template <traits::fsm_event_c EvT>
|
||||
auto dispatchEvent(EvT&& event)
|
||||
{
|
||||
return dispatchEvent(event);
|
||||
}
|
||||
|
||||
template <traits::fsm_event_c EvT>
|
||||
auto dispatchEvent()
|
||||
requires std::default_initializable<EvT>
|
||||
{
|
||||
return dispatchEvent(EvT{});
|
||||
}
|
||||
|
||||
std::string_view currentStateID() const
|
||||
{
|
||||
return _currentStateID;
|
||||
}
|
||||
|
||||
|
||||
// returns IDs of all deduced unique states
|
||||
|
||||
template <mcc::traits::mcc_range_of_input_char_range R>
|
||||
R stateIDs() const
|
||||
{
|
||||
R r;
|
||||
for (auto& el : _stateID) {
|
||||
std::back_inserter(r) = {el.begin(), el.end()};
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> stateIDs() const
|
||||
{
|
||||
return stateIDs<std::vector<std::string_view>>();
|
||||
}
|
||||
|
||||
|
||||
// returns IDs of all deduced events
|
||||
|
||||
template <mcc::traits::mcc_range_of_input_char_range R>
|
||||
R eventIDs() const
|
||||
{
|
||||
R r;
|
||||
for (auto& el : _eventID) {
|
||||
std::back_inserter(r) = {el.begin(), el.end()};
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
std::vector<std::string_view> eventIDs() const
|
||||
{
|
||||
return eventIDs<std::vector<std::string_view>>();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace mcc::fsm
|
||||
@ -13,6 +13,8 @@
|
||||
|
||||
// #include "mcc_traits.h"
|
||||
#include "mcc_angle.h"
|
||||
#include "mcc_finite_state_machine.h"
|
||||
|
||||
|
||||
namespace mcc
|
||||
{
|
||||
@ -73,6 +75,27 @@ static consteval bool mccIsAltAzMount(const MccMountType type)
|
||||
// };
|
||||
|
||||
|
||||
|
||||
/* GENERIC LOGGER CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
concept mcc_logger_c = requires(T t, const T t_const) {
|
||||
{ t.logError(std::declval<const std::string&>()) };
|
||||
{ t.logDebug(std::declval<const std::string&>()) };
|
||||
{ t.logWarn(std::declval<const std::string&>()) };
|
||||
{ t.logInfo(std::declval<const std::string&>()) };
|
||||
};
|
||||
|
||||
|
||||
struct MccNullLogger {
|
||||
void logError(const std::string&) {}
|
||||
void logDebug(const std::string&) {}
|
||||
void logWarn(const std::string&) {}
|
||||
void logInfo(const std::string&) {}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/* FLOATING-POINT LIKE CLASS CONCEPT */
|
||||
|
||||
template <typename T>
|
||||
@ -450,6 +473,17 @@ struct mcc_telemetry_interface_t {
|
||||
return std::forward<SelfT>(self).telemetryData(std::move(pt));
|
||||
}
|
||||
|
||||
|
||||
// compute difference in coordinates:
|
||||
// dx = targetX - mountX
|
||||
// dy = targetY - mountY
|
||||
// where X and Y is in according to 'pair_kind' input parameter
|
||||
template <std::derived_from<mcc_telemetry_interface_t> SelfT>
|
||||
RetT targetToMountDiff(this SelfT&& self, MccCoordPairKind pair_kind, mcc_angle_c auto* dx, mcc_angle_c auto* dy)
|
||||
{
|
||||
std::forward<SelfT>(self).targetToMountDiff(pair_kind, dx, dy);
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_telemetry_interface_t() = default;
|
||||
};
|
||||
@ -515,8 +549,7 @@ concept mcc_hardware_c = requires(T t, const T t_const) {
|
||||
|
||||
// set positions (angles) of mount axes with given speeds
|
||||
// NOTE: exact interpretation (or even ignoring) of the given moving speeds is subject of a hardware-class
|
||||
// implementation.
|
||||
// e.g. it can be maximal speeds at slewing ramp
|
||||
// implementation, e.g. it can be maximal speeds at slewing ramp
|
||||
{ t.setPos(std::declval<typename T::axes_pos_t>()) } -> std::same_as<typename T::error_t>;
|
||||
|
||||
// get current positions and speeds (angles) of mount axes
|
||||
@ -651,22 +684,119 @@ template <typename T>
|
||||
concept mcc_pzone_container_c = std::derived_from<T, mcc_pzone_container_interface_t<typename T::error_t>>;
|
||||
|
||||
|
||||
template <mcc_error_c RetT>
|
||||
template <typename T>
|
||||
concept mcc_slewing_model_c = requires(T t, const T t_const) {
|
||||
requires mcc_error_c<typename T::error_t>;
|
||||
|
||||
// a class of slewing process parameters
|
||||
requires requires(typename T::slewing_params_t pars) {
|
||||
// slew mount to target and stop
|
||||
std::convertible_to<decltype(pars.slewAndStop), bool>;
|
||||
};
|
||||
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
|
||||
{ t.slewToTarget() } -> std::same_as<typename T::error_t>;
|
||||
{ t.stopSlewing() } -> std::same_as<typename T::error_t>;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_tracking_model_c = requires(T t, const T t_const) {
|
||||
requires mcc_error_c<typename T::error_t>;
|
||||
|
||||
// a class of tracking process parameters
|
||||
requires requires(typename T::tracking_params_t pars) {
|
||||
requires mcc_angle_c<decltype(pars.trackSpeedX)>;
|
||||
requires mcc_angle_c<decltype(pars.trackSpeedY)>;
|
||||
};
|
||||
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
|
||||
{ t.trackTarget() } -> std::same_as<typename T::error_t>;
|
||||
{ t.stopTracking() } -> std::same_as<typename T::error_t>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept mcc_guiding_model_c = requires(T t, const T t_const) {
|
||||
requires mcc_error_c<typename T::error_t>;
|
||||
|
||||
// a class of guiding process parameters
|
||||
requires requires(typename T::guiding_params_t pars) {
|
||||
// guide along both mount axis
|
||||
std::convertible_to<decltype(pars.dualAxisGuiding), bool>;
|
||||
};
|
||||
|
||||
{ t_const.name() } -> std::formattable<char>;
|
||||
|
||||
{ t.startGuidingTarget() } -> std::same_as<typename T::error_t>;
|
||||
{ t.stopGuidingTarget() } -> std::same_as<typename T::error_t>;
|
||||
};
|
||||
|
||||
|
||||
/* GENERIC MOUNT CLASS CONCEPT */
|
||||
|
||||
|
||||
|
||||
template <mcc_error_c RetT, typename StopReasonT>
|
||||
struct mcc_generic_mount_interface_t {
|
||||
virtual ~mcc_generic_mount_interface_t() = default;
|
||||
|
||||
RetT slew(mcc_celestial_point_c auto pt) {}
|
||||
// slew mount to target (target coordinates were defined in telemetry data)
|
||||
template <std::derived_from<mcc_generic_mount_interface_t> SelfT>
|
||||
RetT slewToTarget(this SelfT&& self, mcc_slewing_model_c auto model)
|
||||
{
|
||||
return std::forward<SelfT>(self).slewToTarget(std::move(model));
|
||||
}
|
||||
|
||||
// track target, i.e., the mount moves with celestial speed
|
||||
template <std::derived_from<mcc_generic_mount_interface_t> SelfT>
|
||||
RetT trackTarget(this SelfT&& self, mcc_tracking_model_c auto model)
|
||||
{
|
||||
return std::forward<SelfT>(self).trackTarget(std::move(model));
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_generic_mount_interface_t> SelfT>
|
||||
RetT startGuidingTarget(this SelfT&& self, mcc_guiding_model_c auto model)
|
||||
{
|
||||
return std::forward<SelfT>(self).startGuidingTarget(std::move(model));
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_generic_mount_interface_t> SelfT>
|
||||
RetT stopGuidingTarget(this SelfT&& self)
|
||||
{
|
||||
return std::forward<SelfT>(self).stopGuidingTarget();
|
||||
}
|
||||
|
||||
template <std::derived_from<mcc_generic_mount_interface_t> SelfT>
|
||||
RetT stopMount(this SelfT&& self, StopReasonT reason)
|
||||
{
|
||||
return std::forward<SelfT>(self).stopMount(std::move(reason));
|
||||
}
|
||||
|
||||
protected:
|
||||
mcc_generic_mount_interface_t() = default;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
concept mcc_generic_mount_c = mcc_telemetry_c<T> && requires(T t) {
|
||||
requires mcc_error_c<typename T::error_t>;
|
||||
|
||||
{ t.slewToTarget() } -> std::same_as<typename T::error_t>;
|
||||
{ t.guidingTarget() } -> std::same_as<typename T::error_t>;
|
||||
};
|
||||
template <typename T>
|
||||
concept mcc_generic_mount_c =
|
||||
std::derived_from<T, mcc_generic_mount_interface_t<typename T::error_t, typename T::stop_reason_t>> &&
|
||||
mcc_telemetry_c<T> && mcc_pzone_container_c<T>;
|
||||
|
||||
|
||||
// with logging methods
|
||||
template <typename T>
|
||||
concept mcc_generic_log_mount_c = mcc_generic_mount_c<T> && mcc_logger_c<T>;
|
||||
|
||||
|
||||
// Finite-state-machine
|
||||
template <typename T>
|
||||
concept mcc_generic_fsm_mount_c = mcc_generic_mount_c<T> && std::derived_from<T, fsm::MccFiniteStateMachine>;
|
||||
|
||||
template <typename T>
|
||||
concept mcc_generic_fsm_log_mount_c =
|
||||
mcc_generic_mount_c<T> && mcc_logger_c<T> && std::derived_from<T, fsm::MccFiniteStateMachine>;
|
||||
|
||||
|
||||
} // namespace mcc
|
||||
|
||||
185
mcc/mcc_spdlog.h
Normal file
185
mcc/mcc_spdlog.h
Normal file
@ -0,0 +1,185 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <spdlog/logger.h>
|
||||
#include <list>
|
||||
|
||||
#include "mcc_traits.h"
|
||||
|
||||
|
||||
namespace mcc::utils
|
||||
{
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
/* SPDLOG-library based advanced single/multithreaded logger */
|
||||
|
||||
class MccSpdlogLogger
|
||||
{
|
||||
public:
|
||||
// [year-month-day time.millisecs][log-level]: log-message
|
||||
constexpr static std::array LOGGER_DEFAULT_FORMAT = {"[%Y-%m-%d %T.%e]"sv, "[%l]"sv, ": "sv, "%v"sv};
|
||||
|
||||
typedef spdlog::level::level_enum loglevel_t;
|
||||
|
||||
template <traits::mcc_range_of_input_char_range R = decltype(LOGGER_DEFAULT_FORMAT)>
|
||||
MccSpdlogLogger(std::shared_ptr<spdlog::logger> logger, const R& pattern_range = LOGGER_DEFAULT_FORMAT)
|
||||
: _currentLogPatternRange(), _currentLogPattern(), _loggerSPtr(logger)
|
||||
{
|
||||
if (std::distance(pattern_range.begin(), pattern_range.end())) {
|
||||
std::ranges::copy(
|
||||
pattern_range | std::views::transform([](const auto& el) { return std::string(el.begin(), el.end()); }),
|
||||
std::back_inserter(_currentLogPatternRange));
|
||||
} else {
|
||||
std::ranges::copy(LOGGER_DEFAULT_FORMAT | std::views::transform([](const auto& el) {
|
||||
return std::string(el.begin(), el.end());
|
||||
}),
|
||||
std::back_inserter(_currentLogPatternRange));
|
||||
}
|
||||
|
||||
std::ranges::copy(std::views::join(_currentLogPatternRange), std::back_inserter(_currentLogPattern));
|
||||
|
||||
_loggerSPtr->set_pattern(_currentLogPattern);
|
||||
}
|
||||
|
||||
|
||||
virtual ~MccSpdlogLogger() = default;
|
||||
|
||||
|
||||
void setLogLevel(loglevel_t log_level)
|
||||
{
|
||||
_loggerSPtr->set_level(log_level);
|
||||
}
|
||||
|
||||
loglevel_t getLogLevel() const
|
||||
{
|
||||
return _loggerSPtr->level();
|
||||
}
|
||||
|
||||
void logMessage(loglevel_t level, const std::string& msg)
|
||||
{
|
||||
_loggerSPtr->log(level, msg);
|
||||
}
|
||||
|
||||
// specialized for given level methods
|
||||
|
||||
void logCritical(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::critical, msg);
|
||||
}
|
||||
|
||||
void logError(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::err, msg);
|
||||
}
|
||||
|
||||
void logWarn(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::warn, msg);
|
||||
}
|
||||
|
||||
void logInfo(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::info, msg);
|
||||
}
|
||||
|
||||
void logDebug(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::debug, msg);
|
||||
}
|
||||
|
||||
void logTrace(const std::string& msg)
|
||||
{
|
||||
logMessage(spdlog::level::trace, msg);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logMessage(spdlog::level::level_enum level, spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(level, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logCritical(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::critical, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <typename... ArgTs>
|
||||
void logError(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::err, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logWarn(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::warn, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logInfo(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::info, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logDebug(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::debug, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
template <traits::mcc_formattable... ArgTs>
|
||||
void logTrace(spdlog::format_string_t<ArgTs...> fmt, ArgTs&&... args)
|
||||
{
|
||||
_loggerSPtr->log(spdlog::level::trace, fmt, std::forward<ArgTs>(args)...);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::list<std::string> _currentLogPatternRange;
|
||||
std::string _currentLogPattern;
|
||||
std::shared_ptr<spdlog::logger> _loggerSPtr;
|
||||
|
||||
|
||||
// helper methods
|
||||
|
||||
auto getThreadId() const
|
||||
{
|
||||
std::ostringstream st;
|
||||
st << std::this_thread::get_id();
|
||||
return st.str();
|
||||
}
|
||||
|
||||
// 'after_idx' is 0-based index!
|
||||
void addMarkToPatternIdx(const traits::mcc_input_char_range auto& mark, size_t after_idx = 1)
|
||||
requires(!std::is_pointer_v<std::decay_t<decltype(mark)>>)
|
||||
{
|
||||
if (!std::distance(mark.begin(), mark.end())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto it = _currentLogPatternRange.begin();
|
||||
size_t idx = 0;
|
||||
while (it != _currentLogPatternRange.end()) {
|
||||
++it;
|
||||
if (idx == after_idx)
|
||||
break;
|
||||
|
||||
++idx;
|
||||
}
|
||||
|
||||
_currentLogPatternRange.emplace(it, mark.begin(), mark.end());
|
||||
|
||||
_currentLogPattern.clear();
|
||||
std::ranges::copy(std::views::join(_currentLogPatternRange), std::back_inserter(_currentLogPattern));
|
||||
|
||||
_loggerSPtr->set_pattern(_currentLogPattern);
|
||||
}
|
||||
|
||||
void addMarkToPatternIdx(const char* mark, size_t after_idx = 1)
|
||||
{
|
||||
addMarkToPatternIdx(std::string_view{mark}, after_idx);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mcc::utils
|
||||
@ -26,7 +26,8 @@ enum MccTelemetryErrorCode : int {
|
||||
ERROR_PCM_COMP,
|
||||
ERROR_HARDWARE_GETPOS,
|
||||
ERROR_UPDATE_STOPPED,
|
||||
ERROR_DATA_TIMEOUT
|
||||
ERROR_DATA_TIMEOUT,
|
||||
ERROR_UNSUPPORTED_COORD_PAIR
|
||||
};
|
||||
|
||||
} // namespace mcc
|
||||
@ -75,6 +76,8 @@ struct MccTelemetryCategory : public std::error_category {
|
||||
return "telemetry update was stopped";
|
||||
case MccTelemetryErrorCode::ERROR_DATA_TIMEOUT:
|
||||
return "a timeout occured while waiting for new data";
|
||||
case MccTelemetryErrorCode::ERROR_UNSUPPORTED_COORD_PAIR:
|
||||
return "unsupported coordinate pair";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
@ -559,6 +562,25 @@ public:
|
||||
}
|
||||
|
||||
|
||||
error_t targetToMountDiff(MccCoordPairKind pair_kind, mcc_angle_c auto* dx, mcc_angle_c auto* dy)
|
||||
{
|
||||
std::lock_guard lock{*_updateMutex};
|
||||
|
||||
if (pair_kind == MccCoordPairKind::COORDS_KIND_AZALT || pair_kind == MccCoordPairKind::COORDS_KIND_AZZD) {
|
||||
*dx = (double)_data.target.AZ - (double)_data.AZ;
|
||||
*dy = (double)_data.target.ALT - (double)_data.ALT;
|
||||
} else if (pair_kind == MccCoordPairKind::COORDS_KIND_HADEC_APP ||
|
||||
pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_APP ||
|
||||
pair_kind == MccCoordPairKind::COORDS_KIND_RADEC_ICRS) {
|
||||
*dx = (double)_data.target.HA - (double)_data.HA;
|
||||
*dy = (double)_data.target.DEC_APP - (double)_data.DEC_APP;
|
||||
} else {
|
||||
return MccTelemetryErrorCode::ERROR_UNSUPPORTED_COORD_PAIR;
|
||||
}
|
||||
|
||||
return MccTelemetryErrorCode::ERROR_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<std::atomic_bool> _isDataUpdated;
|
||||
MccTelemetryData _data;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user