Boost 1.55 버전 기준으로 작성했습니다.
#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 msm = boost::msm;
namespace mpl = boost::mpl;
using namespace boost::msm::front;
// 이벤트들
struct play {};
struct end_pause{};
struct stop{};
struct pause {};
struct open_close {};
struct NextSong {};
struct PreviousSong {};
struct error_found {};
struct end_error {};
struct end_error2 {};
// 플래그, 현재 상태의 property에 관한 정보를 허용한다.
struct PlayingPaused {};
struct CDLoaded {};
struct FirstSongPlaying {};
// 약간의 데이터를 전달하는 완료된 이벤트 타입
struct cd_detected
{
cd_detected( std::string _name ) : name( _name )
{}
std::string name;
};
// front-end 로 정의돈 FSM 구조체
struct player_ : public msm::front::state_machine_def< player_ >
{
typedef int activate_deferred_events;
//
// FSM 상태들.
struct Empty : public msm::front::state<>
{
// 플레이 이벤트가 이 상태를 떠나면, 그 플레이 상태가 처리 되거나 또는 거부 될때까지 지연된다
//typedef mpl::vector<play> deferred_events;
// 모든(선택적인) entry/exit 함수는 전달된 이벤트를 갖는다.
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 빈 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 빈 상태" << std::endl; }
};
struct Open : public msm::front::state<>
{
// 플레이 이벤트가 이 상태를 떠나면, 그 플레이 상태가 처리 되거나 또는 거부 될때까지 지연된다
//typedef mpl::vector<play> deferred_events;
typedef mpl::vector1<CDLoaded> flag_list;
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 열린 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 열린 상태" << std::endl; }
};
struct Stopped : public msm::front::state<>
{
// 플레이 이벤트가 이 상태를 떠나면, 그 플레이 상태가 처리 되거나 또는 거부 될때까지 지연된다
typedef mpl::vector1<CDLoaded> flag_list;
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 정지된 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 정지된 상태" << std::endl; }
};
// 플레이어 상태 머신에 포함된 상태는 스스로 그러한 상태 머신이 된다.
// 당신이 보는것처럼, 플레잉은 따로 선언이 필요 없이
// 어디에서든 다른팀의 다른 모듈로 따로따로 개발되어질수 있다.
// 이것은 내가 간단히 plyaer 내부에 정의했다.
struct Playing_ : public msm::front::state_machine_def<Playing_>
{
// 플레이 중일때, cd 는 로딩완료되었고, 우리는 일시정지 또는 플레잉안에 있다.
typedef mpl::vector2< PlayingPaused, CDLoaded > flag_list;
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 플레이중 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 플레이중 상태" << std::endl; }
// FSM 상태 리스트들
struct Song1 : public msm::front::state<>
{
typedef mpl::vector1< FirstSongPlaying > flag_list;
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 첫번째 노래 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 첫번째 노래 상태" << std::endl; }
};
struct Song2 : public msm::front::state<>
{
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 두번째 노래 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 두번째 노래 상태" << std::endl; }
};
struct Song3 : public msm::front::state<>
{
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: 세번째 노래 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: 세번째 노래 상태" << std::endl; }
};
// 초기화 상태는 반드시 정의되야 함
typedef Song1 initial_state;
//
// 전이 행동들
void start_next_song( NextSong const& ) { std::cout << "재생중:다음곡 재생 시작" << std::endl; }
void start_prev_song( PreviousSong const& ) { std::cout << "재생중:이전곡 재생 시작" << std::endl; }
//
// 방어 조건들
//
// 전이 테이블을 쉽게 만들기 위해 typedef
typedef Playing_ pl;
// Playing_을 위한 전이 테이블
struct transition_table : mpl::vector4
<
// 시작, 이벤트, 다음, 전이 행동, 방어행동
// +-------------------+-------------------+-------------------+--------------------------+----------------+
a_row< Song1 , NextSong , Song2 , &pl::start_next_song >,
a_row< Song2 , PreviousSong , Song1 , &pl::start_prev_song >,
a_row< Song2 , NextSong , Song3 , &pl::start_next_song >,
a_row< Song3 , PreviousSong , Song2 , &pl::start_prev_song >
> {};
// 기본 no-transition 응답을 재정의
template< class FSM, class Event >
void no_transition( Event const& e, FSM&, int state )
{
std::cout << "no_transition from state " << state << " on evert" << typeid(e).name() << std::endl;
}
};
// back-end( 실질적인 인스턴스를 생성하는 것 )
typedef msm::back::state_machine< Playing_ > Playing;
// entry/exit 를 정의하지 않는 상태
struct Paused : public msm::front::state<>
{
typedef mpl::vector2< PlayingPaused, CDLoaded > flag_list;
};
// all ok
struct AllOK : public msm::front::state<>
{
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: All OK 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: All OK 상태" << std::endl; }
};
// 이 상태는 모든 이벤트가 막혔을 때 가게되는 만들어진 종점 상태이다.
struct ErrorMode : public msm::front::interrupt_state< end_error > // 에러모드는 중단 뿐이 없다. 되돌리려면 end_error 이벤트를 발생시킨다.
{
template< class Event, class FSM >
void on_entry(Event const&, FSM&) { std::cout << "입장: ErrorMode 상태" << std::endl; }
template< class Event, class FSM >
void on_exit( Event const& , FSM& ) { std::cout << "퇴장: ErrorMode 상태" << std::endl; }
};
// player 상태 머신의 초기화 상태
typedef mpl::vector< Empty, AllOK > initial_state;
// 전이 행돌들
void start_playback( play const& ) { std::cout << "CD 플레이어: 재생 시작" << std::endl; }
void open_drawer( open_close const& ) { std::cout << "CD 플레이어: CD drawer 열기" << std::endl; }
void close_drawer( open_close const& ) { std::cout << "CD 플레이어: CD drawer 닫기" << std::endl; }
void store_cd_info( cd_detected const& ) { std::cout << "CD 플레이어: CD 정보 읽기" << std::endl; }
void stop_playback( stop const& ) { std::cout << "CD 플레이어: 재생 중지" << std::endl; }
void pause_playback( pause const& ) { std::cout << "CD 플레이어: 재생 일시 정지" << std::endl; }
void resume_playback( end_pause const& ) { std::cout << "CD 플레이어: 다시 재생" << std::endl; }
void stop_and_open( open_close const& ) { std::cout << "CD 플레이어: 정지 후 CD drawer 열기" << std::endl; }
void stopped_again( stop const& ) { std::cout << "CD 플레이어: 다시 정지" << std::endl; }
void report_error( error_found const& ) { std::cout << "CD 플레이어: 문제 발생 알림" << std::endl; }
void report_end_error( end_error const& ) { std::cout << "CD 플레이어: 문제 종료 알림" << std::endl; }
// 전이 테이플 편하게 할라꼬
typedef player_ p;
struct transition_table : mpl::vector<
// Start Event Next Action Guard
// +--------------+------------------+----------------+------------------------+------------------------+
// 정지 상태
a_row < Stopped, play, Playing, &p::start_playback >,
a_row < Stopped, open_close, Open, &p::open_drawer >,
a_row < Stopped, stop, Stopped, &p::stopped_again >,
// +--------------+------------------+----------------+------------------------+------------------------+
// 열림 상태
a_row < Open, open_close, Empty, &p::close_drawer >,
Row < Open, play, none, Defer, none >,
// +--------------+------------------+----------------+------------------------+------------------------+
// 빈 상태
a_row < Empty, open_close, Open, &p::open_drawer >,
a_row < Empty, cd_detected, Stopped, &p::store_cd_info >,
Row < Empty, play, none, Defer, none >,
// +--------------+------------------+----------------+------------------------+------------------------+
// 재생 상태
a_row < Playing, stop, Stopped, &p::stop_playback >,
a_row < Playing, pause, Paused, &p::pause_playback >,
a_row < Playing, open_close, Open, &p::stop_and_open >,
// +--------------+------------------+----------------+------------------------+------------------------+
// 일지 정시 상태
a_row < Paused, end_pause, Playing, &p::resume_playback >,
a_row < Paused, stop, Stopped, &p::stop_playback >,
a_row < Paused, open_close, Open, &p::stop_and_open >,
// +--------------+------------------+----------------+------------------------+------------------------+
// 일지 정시 상태
a_row < AllOK, error_found, ErrorMode, &p::report_error >,
a_row < ErrorMode, end_error, AllOK, &p::report_end_error >
>{};
// 기본 no-transition 응답을 재정의
template< class FSM, class Event >
void no_transition( Event const& e, FSM&, int state )
{
std::cout << "no_transition from state " << state << " on evert" << typeid(e).name() << std::endl;
}
};
// back-end 를 뽑아내자
typedef msm::back::state_machine< player_ > player_state_machine;
static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused", "AllOK", "ErrorMode" };
void pstate(player_state_machine const& p)
{
for( unsigned int i = 0; i < player_state_machine::nr_regions::value; ++i )
{
std::cout << " -> " << state_names[ p.current_state()[i] ] << std::endl;
}
}
void msm_test()
{
player_state_machine psm;
// 고수준 상태 머신은 시작이 필요하단다. 이것은 on_entry와 상태 머신의 시작의 mark를 호출 할것이다.
psm.start();
std::cout << std::endl;
// 지연된 이벤트 테스트
// 무조건 cd_detected 이후에 처리되야 하는 empty 와 open안에서 지연됨
psm.process_event( play() );
// 플래그 테스트
std::cout << "CDLoaded active:" << std::boolalpha << psm.is_flag_active< CDLoaded >() << std::endl; // -> 실패 ( 여전히 CD가 없다. )
// 열기로 가자, empty의 on_exit 가 호출되고, 그리고 행동, 그리고 open의 on_entry가 호출된다.
psm.process_event( open_close() ); pstate( psm );
psm.process_event( open_close() ); pstate( psm );
psm.process_event( cd_detected( "louie, louie" ) );
//psm.process_event( cd_detected( "louie, louie" ) );
//psm.process_event( play() );
// 이 시점에, play는 active( 지연되었던 )??
std::cout << "PlayingPaused active:" << std::boolalpha << psm.is_flag_active< PlayingPaused >() << std::endl;
std::cout << "FirstSong active:" << std::boolalpha << psm.is_flag_active< FirstSongPlaying >() << std::endl;
psm.process_event( NextSong() ); pstate( psm );
std::cout << std::endl;
psm.stop();
printf( "end msm test... (press enter)\n" );
getchar();
}
Multi Index 샘플 (0) | 2016.01.31 |
---|---|
Boost MPL 샘플 (0) | 2016.01.29 |
Boost Meta State Machine 테스트 (0) | 2016.01.29 |
Boost lock-free Queue 성능 테스트 (0) | 2016.01.29 |
Boost Fusion 샘플 (0) | 2016.01.29 |