// Copyright 2010 Christophe Henry // henry UNDERSCORE christophe AT hotmail DOT com // This is an extended version of the state machine available in the boost::mpl library // Distributed under the same license as the original. // Copyright for the original version: // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed // under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt) #include // back-end #include //front-end #include #include using namespace std; namespace msm = boost::msm; using namespace msm::front; using namespace msm::front::puml; namespace { // Flags. Allow information about a property of the current state struct PlayingPaused {}; struct CDLoaded {}; struct FirstSongPlaying {}; } namespace boost::msm::front::puml { // events, only if non-default structs template<> struct Event { Event(std::string name) : name(name) {} std::string name; }; // The list of FSM states, only if non-default template<> struct State : public msm::front::state<> { typedef boost::fusion::vector flag_list; // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } }; //actions. Must be provided template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; // guard conditions. Must be provided template<> struct Guard { template bool operator()(EVT const&, FSM&, SourceState&, TargetState&) { return true; } }; } namespace { // front-end: define the FSM structure struct playing_ : public msm::front::state_machine_def { typedef boost::fusion::vector flag_list; // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } BOOST_MSM_PUML_DECLARE_TABLE( R"( @startuml Player skinparam linetype polyline state Player{ [*]-> Song1 Song1 -> Song2 : NextSong Song2 -> Song1 : PreviousSong / start_prev_song [start_prev_song_guard] Song2 -> Song3 : NextSong / start_next_song Song3 -> Song2 : PreviousSong [start_prev_song_guard] } @enduml )" ) // Replaces the default no-transition response. template void no_transition(Event const&, FSM&, int) { } }; } namespace boost::msm::front::puml { // define submachine with desired back-end template<> struct State : public msm::back11::state_machine { }; } namespace boost::msm::front::puml { // states, only if non-default template<> struct State : public msm::front::state<> { typedef boost::fusion::vector flag_list; // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } }; template<> struct State : public msm::front::state<> { typedef boost::fusion::vector flag_list; // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } }; template<> struct State : public msm::front::interrupt_state < Event> { // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } }; template<> struct State : public msm::front::terminate_state<> { // optional template void on_entry(Event const&, FSM&) { } template void on_exit(Event const&, FSM&) { } }; //actions. Must be provided template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { // mark as deferring to avoid stack overflows in certain conditions typedef int deferring_action; template void operator()(EVT const&, FSM& fsm, SourceState&, TargetState&) const { Event e{}; fsm.defer_event(Event{}); } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT const&, FSM&, SourceState&, TargetState&) { } }; template<> struct Action { template void operator()(EVT&, FSM&, SourceState&, TargetState&) { } }; // guard conditions. Must be provided template<> struct Guard { template bool operator()(EVT const& evt, FSM& , SourceState&, TargetState&) { bool is_play = boost::any_cast>(&evt) != 0; return is_play; } }; } namespace { // front-end: define the FSM structure struct player_ : public msm::front::state_machine_def { // we want deferred events and no state requires deferred events (only the fsm in the // transition table), so the fsm does. typedef int activate_deferred_events; BOOST_MSM_PUML_DECLARE_TABLE( R"( @startuml Player skinparam linetype polyline state Player{ [*]-> Empty Stopped -> PlayingFsm : play / start_playback Stopped -> Open : open_close / open_drawer Stopped -> Stopped : stop Open -> Empty : open_close 'default defer is provided Open -> Open : -play / defer Empty --> Open : open_close / open_drawer Empty ---> Stopped : cd_detected '* is any event (Kleene) Empty -> Empty : -* / my_defer [is_play_event] PlayingFsm --> Stopped : stop / stop_playback PlayingFsm -> Paused : pause / pause_playback PlayingFsm --> Open : open_close / stop_playback, open_drawer Paused -> PlayingFsm : end_pause / resume_playback Paused --> Stopped : stop / stop_playback Paused --> Open : open_close / stop_playback, open_drawer -- ' a second region [*]-> AllOk AllOk -> ErrorMode : error_found / report_error ErrorMode -> AllOk : end_error / report_end_error AllOk -> ErrorTerminate : do_terminate } @enduml )" ) // Replaces the default no-transition response. template void no_transition(Event const&, FSM&,int) { } }; // Pick a back-end typedef msm::back11::state_machine player; void test( ) { player p; // needed to start the highest-level SM. This will call on_entry and mark the start of the SM p.start(); // test deferred event // deferred in Empty and Open, will be handled only after event cd_detected p.process_event(Event{}); assert(p.current_state()[0] == 2); //Empty //flags assert(p.is_flag_active() == false); p.process_event(Event{}); assert(p.current_state()[0] == 1); //Open p.process_event(Event{}); assert(p.current_state()[0] == 2); //Empty //deferred event should have been processed p.process_event(Event{"louie, louie"}); assert(p.current_state()[0] == 3); //Playing assert(p.get_state&>().current_state()[0] == 0);//Song1 //flags assert(p.is_flag_active() == true); assert(p.is_flag_active() == true); p.process_event(Event{}); assert(p.current_state()[0] == 3); //Playing assert(p.get_state&>().current_state()[0] == 1);//Song2 p.process_event(Event{}); assert(p.current_state()[0] == 3); //Playing assert(p.get_state&>().current_state()[0] == 2);// Song3 p.process_event(Event{}); assert(p.current_state()[0] == 3); //Playing assert(p.get_state&>().current_state()[0] == 1);// Song2 //flags assert(p.is_flag_active() == true); assert(p.is_flag_active() == false); p.process_event(Event{}); assert(p.current_state()[0] == 4); //Paused //flags assert(p.is_flag_active() == true); // go back to Playing p.process_event(Event{}); assert(p.current_state()[0] == 3); //Playing p.process_event(Event{}); assert(p.current_state()[0] == 4); //Paused p.process_event(Event{}); assert(p.current_state()[0] == 0); //Stopped //flags assert(p.is_flag_active() == false); assert(p.is_flag_active() == true); p.process_event(Event{}); assert(p.current_state()[0] == 0); //Stopped //test interrupt assert(p.current_state()[1] == 5); //AllOk p.process_event(Event{}); assert(p.current_state()[1] == 6); //ErrorMode // try generating more events p.process_event(Event{}); assert(p.current_state()[1] == 6); //ErrorMode assert(p.current_state()[0] == 0); //Stopped p.process_event(Event{}); assert(p.current_state()[1] == 5); //AllOk p.process_event(Event{}); assert(p.current_state()[0] == 3); //Playing //test terminate assert(p.current_state()[1] == 5); //AllOk p.process_event(Event{}); assert(p.current_state()[1] == 7); //ErrorTerminate // try generating more events p.process_event(Event{}); assert(p.current_state()[1] == 7); //ErrorTerminate assert(p.current_state()[0] == 3); //Playing } } int main() { test(); return 0; }