149 lines
3.5 KiB
C++
149 lines
3.5 KiB
C++
#include "fsm.h"
|
|
#include <cstdint>
|
|
|
|
/** States */
|
|
struct Idle { };
|
|
struct Active {
|
|
std::uint32_t session_id = 0;
|
|
};
|
|
|
|
/** Events */
|
|
struct EvStart {
|
|
std::uint32_t requested_session = 0;
|
|
};
|
|
struct EvStop { };
|
|
|
|
/** Context */
|
|
struct Context {
|
|
std::uint32_t starts = 0;
|
|
};
|
|
|
|
/** Names (optional) */
|
|
template <> struct name_of<Idle> {
|
|
static constexpr const char *value = "Idle";
|
|
};
|
|
template <> struct name_of<Active> {
|
|
static constexpr const char *value = "Active";
|
|
};
|
|
template <> struct name_of<EvStart> {
|
|
static constexpr const char *value = "EvStart";
|
|
};
|
|
template <> struct name_of<EvStop> {
|
|
static constexpr const char *value = "EvStop";
|
|
};
|
|
|
|
/** Logger */
|
|
struct MyLogger {
|
|
void ignored_transition (const char *from_state, const char *event) noexcept {
|
|
(void)from_state;
|
|
(void)event;
|
|
// printf("FSM ignored: %s + %s\n", from_state, event);
|
|
}
|
|
|
|
void unhandled_transition (const char *from_state, const char *event) noexcept {
|
|
(void)from_state;
|
|
(void)event;
|
|
// printf("FSM UNHANDLED: %s + %s\n", from_state, event);
|
|
}
|
|
|
|
void guard_blocked (const char *from_state, const char *event) noexcept {
|
|
(void)from_state;
|
|
(void)event;
|
|
// printf("FSM guard blocked: %s + %s\n", from_state, event);
|
|
}
|
|
};
|
|
|
|
/** Guard */
|
|
struct GuardStartAllowed {
|
|
bool operator() (Context &ctx, const Idle &, const EvStart &) const noexcept {
|
|
return ctx.starts < 10U;
|
|
}
|
|
};
|
|
|
|
/** Action */
|
|
struct ActionOnStart {
|
|
void operator() (Context &ctx, const Idle &, const Active &, const EvStart &) const noexcept {
|
|
++ctx.starts;
|
|
}
|
|
};
|
|
|
|
/** Factory specialization (NOTE: now includes Context in template parameters) */
|
|
template <>
|
|
struct state_factory<Context, Idle, EvStart, Active> {
|
|
static Active make (Context &, const Idle &, const EvStart &ev) {
|
|
Active a{};
|
|
a.session_id = ev.requested_session;
|
|
return a;
|
|
}
|
|
};
|
|
|
|
/** Hooks (optional) */
|
|
template <>
|
|
struct on_entry_state<Active> {
|
|
static void call (Context &, const Active &) noexcept {
|
|
// start timers, etc.
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct on_exit_state<Active> {
|
|
static void call (Context &, const Active &) noexcept {
|
|
// stop timers, etc.
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct on_entry_event<Active, EvStart> {
|
|
static void call (Context &, const Active &, const EvStart &) noexcept {
|
|
// entered Active due to EvStart
|
|
}
|
|
};
|
|
|
|
/** Transitions */
|
|
template <>
|
|
struct transition<Idle, EvStart> {
|
|
using type = transition_to<Active, GuardStartAllowed, ActionOnStart>;
|
|
};
|
|
|
|
template <>
|
|
struct transition<Active, EvStop> {
|
|
using type = transition_to<Idle>;
|
|
};
|
|
|
|
/**
|
|
* "Adult strictness": mark only required pairs strict.
|
|
* Everything else is ignore by default.
|
|
*/
|
|
template <>
|
|
struct missing_transition<Idle, EvStart> {
|
|
static constexpr missing_transition_policy policy = missing_transition_policy::strict;
|
|
};
|
|
|
|
template <>
|
|
struct missing_transition<Active, EvStop> {
|
|
static constexpr missing_transition_policy policy = missing_transition_policy::strict;
|
|
};
|
|
|
|
/** FSM type */
|
|
using MyFsm = fsm<Context, MyLogger, Idle, Active>;
|
|
|
|
static constexpr void validate_fsm_contract() {
|
|
MyFsm::validate_events<EvStart, EvStop>();
|
|
}
|
|
|
|
int main() {
|
|
validate_fsm_contract();
|
|
|
|
Context ctx{};
|
|
MyFsm f (ctx, Idle{}, MyLogger{});
|
|
|
|
f.dispatch (EvStart{42}); // Idle -> Active
|
|
f.dispatch (EvStop{}); // Active -> Idle
|
|
|
|
// Not required by strict contract => default ignore (logged via ignored_transition)
|
|
f.dispatch (EvStop{}); // Idle + EvStop => ignored
|
|
f.dispatch (EvStart{7}); // (if currently Active) Active + EvStart => ignored
|
|
|
|
return 0;
|
|
}
|