| // 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 <vector> |
| #include <set> |
| #include <string> |
| #include <iostream> |
| // we need more than the default 20 states |
| #define FUSION_MAX_VECTOR_SIZE 20 |
| // we need more than the default 20 transitions |
| #include "boost/mpl/vector/vector50.hpp" |
| #include <boost/msm/back/state_machine.hpp> |
| #include <boost/msm/front/euml/euml.hpp> |
| |
| |
| using namespace std; |
| using namespace boost::msm::front::euml; |
| namespace msm = boost::msm; |
| |
| // attribute names and types |
| BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_Selected) |
| BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_SongIndex) |
| BOOST_MSM_EUML_DECLARE_ATTRIBUTE(int,m_NumberOfSongs) |
| #include "ipod_functors.hpp" |
| |
| |
| namespace // Concrete FSM implementation |
| { |
| //flags |
| BOOST_MSM_EUML_FLAG(MenuActive) |
| BOOST_MSM_EUML_FLAG(NoFastFwd) |
| // hardware-generated events |
| BOOST_MSM_EUML_EVENT(Hold) |
| BOOST_MSM_EUML_EVENT(NoHold) |
| BOOST_MSM_EUML_EVENT(SouthPressed) |
| BOOST_MSM_EUML_EVENT(SouthReleased) |
| BOOST_MSM_EUML_EVENT(MiddleButton) |
| BOOST_MSM_EUML_EVENT(EastPressed) |
| BOOST_MSM_EUML_EVENT(EastReleased) |
| BOOST_MSM_EUML_EVENT(Off) |
| BOOST_MSM_EUML_EVENT(MenuButton) |
| // internally defined events |
| BOOST_MSM_EUML_EVENT(PlayPause) |
| BOOST_MSM_EUML_EVENT(EndPlay) |
| struct CloseMenu_impl : euml_event<CloseMenu_impl> |
| { |
| CloseMenu_impl(){}//defined only for stt |
| template<class EVENT> |
| CloseMenu_impl(EVENT const &) {} |
| }; |
| CloseMenu_impl const CloseMenu; |
| BOOST_MSM_EUML_EVENT(OnOffTimer) |
| BOOST_MSM_EUML_EVENT(MenuMiddleButton) |
| BOOST_MSM_EUML_EVENT(SelectSong) |
| BOOST_MSM_EUML_EVENT(SongFinished) |
| |
| BOOST_MSM_EUML_ATTRIBUTES((attributes_ << m_Selected ), StartSongAttributes) |
| BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(StartSong,StartSongAttributes) |
| BOOST_MSM_EUML_EVENT(PreviousSong) |
| BOOST_MSM_EUML_EVENT(NextSong) |
| BOOST_MSM_EUML_EVENT(ForwardTimer) |
| BOOST_MSM_EUML_EVENT(PlayingMiddleButton) |
| |
| // Concrete iPod implementation |
| // The list of iPod states |
| BOOST_MSM_EUML_STATE(( NotHolding_Entry ),NotHolding) |
| BOOST_MSM_EUML_INTERRUPT_STATE(( NoHold,Holding_Entry ),Holding) |
| BOOST_MSM_EUML_STATE(( NotPlaying_Entry ),NotPlaying) |
| BOOST_MSM_EUML_STATE(( NoMenuMode_Entry ),NoMenuMode) |
| BOOST_MSM_EUML_STATE(( NoOnOffButton_Entry ),NoOnOffButton) |
| BOOST_MSM_EUML_STATE(( OffDown_Entry ),OffDown) |
| BOOST_MSM_EUML_STATE(( PlayerOff_Entry ),PlayerOff) |
| BOOST_MSM_EUML_STATE(( CheckMiddleButton_Entry ),CheckMiddleButton) |
| |
| // Concrete PlayingMode_ implementation |
| // The list of PlayingMode_ states |
| BOOST_MSM_EUML_STATE(( Playing_Entry ),Playing) |
| BOOST_MSM_EUML_STATE(( WaitingForNextPrev_Entry ),WaitingForNextPrev) |
| BOOST_MSM_EUML_STATE(( Paused_Entry ),Paused) |
| BOOST_MSM_EUML_STATE(( WaitingForEnd_Entry ),WaitingForEnd) |
| BOOST_MSM_EUML_STATE(( NoForward_Entry ),NoForward) |
| BOOST_MSM_EUML_STATE(( ForwardPressed_Entry,ForwardPressed_Exit ),ForwardPressed) |
| BOOST_MSM_EUML_STATE(( FastForward_Entry,FastForward_Exit ),FastForward) |
| BOOST_MSM_EUML_STATE(( StdDisplay_Entry ),StdDisplay) |
| BOOST_MSM_EUML_STATE(( SetPosition_Entry ),SetPosition) |
| BOOST_MSM_EUML_STATE(( SetMark_Entry ),SetMark) |
| BOOST_MSM_EUML_EXIT_STATE(( EndPlay,PlayingExit_Entry ),PlayingExit) |
| |
| //stt |
| BOOST_MSM_EUML_TRANSITION_TABLE(( |
| // +------------------------------------------------------------------------------+ |
| Paused == Playing + PlayPause , |
| Paused == Playing + Off , |
| Playing == Playing + StartSong |
| / (if_then_(event_(m_Selected) > Int_<0>() && |
| event_(m_Selected) < fsm_(m_NumberOfSongs), |
| fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , |
| Playing == Playing + SongFinished |
| / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ |
| show_playing_song, /*then*/ |
| (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay))/*else*/ ) ) , |
| Playing == Paused + PlayPause , |
| Playing == Paused + StartSong |
| / (if_then_(event_(m_Selected) > Int_<0>() && |
| event_(m_Selected) < fsm_(m_NumberOfSongs), |
| fsm_(m_SongIndex) = event_(m_Selected) ),show_selected_song) , |
| WaitingForNextPrev == WaitingForNextPrev+ PreviousSong |
| /( if_then_else_(--fsm_(m_SongIndex) > Int_<0>(), /*if*/ |
| show_playing_song, /*then*/ |
| (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ) , |
| WaitingForNextPrev == WaitingForNextPrev+ NextSong |
| / (if_then_else_(++fsm_(m_SongIndex) <= fsm_(m_NumberOfSongs), /*if*/ |
| show_playing_song, /*then*/ |
| (fsm_(m_SongIndex)=Int_<1>(),process_(EndPlay)) /*else*/ ) ), |
| |
| PlayingExit == WaitingForEnd + EndPlay , |
| ForwardPressed == NoForward + EastPressed [!is_flag_(NoFastFwd)] , |
| NoForward == ForwardPressed + EastReleased / process_(NextSong) , |
| FastForward == ForwardPressed + ForwardTimer / do_fast_forward , |
| FastForward == FastForward + ForwardTimer / do_fast_forward , |
| FastForward == NoForward + EastReleased , |
| SetPosition == StdDisplay + PlayingMiddleButton , |
| StdDisplay == SetPosition + StartSong , |
| SetMark == SetPosition + PlayingMiddleButton , |
| StdDisplay == SetMark + PlayingMiddleButton , |
| StdDisplay == SetMark + StartSong |
| // +------------------------------------------------------------------------------+ |
| ),playingmode_transition_table ) |
| |
| BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playingmode_transition_table, //STT |
| init_ << Playing << WaitingForNextPrev << WaitingForEnd |
| << NoForward << StdDisplay, // Init States |
| fsm_(m_NumberOfSongs)=Int_<5>(), // entry |
| no_action, // exit |
| attributes_ << m_SongIndex << m_NumberOfSongs, //attributes |
| configure_<< NoFastFwd // Flags, Deferred events, configuration |
| ),PlayingMode_) |
| |
| // choice of back-end |
| typedef msm::back::state_machine<PlayingMode_> PlayingMode_type; |
| PlayingMode_type const PlayingMode; |
| |
| // Concrete MenuMode_ implementation |
| // The list of MenuMode_ states |
| BOOST_MSM_EUML_STATE(( WaitingForSongChoice_Entry ),WaitingForSongChoice) |
| BOOST_MSM_EUML_STATE(( StartCurrentSong_Entry ),StartCurrentSong) |
| BOOST_MSM_EUML_EXIT_STATE(( CloseMenu,MenuExit_Entry ),MenuExit) |
| |
| //stt |
| BOOST_MSM_EUML_TRANSITION_TABLE(( |
| // +------------------------------------------------------------------------------+ |
| StartCurrentSong == WaitingForSongChoice + MenuMiddleButton , |
| MenuExit == StartCurrentSong + SelectSong |
| // +------------------------------------------------------------------------------+ |
| ),menumode_transition_table ) |
| |
| BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (menumode_transition_table, //STT |
| init_ << WaitingForSongChoice, // Init States |
| no_action, // entry |
| no_action, // exit |
| attributes_ << no_attributes_, //attributes |
| configure_<< MenuActive // Flags, Deferred events, configuration |
| ),MenuMode_) |
| |
| typedef msm::back::state_machine<MenuMode_> MenuMode_type; |
| MenuMode_type const MenuMode; |
| |
| // iPod stt |
| BOOST_MSM_EUML_TRANSITION_TABLE(( |
| // +------------------------------------------------------------------------------+ |
| Holding == NotHolding + Hold , |
| NotHolding == Holding + NoHold , |
| PlayingMode == NotPlaying + PlayPause , |
| NotPlaying == exit_pt_(PlayingMode,PlayingExit) + EndPlay |
| / process_(MenuButton) , |
| MenuMode == NoMenuMode + MenuButton , |
| NoMenuMode == exit_pt_(MenuMode,MenuExit)+ CloseMenu |
| / process2_(StartSong,Int_<5>()) , |
| OffDown == NoOnOffButton + SouthPressed , |
| NoOnOffButton == OffDown + SouthReleased |
| / process_(PlayPause) , |
| PlayerOff == OffDown + OnOffTimer |
| / (show_player_off,process_(Off)) , |
| NoOnOffButton == PlayerOff + SouthPressed / show_player_on , |
| NoOnOffButton == PlayerOff + NoHold / show_player_on , |
| CheckMiddleButton == CheckMiddleButton + MiddleButton |
| [is_flag_(MenuActive)] / process_(PlayingMiddleButton) , |
| CheckMiddleButton == CheckMiddleButton + MiddleButton |
| [!is_flag_(MenuActive)] / process_(PlayingMiddleButton) |
| // +------------------------------------------------------------------------------+ |
| ),ipod_transition_table ) |
| |
| BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( ipod_transition_table, //STT |
| init_ << NotHolding << NotPlaying << NoMenuMode |
| << NoOnOffButton << CheckMiddleButton |
| ), |
| iPod_) //fsm name |
| typedef msm::back::state_machine<iPod_> iPod; |
| |
| 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; |
| } |