/////////////////////////////////////////////////////////////////////////////
//
// File: FSMSample.cpp
//
// Created by MOON, Eui-kwon.
// Created on Aug-10th, 2011.
//
// 교육용으로작성한FSM Sample.
// 예전에FSM 공부할때cdplayer라는샘플코드를본것같은데코드는없고
// 웹에상태천이표만있어서그기준으로코드를작성함.
//
// machine, state, event 각각의인터페이스는비지터패턴형식으로구성함.
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
#include "stdafx.h"
#include <assert.h>
/////////////////////////////////////////////////////////////////////////////
//
// Finite state machine
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
namespace fsm
{
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
class event;
class state;
class machine;
/////////////////////////////////////////////////////////////////////////////
//
// Interface
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
class machine
{
private:
state* _state;
public:
machine();
virtual ~machine();
public:
virtual void transition (state*);
virtual void notify (event*);
virtual state* get_state (void ) const;
};
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
class event
{
public:
event();
virtual ~event();
public:
virtual void notify (machine*, state*);
};
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
class state
{
public:
state();
virtual ~state();
public:
virtual void on_entry (machine*, state*);
virtual void on_exit (machine*, state*);
virtual void on_event (machine*, event*);
public:
virtual void transition (machine*, state*);
};
/////////////////////////////////////////////////////////////////////////////
//
// Implementation
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
event:: event(){}
event::~event(){}
void event::notify (machine* m, state*s)
{
if (s)
{
s->on_event (m, this);
}
}
//===========================================================================
state:: state(){}
state::~state(){}
void state::on_entry (machine*, state*) {}
void state::on_exit (machine*, state*) {}
void state::on_event (machine*, event*) {}
void state::transition (machine* m, state* s)
{
if (m)
{
m->transition (s);
}
}
//===========================================================================
machine::machine():
_state (0)
{
}
machine::~machine()
{
}
state* machine::get_state (void) const
{
return _state;
}
void machine::transition (state* current)
{
state* previous;
previous = _state;
if (previous)
{
previous->on_exit (this, current );
}
_state = current;
if (current)
{
current ->on_entry (this, previous);
}
}
void machine::notify (event* e)
{
if (e)
{
e->notify (this, _state);
}
}
}; // end of "namespace fsm"
/////////////////////////////////////////////////////////////////////////////
//
// Sample
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
#define fsm_trace printf
#define fsm_assert assert
//===========================================================================
enum cdplayer_event_code_t
{
ec_play = 0,
ec_open_close = 1,
ec_cd_detected = 2,
ec_stop = 3,
ec_pause = 4
};
//===========================================================================
class cdplayer_event : public fsm::event
{
public:
const cdplayer_event_code_t _code;
explicit cdplayer_event (cdplayer_event_code_t code) : _code(code)
{
}
void notify (fsm::machine* m, fsm::state*s)
{
switch (_code)
{
case ec_play : fsm_trace("\t\tnotify: play \r\n"); break;
case ec_open_close : fsm_trace("\t\tnotify: open_close \r\n"); break;
case ec_cd_detected: fsm_trace("\t\tnotify: cd_detected \r\n"); break;
case ec_stop : fsm_trace("\t\tnotify: stop \r\n"); break;
case ec_pause : fsm_trace("\t\tnotify: pause \r\n"); break;
}
fsm::event::notify (m, s);
}
};
//===========================================================================
class cdplayer_state : public fsm::state
{
public:
virtual const char* get_name (void) = 0;
virtual void on_event (fsm::machine* m, fsm::event* e)
{
/*
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_play : break;
case ec_open_close : break;
case ec_cd_detected: break;
case ec_stop : break;
case ec_pause : break;
}
*/
}
virtual void on_entry (fsm::machine*, fsm::state*)
{
fsm_trace("\ton_entry: %s \r\n", get_name());
}
virtual void on_exit (fsm::machine*, fsm::state*)
{
fsm_trace("\ton_exit : %s \r\n", get_name());
}
};
#define DECLARE_NAME(n) virtual const char* get_name (void) { return (n); }
//===========================================================================
class stopped: public cdplayer_state
{
public:
DECLARE_NAME ("stopped" )
virtual void on_event (fsm::machine* m, fsm::event* e);
};
//===========================================================================
class open: public cdplayer_state
{
public:
DECLARE_NAME ("open" )
virtual void on_event (fsm::machine* m, fsm::event* e);
};
//===========================================================================
class empty: public cdplayer_state
{
public:
DECLARE_NAME ("empty" )
virtual void on_event (fsm::machine* m, fsm::event* e);
};
//===========================================================================
class playing: public cdplayer_state
{
public:
DECLARE_NAME ("playing" )
virtual void on_event (fsm::machine* m, fsm::event* e);
};
//===========================================================================
class paused: public cdplayer_state
{
public:
DECLARE_NAME ("paused" )
virtual void on_event (fsm::machine* m, fsm::event* e);
};
//===========================================================================
class cdplayer: public fsm::machine
{
private:
stopped _stopped;
open _open ;
empty _empty ;
playing _playing;
paused _paused ;
public:
enum state_id_t
{
sid_stopped,
sid_open ,
sid_empty ,
sid_playing,
sid_paused ,
};
public:
virtual void transition (fsm::state* n)
{
cdplayer_state* previous = static_cast<cdplayer_state*> ( get_state() );
cdplayer_state* current = static_cast<cdplayer_state*> ( n );
fsm_trace ("transition:");
if (previous) fsm_trace (" %s -> ", previous->get_name());
if (current ) fsm_trace (" %s ", current ->get_name());
fsm_trace ("\r\n");
fsm::machine::transition(n);
}
cdplayer_state* get_state_instance (state_id_t id)
{
switch (id)
{
case sid_stopped : return &_stopped ;
case sid_open : return &_open ;
case sid_empty : return &_empty ;
case sid_playing : return &_playing ;
case sid_paused : return &_paused ;
}
fsm_assert (0);
return 0;
}
public:
void start_playback (void){ printf("\t\t\taction: start_playback \r\n"); }
void open_drawer (void){ printf("\t\t\taction: open_drawer \r\n"); }
void close_drawer (void){ printf("\t\t\taction: close_drawer \r\n"); }
void collect_cd_information (void){ printf("\t\t\taction: collect_cd_information \r\n"); }
void store_cd_information (void){ printf("\t\t\taction: store_cd_information \r\n"); }
void stop_playback (void){ printf("\t\t\taction: stop_playback \r\n"); }
void pause_playback (void){ printf("\t\t\taction: pause_playback \r\n"); }
void resume_playback (void){ printf("\t\t\taction: resume_playback \r\n"); }
};
/*********************************************************************************
CD PLAYER STATE TRANSITION TABLE
---------------+-------------+------------+--------------------------------------
CURRENT STATE | EVENT | NEXT STATE | TRANSITION ACTION
---------------+-------------+------------+--------------------------------------
stopped | play | playing | start playback
stopped | open/close | open | open drawer
open | open/close | empty | close drawer; collect cd information
empty | open/close | open | open drawer
empty | cd-detected | stopped | store cd information
playing | stop | stopped | stop playback
playing | pause | paused | pause playback
playing | open/close | open | stop playback; open drawer
paused | play | playing | resume playback
paused | stop | stopped | stop playback
paused | open/close | open | stop playback; open drawer
---------------+-------------+------------+--------------------------------------
*********************************************************************************/
//===========================================================================
void stopped::on_event (fsm::machine* m, fsm::event* e)
{
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_play :
cm->start_playback();
transition(cm, cm->get_state_instance (cdplayer::sid_playing));
break;
case ec_open_close :
cm->open_drawer ();
transition(cm, cm->get_state_instance (cdplayer::sid_open));
break;
}
}
//===========================================================================
void open::on_event (fsm::machine* m, fsm::event* e)
{
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_open_close :
cm->close_drawer();
cm->collect_cd_information();
transition(cm, cm->get_state_instance (cdplayer::sid_empty));
break;
}
}
//===========================================================================
void empty::on_event (fsm::machine* m, fsm::event* e)
{
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_open_close :
cm->open_drawer();
transition(cm, cm->get_state_instance (cdplayer::sid_open));
break;
case ec_cd_detected:
cm->store_cd_information();
transition(cm, cm->get_state_instance (cdplayer::sid_stopped));
break;
}
}
//===========================================================================
void playing::on_event (fsm::machine* m, fsm::event* e)
{
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_stop :
cm->stop_playback();
transition(cm, cm->get_state_instance (cdplayer::sid_stopped));
break;
case ec_pause :
cm->pause_playback();
transition(cm, cm->get_state_instance (cdplayer::sid_paused));
break;
case ec_open_close :
cm->stop_playback();
cm->open_drawer();
transition(cm, cm->get_state_instance (cdplayer::sid_open));
break;
}
}
//===========================================================================
void paused::on_event (fsm::machine* m, fsm::event* e)
{
cdplayer* cm = static_cast<cdplayer* > ( m );
cdplayer_event* ce = static_cast<cdplayer_event*> ( e );
switch (ce->_code)
{
case ec_play :
cm->resume_playback();
transition(cm, cm->get_state_instance (cdplayer::sid_playing));
break;
case ec_stop :
cm->stop_playback();
transition(cm, cm->get_state_instance (cdplayer::sid_stopped));
break;
case ec_open_close :
cm->stop_playback();
cm->open_drawer();
transition(cm, cm->get_state_instance (cdplayer::sid_open));
break;
}
}
//===========================================================================
int _tmain(int argc, _TCHAR* argv[])
{
cdplayer player;
cdplayer_event play (ec_play );
cdplayer_event open_close (ec_open_close );
cdplayer_event cd_detected (ec_cd_detected);
cdplayer_event stop (ec_stop );
cdplayer_event pause (ec_pause );
player.transition ( player.get_state_instance(cdplayer::sid_empty) );
player.notify ( &cd_detected );
player.notify ( &play );
player.notify ( &pause );
player.notify ( &stop );
player.notify ( &open_close );
player.notify ( &stop );
player.notify ( &play );
player.notify ( &open_close );
player.notify ( &open_close );
player.notify ( &cd_detected );
player.notify ( &play );
return 0;
}
/////////////////////////////////////////////////////////////////////////////
//
// result
//
/////////////////////////////////////////////////////////////////////////////
//===========================================================================
/*
transition: empty
on_entry: empty
notify: cd_detected
action: store_cd_information
transition: empty -> stopped
on_exit : empty
on_entry: stopped
notify: play
action: start_playback
transition: stopped -> playing
on_exit : stopped
on_entry: playing
notify: pause
action: pause_playback
transition: playing -> paused
on_exit : playing
on_entry: paused
notify: stop
action: stop_playback
transition: paused -> stopped
on_exit : paused
on_entry: stopped
notify: open_close
action: open_drawer
transition: stopped -> open
on_exit : stopped
on_entry: open
notify: stop
notify: play
notify: open_close
action: close_drawer
action: collect_cd_information
transition: open -> empty
on_exit : open
on_entry: empty
notify: open_close
action: open_drawer
transition: empty -> open
on_exit : empty
on_entry: open
notify: cd_detected
notify: play
계속하려면아무키나누르십시오. . .
*/