77c623d8b8
[SVN r67936]
244 lines
12 KiB
C++
244 lines
12 KiB
C++
// 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>
|
|
#define FUSION_MAX_VECTOR_SIZE 20
|
|
|
|
#include "boost/mpl/vector/vector50.hpp"
|
|
#include <boost/msm/back/state_machine.hpp>
|
|
#include <boost/msm/front/state_machine_def.hpp>
|
|
|
|
#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_,
|
|
::boost::msm::back::favor_compile_time> iPod;
|
|
|
|
// Concrete FSM implementation
|
|
struct iPod_ : public msm::front::state_machine_def<iPod_>
|
|
{
|
|
// The list of FSM states
|
|
struct NotHolding : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: NotHolding" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotHolding" << std::endl;}
|
|
};
|
|
struct Holding : public msm::front::interrupt_state<NoHold>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: Holding" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: Holding" << std::endl;}
|
|
};
|
|
struct NotPlaying : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: NotPlaying" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: NotPlaying" << std::endl;}
|
|
};
|
|
struct NoMenuMode : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: NoMenuMode" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoMenuMode" << std::endl;}
|
|
};
|
|
struct NoOnOffButton : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: NoOnOffButton" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: NoOnOffButton" << std::endl;}
|
|
};
|
|
struct OffDown : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: OffDown, start timer" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: OffDown, end timer" << std::endl;}
|
|
};
|
|
struct PlayerOff : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: PlayerOff" << std::endl;}
|
|
template <class Event,class FSM>
|
|
void on_exit(Event const&,FSM& ) {std::cout << "finishing: PlayerOff" << std::endl;}
|
|
};
|
|
struct CheckMiddleButton : public msm::front::state<>
|
|
{
|
|
template <class Event,class FSM>
|
|
void on_entry(Event const&,FSM& ) {std::cout << "starting: CheckMiddleButton" << std::endl;}
|
|
template <class Event,class FSM>
|
|
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<NotHolding,NotPlaying,NoMenuMode,NoOnOffButton,CheckMiddleButton>
|
|
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<iPod*>(this))->process_event(MenuButton());
|
|
}
|
|
void send_StartSong(CloseMenu const&)
|
|
{
|
|
// we suppose the 5th song was selected
|
|
(static_cast<iPod*>(this))->process_event(StartSong(5));
|
|
}
|
|
void send_PlayPause(SouthReleased const&)
|
|
{
|
|
// action using the message queue to generate another event
|
|
(static_cast<iPod*>(this))->process_event(PlayPause());
|
|
}
|
|
void send_Off(OnOffTimer const&)
|
|
{
|
|
std::cout << "turning player off" << std::endl;
|
|
(static_cast<iPod*>(this))->process_event(Off());
|
|
}
|
|
void send_PlayingMiddleButton(MiddleButton const&)
|
|
{
|
|
(static_cast<iPod*>(this))->process_event(PlayingMiddleButton());
|
|
}
|
|
void send_MenuMiddleButton(MiddleButton const&)
|
|
{
|
|
(static_cast<iPod*>(this))->process_event(MenuMiddleButton());
|
|
}
|
|
// guard conditions
|
|
bool is_menu(MiddleButton const&)
|
|
{
|
|
return (static_cast<iPod*>(this))->is_flag_active<MenuActive>();
|
|
}
|
|
bool is_no_menu(MiddleButton const& evt)
|
|
{
|
|
return !is_menu(evt);
|
|
}
|
|
template <class EVENT>
|
|
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<PlayingMode_::
|
|
PlayingExit> , EndPlay , NotPlaying , &fsm::send_ActivateMenu >,
|
|
// +-------------------+---------------+-------------------+--------------------------------+----------------------+
|
|
_row < NoMenuMode , MenuButton , MenuMode >,
|
|
a_row < MenuMode::exit_pt<MenuMode_::
|
|
MenuExit> , 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 <class FSM,class Event>
|
|
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;
|
|
}
|