// 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 #include #include #include #define FUSION_MAX_VECTOR_SIZE 20 #include "boost/mpl/vector/vector50.hpp" #include #include #include "Events.hpp" #include "PlayingMode.hpp" #include "MenuMode.hpp" using namespace std; namespace msm = boost::msm; namespace // Concrete FSM implementation { struct iPod_; typedef msm::back::state_machine iPod; // Concrete FSM implementation struct iPod_ : public msm::front::state_machine_def { // The list of FSM states struct NotHolding : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: NotHolding" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotHolding" << std::endl;} }; struct Holding : public msm::front::interrupt_state { template void on_entry(Event const&,FSM& ) {std::cout << "starting: Holding" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: Holding" << std::endl;} }; struct NotPlaying : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: NotPlaying" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotPlaying" << std::endl;} }; struct NoMenuMode : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: NoMenuMode" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoMenuMode" << std::endl;} }; struct NoOnOffButton : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: NoOnOffButton" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoOnOffButton" << std::endl;} }; struct OffDown : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: OffDown, start timer" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: OffDown, end timer" << std::endl;} }; struct PlayerOff : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayerOff" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayerOff" << std::endl;} }; struct CheckMiddleButton : public msm::front::state<> { template void on_entry(Event const&,FSM& ) {std::cout << "starting: CheckMiddleButton" << std::endl;} template void on_exit(Event const&,FSM& ) {std::cout << "finishing: CheckMiddleButton" << std::endl;} }; // the initial state of the player SM. Must be defined typedef mpl::vector5 initial_state; // transition actions void send_ActivateMenu(EndPlay const&) { std::cout << "leaving Playing" << std::endl; // we need to activate the menu and simulate its button (static_cast(this))->process_event(MenuButton()); } void send_StartSong(CloseMenu const&) { // we suppose the 5th song was selected (static_cast(this))->process_event(StartSong(5)); } void send_PlayPause(SouthReleased const&) { // action using the message queue to generate another event (static_cast(this))->process_event(PlayPause()); } void send_Off(OnOffTimer const&) { std::cout << "turning player off" << std::endl; (static_cast(this))->process_event(Off()); } void send_PlayingMiddleButton(MiddleButton const&) { (static_cast(this))->process_event(PlayingMiddleButton()); } void send_MenuMiddleButton(MiddleButton const&) { (static_cast(this))->process_event(MenuMiddleButton()); } // guard conditions bool is_menu(MiddleButton const&) { return (static_cast(this))->is_flag_active(); } bool is_no_menu(MiddleButton const& evt) { return !is_menu(evt); } template void switch_on(EVENT const&) { std::cout << "turning player on" << std::endl; } typedef iPod_ fsm; // makes transition table cleaner // Transition table for player struct transition_table : mpl::vector< // Start Event Next Action Guard // +-------------------+---------------+-------------------+--------------------------------+----------------------+ _row < NotHolding , Hold , Holding >, _row < Holding , NoHold , NotHolding >, // +-------------------+---------------+-------------------+--------------------------------+----------------------+ _row < NotPlaying , PlayPause , PlayingMode >, a_row < PlayingMode::exit_pt , EndPlay , NotPlaying , &fsm::send_ActivateMenu >, // +-------------------+---------------+-------------------+--------------------------------+----------------------+ _row < NoMenuMode , MenuButton , MenuMode >, a_row < MenuMode::exit_pt , CloseMenu , NoMenuMode , &fsm::send_StartSong >, // +-------------------+---------------+-------------------+--------------------------------+----------------------+ _row < NoOnOffButton , SouthPressed , OffDown >, a_row < OffDown , SouthReleased , NoOnOffButton , &fsm::send_PlayPause >, a_row < OffDown , OnOffTimer , PlayerOff , &fsm::send_Off >, a_row < PlayerOff , SouthPressed , NoOnOffButton , &fsm::switch_on >, a_row < PlayerOff , NoHold , NoOnOffButton , &fsm::switch_on >, // +-------------------+---------------+--------------------+--------------------------------+----------------------+ row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_PlayingMiddleButton , &fsm::is_menu >, row < CheckMiddleButton , MiddleButton , CheckMiddleButton , &fsm::send_MenuMiddleButton , &fsm::is_no_menu > // +-------------------+---------------+--------------------+--------------------------------+----------------------+ > {}; // Replaces the default no-transition response. template void no_transition(Event const& e, FSM&,int state) { std::cout << "no transition from state " << state << " on event " << typeid(e).name() << std::endl; } }; void test() { iPod sm; sm.start(); // we first press Hold std::cout << "pressing hold" << std::endl; sm.process_event(Hold()); // pressing a button is now ignored std::cout << "pressing a button" << std::endl; sm.process_event(SouthPressed()); // or even one contained in a submachine sm.process_event(EastPressed()); // no more holding std::cout << "no more holding, end interrupt event sent" << std::endl; sm.process_event(NoHold()); std::cout << "pressing South button a short time" << std::endl; sm.process_event(SouthPressed()); // we suppose a short pressing leading to playing a song sm.process_event(SouthReleased()); // we move to the next song std::cout << "we move to the next song" << std::endl; sm.process_event(NextSong()); // then back to no song => exit from playing, menu active std::cout << "we press twice the West button (simulated)=> end of playing" << std::endl; sm.process_event(PreviousSong()); sm.process_event(PreviousSong()); // even in menu mode, pressing play will start playing the first song std::cout << "pressing play/pause" << std::endl; sm.process_event(SouthPressed()); sm.process_event(SouthReleased()); // of course pausing must be possible std::cout << "pressing play/pause" << std::endl; sm.process_event(SouthPressed()); sm.process_event(SouthReleased()); std::cout << "pressing play/pause" << std::endl; sm.process_event(SouthPressed()); sm.process_event(SouthReleased()); // while playing, you can fast forward std::cout << "pressing East button a long time" << std::endl; sm.process_event(EastPressed()); // let's suppose the timer just fired sm.process_event(ForwardTimer()); sm.process_event(ForwardTimer()); // end of fast forwarding std::cout << "releasing East button" << std::endl; sm.process_event(EastReleased()); // we now press the middle button to set playing at a given position std::cout << "pressing Middle button, fast forwarding disabled" << std::endl; sm.process_event(MiddleButton()); std::cout <<"pressing East button to fast forward" << std::endl; sm.process_event(EastPressed()); // we switch off and on std::cout <<"switch off player" << std::endl; sm.process_event(SouthPressed()); sm.process_event(OnOffTimer()); std::cout <<"switch on player" << std::endl; sm.process_event(SouthPressed()); } } int main() { test(); return 0; }