I am using Boost MSM to model the behavior of a robot. There are multiple states, as "ManualMove", "AutoMove", "Work", "Idle", etc... However I need to be able to force stop the robot from any state, putting it in state where it cannot move and cannot receive new commands, so I created an "EmergencyStopped" state. When the robot is asked to rearm itself, the robot should go back to the "Idle" state.
However, Boost does not recommend to create a transition from all state to a single one, and prefer to use orthogonal states.
So I could for example do "AllOk" and an interrupt_state
"EmergencyStopped" orthogonal states.
The problem is, while I can easily put the robot in "EmergencyStopped", I cannot quit it and put the robot into the "Idle" state from the state it was before. For example, if the robot do:
The robot will be in the state [Work, AllOk], while I want it to go in the state [Idle, AllOk].
So my questions are:
There is another solution. In your case, composite state is better choice. See the diagram.
Em-place all states that need to transition to "EmergencyStopped" if "stop" event is happend into "Normal" state. And put the transition that is from "Normal" to "EmergencyStopped". Its trigger event is "stop". This approach can avoid writing many transitions to "EmergencyStopped". Even if you would add other child state of "Normal", you don't need to add the transition for the added state. That is one of benefit of the composite state approach.
Set "Idle" state to initial_state
. It reflect to Initial puseudo state in UML state-machine diagram.
typedef mpl::vector<Idle> initial_state;
If transition target is parent state "Normal", then transition target state is "Idle" state because it is marked as initial_state
.
Those two technique solve your question.
Here is the complete code:
#include <iostream>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/static_assert.hpp>
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
// ----- Events
struct ev1 {};
struct ev2 {};
struct ev3 {};
struct stop {};
struct recover {};
// ----- State machine
struct YourSystem_:msmf::state_machine_def<YourSystem_>
{
struct Normal_:msmf::state_machine_def<Normal_>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Normal::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Normal::on_exit()" << std::endl;
}
struct Idle:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Idle::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Idle::on_exit()" << std::endl;
}
};
struct Work:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "Work::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "Work::on_exit()" << std::endl;
}
};
struct AllOk:msmf::state<> {
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "AllOk::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "AllOk::on_exit()" << std::endl;
}
};
// Set initial state
typedef mpl::vector<Idle> initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Idle, ev1, Work, msmf::none, msmf::none >,
msmf::Row < Work, ev2, AllOk, msmf::none, msmf::none >,
msmf::Row < AllOk, ev3, Idle, msmf::none, msmf::none >
> {};
};
struct EmergencyStopped:msmf::state<>
{
template <class Event,class Fsm>
void on_entry(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_entry()" << std::endl;
}
template <class Event,class Fsm>
void on_exit(Event const&, Fsm&) const {
std::cout << "EmergencyStopped::on_exit()" << std::endl;
}
};
typedef msm::back::state_machine<Normal_> Normal;
// Set initial state
typedef Normal initial_state;
// Transition table
struct transition_table:mpl::vector<
// Start Event Next Action Guard
msmf::Row < Normal, stop, EmergencyStopped, msmf::none, msmf::none >,
msmf::Row < EmergencyStopped, recover, Normal, msmf::none, msmf::none >
> {};
};
// Pick a back-end
typedef msm::back::state_machine<YourSystem_> Ys;
int main()
{
Ys ys;
ys.start();
std::cout << "> Send ev1()" << std::endl;
ys.process_event(ev1());
std::cout << "> Send ev2()" << std::endl;
ys.process_event(ev2());
std::cout << "> Send stop()" << std::endl;
ys.process_event(stop());
std::cout << "> Send recover()" << std::endl;
ys.process_event(recover());
}
and running demo https://wandbox.org/permlink/uBm6jTvG0YL3gSgl