I am new to finite state machines and I'm trying to understand if I should use a hierarchical state machine, or stick with the flat structure I've got to model my problem in the simplest way.
I have an "analyzer" which can be either stopped, analyzing input, analyzing output, analyzing input and output, or clearing down. I have some analyzer states in an enum
called analyzer_states
and the transitions between states in analyzer_transitions
.
The question is: could I use a hierarchical state machine to model this more simply? Or maybe a FSM is not helping with this problem?
#include <iostream>
#include <cstdlib>
#include <string>
#include "fsm.h"
enum class analyzer_transitions
{
start_input,
start_output,
clear,
stop_input,
stop_output,
resume
};
enum class analyzer_states
{
analyzing_both,
analyzing_input,
analyzing_output,
clearing_both,
clearing_input,
clearing_output,
stopped,
};
inline auto output(std::string s)
{
return [s]()
{
std::cout << s << std::endl;
};
}
int main()
{
fsm<analyzer_states, analyzer_transitions> machine{ analyzer_states::stopped };
machine.add(
analyzer_states::stopped, analyzer_transitions::start_input, analyzer_states::analyzing_input,
output("-> analyzing_input"));
machine.add(
analyzer_states::stopped, analyzer_transitions::start_output, analyzer_states::analyzing_output,
output("-> analyzing_output"));
machine.add(
analyzer_states::analyzing_input, analyzer_transitions::stop_input, analyzer_states::stopped,
output("-> stopped"));
machine.add(
analyzer_states::analyzing_output, analyzer_transitions::stop_output, analyzer_states::stopped,
output("-> stopped"));
machine.add(
analyzer_states::analyzing_both, analyzer_transitions::stop_input, analyzer_states::analyzing_output,
output("-> analyzing_output"));
machine.add(
analyzer_states::analyzing_both, analyzer_transitions::stop_output, analyzer_states::analyzing_input,
output("-> analyzing_input"));
machine.add(
analyzer_states::analyzing_input, analyzer_transitions::start_output, analyzer_states::analyzing_both,
output("-> analyzing_both"));
machine.add(
analyzer_states::analyzing_output, analyzer_transitions::start_input, analyzer_states::analyzing_both,
output("-> analyzing_both"));
machine.add(
analyzer_states::analyzing_input, analyzer_transitions::clear, analyzer_states::clearing_input,
output("-> clearing_input"));
machine.add(
analyzer_states::analyzing_output, analyzer_transitions::clear, analyzer_states::clearing_output,
output("-> clearing_output"));
machine.add(
analyzer_states::analyzing_both, analyzer_transitions::clear, analyzer_states::clearing_both,
output("-> clearing_both"));
machine.add(
analyzer_states::clearing_input, analyzer_transitions::resume, analyzer_states::analyzing_input,
output("-> analyzing_input"));
machine.add(
analyzer_states::clearing_output, analyzer_transitions::resume, analyzer_states::analyzing_output,
output("-> analyzing_output"));
machine.add(
analyzer_states::clearing_both, analyzer_transitions::resume, analyzer_states::analyzing_both,
output("-> analyzing_both"));
machine.to(analyzer_transitions::start_input);
machine.to(analyzer_transitions::start_output);
machine.to(analyzer_transitions::stop_input);
machine.to(analyzer_transitions::clear);
machine.to(analyzer_transitions::resume);
std::cout << "Done...";
std::getchar();
return EXIT_SUCCESS;
}
The output is:
-> analyzing_input
-> analyzing_both
-> analyzing_output
-> clearing_output
-> analyzing_output
fsm.h:
#include <map>
#include <functional>
#include <atomic>
template <typename State, typename Transition>
class fsm
{
using handler_t = std::function<void()>;
class from_t
{
Transition transition_;
State state_;
public:
from_t(Transition transition, State state) :
transition_{ transition },
state_{ state }
{
}
bool operator<(const from_t& other) const
{
if (transition_ < other.transition_)
{
return true;
}
else if (transition_ > other.transition_)
{
return false;
}
else
{
return state_ < other.state_;
}
}
};
class to_t
{
State state_;
handler_t handler_;
public:
to_t(State state, handler_t handler) :
state_{ state },
handler_{ handler }
{
}
State state() const { return state_; }
void operator()() const { handler_(); }
};
std::map<from_t, to_t> transitions_;
std::atomic<State> state_;
public:
fsm(State initial_state) :
state_{ initial_state }
{
}
void add(State from, Transition transition, State to, handler_t handler)
{
transitions_.insert({ { transition, from }, { to, handler } });
}
void add(State from, Transition transition, State to)
{
add(transition, to, from, [] {});
}
bool to(Transition transition)
{
auto found = transitions_.find({ transition, state_ });
if (found != transitions_.end())
{
auto& to = found->second;
state_ = to.state();
to();
return true;
}
else
{
return false;
}
}
};