Deferred Events
UML supports a deferred events function. Consider the following diagram.
When an event occurs in the state that has a defer description that corresponds to the event, it will be preserved. And when the transition will reach a state that doesn't have a defer description that corresponds to the deferred event, it will be invoked.
For example, when Event1 occurs twice in State1 in the diagram above, they are preserved. And then, when Event2 occurs, they are still preserved. Because State2 also has the defer description. When Event2 occurs once more, transitions occur in order of State3, State4, and State5.
Boost.Msm supports deferred events directly. To use the deferred events, you need to insert the following typedef in the definition of your state machine.
typedef int activate_deferred_events;
And then, place the msmf::Defer into the Action field in the transition table. As with a normal action, you can write a guard function object. If you write that, the event is preserved only when the guard condition is satisfied.
struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1, Event1, msmf::none, msmf::Defer, msmf::none >, msmf::Row < State2, Event1, msmf::none, msmf::Defer, msmf::none >,
The following code corresponds to the diagram above.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // ----- Events struct Event1 {}; struct Event2 {}; // ----- State machine struct Sm1_:msmf::state_machine_def<Sm1_> { // States struct State1:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State1::on_entry()" << std::endl; } }; struct State2:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State2::on_entry()" << std::endl; } }; struct State3:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State3::on_entry()" << std::endl; } }; struct State4:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State4::on_entry()" << std::endl; } }; struct State5:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State5::on_entry()" << std::endl; } }; struct State6:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State6::on_entry()" << std::endl; } }; // Set initial state typedef State1 initial_state; // Enable deferred capability typedef int activate_deferred_events; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1, Event1, msmf::none, msmf::Defer, msmf::none >, msmf::Row < State2, Event1, msmf::none, msmf::Defer, msmf::none >, msmf::Row < State1, Event2, State2, msmf::none, msmf::none >, msmf::Row < State2, Event2, State3, msmf::none, msmf::none >, msmf::Row < State3, Event1, State4, msmf::none, msmf::none >, msmf::Row < State4, Event1, State5, msmf::none, msmf::none >, msmf::Row < State5, Event1, State6, msmf::none, msmf::none > > {}; }; // Pick a back-end typedef msm::back::state_machine<Sm1_> Sm1; void test() { Sm1 sm1; sm1.start(); std::cout << "> Send Event1" << std::endl; sm1.process_event(Event1()); std::cout << "> Send Event1" << std::endl; sm1.process_event(Event1()); std::cout << "> Send Event2" << std::endl; sm1.process_event(Event2()); std::cout << "> Send Event2" << std::endl; sm1.process_event(Event2()); } } int main() { test(); return 0; } // Output: // // State1::on_entry() // > Send Event1 // > Send Event1 // > Send Event2 // State2::on_entry() // > Send Event2 // State3::on_entry() // State4::on_entry() // State5::on_entry()
When the Event1 occurs in State1, the event is preserved. And then, when Event2 occurs, the current state transitions to State2. State2 has two outgoing transitions. One has Event1, the other is anonymous transition. According to the priority the anonymous transition is chosen. As a result, transitions occur in order of State3 and State4.
#include <iostream> #include <boost/msm/back/state_machine.hpp> #include <boost/msm/front/state_machine_def.hpp> #include <boost/msm/front/functor_row.hpp> namespace { namespace msm = boost::msm; namespace msmf = boost::msm::front; namespace mpl = boost::mpl; // ----- Events struct Event1 {}; struct Event2 {}; // ----- State machine struct Sm1_:msmf::state_machine_def<Sm1_> { // States struct State1:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State1::on_entry()" << std::endl; } }; struct State2:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State2::on_entry()" << std::endl; } }; struct State3:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State3::on_entry()" << std::endl; } }; struct State4:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State4::on_entry()" << std::endl; } }; struct State5:msmf::state<> { template <class Event,class Fsm> void on_entry(Event const&, Fsm&) const { std::cout << "State5::on_entry()" << std::endl; } }; // Set initial state typedef State1 initial_state; // Enable deferred capability typedef int activate_deferred_events; // Transition table struct transition_table:mpl::vector< // Start Event Next Action Guard msmf::Row < State1, Event1, msmf::none, msmf::Defer, msmf::none >, msmf::Row < State1, Event2, State2, msmf::none, msmf::none >, msmf::Row < State2, msmf::none, State3, msmf::none, msmf::none >, msmf::Row < State3, Event1, State4, msmf::none, msmf::none >, msmf::Row < State2, Event1, State5, msmf::none, msmf::none > > {}; }; // Pick a back-end typedef msm::back::state_machine<Sm1_> Sm1; void test() { Sm1 sm1; sm1.start(); std::cout << "> Send Event1" << std::endl; sm1.process_event(Event1()); std::cout << "> Send Event2" << std::endl; sm1.process_event(Event2()); } } int main() { test(); return 0; } // Output: // // State1::on_entry() // > Send Event1 // > Send Event2 // State2::on_entry() // State3::on_entry() // State4::on_entry()
page revision: 13, last edited: 14 Mar 2012 14:01
why no display "State5::on_entry()" in second sample
Because an anonymous transition has higher priority than deferred events. In the second example, the transition from State2 to State3 is an anonymous transition. 'Anonymous transition' means transition without event.
When the current state is State2, there are two outgoing transitions. One is the anonymous transition, the other is to State5 transition that have Event1 as a trigger. Then, the anonymous transition is chosen. Deferred event is not consumed. After transitioning to State3, deferred event is fired. So the transition from State3 to State4 is occurred. That why, State5::on_entry() is not displayed.
So, how can we handle "ANY OTHER" events?
In this example if we would want that for event 1 and 2 we do state changes, and for ANY other will be deferred..
Could you give me a concrete scenario? Which is the current state and which event occur?
I understand the point of the question. AFAIK, there is no direct way to defer "ANY OTHER" events. You need to list up all event that you want to defer. Other events are consumed.