1 // Copyright 2010 Christophe Henry 2 // henry UNDERSCORE christophe AT hotmail DOT com 3 // This is an extended version of the state machine available in the boost::mpl library 4 // Distributed under the same license as the original. 5 // Copyright for the original version: 6 // Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed 7 // under the Boost Software License, Version 1.0. (See accompanying 8 // file LICENSE_1_0.txt or copy at 9 // http://www.boost.org/LICENSE_1_0.txt) 10 11 #include <iostream> 12 // back-end 13 #include <boost/msm/back/state_machine.hpp> 14 //front-end 15 #include <boost/msm/front/state_machine_def.hpp> 16 #include <boost/msm/front/functor_row.hpp> 17 #include <boost/msm/front/euml/common.hpp> 18 19 #ifndef BOOST_MSM_NONSTANDALONE_TEST 20 #define BOOST_TEST_MODULE MyTest 21 #endif 22 #include <boost/test/unit_test.hpp> 23 24 using namespace std; 25 namespace msm = boost::msm; 26 using namespace msm::front; 27 namespace mpl = boost::mpl; 28 29 namespace 30 { 31 // events 32 struct play {}; 33 struct end_pause {}; 34 struct stop {}; 35 struct pause {}; 36 struct open_close {}; 37 struct internal_evt {}; 38 struct to_ignore {}; 39 40 // A "complicated" event type that carries some data. 41 enum DiskTypeEnum 42 { 43 DISK_CD=0, 44 DISK_DVD=1 45 }; 46 struct cd_detected 47 { cd_detected__anon6e83b8490111::cd_detected48 cd_detected(std::string name, DiskTypeEnum diskType) 49 : name(name), 50 disc_type(diskType) 51 {} 52 53 std::string name; 54 DiskTypeEnum disc_type; 55 }; 56 57 // front-end: define the FSM structure 58 struct player_ : public msm::front::state_machine_def<player_> 59 { 60 unsigned int start_playback_counter; 61 unsigned int can_close_drawer_counter; 62 unsigned int internal_action_counter; 63 unsigned int internal_guard_counter; 64 player___anon6e83b8490111::player_65 player_(): 66 start_playback_counter(0), 67 can_close_drawer_counter(0), 68 internal_action_counter(0), 69 internal_guard_counter(0) 70 {} 71 72 // The list of FSM states 73 struct Empty : public msm::front::state<> 74 { 75 template <class Event,class FSM> on_entry__anon6e83b8490111::player_::Empty76 void on_entry(Event const&,FSM& ) {++entry_counter;} 77 template <class Event,class FSM> on_exit__anon6e83b8490111::player_::Empty78 void on_exit(Event const&,FSM& ) {++exit_counter;} 79 int entry_counter; 80 int exit_counter; 81 unsigned int empty_internal_guard_counter; 82 unsigned int empty_internal_action_counter; 83 struct internal_guard_fct 84 { 85 template <class EVT,class FSM,class SourceState,class TargetState> operator ()__anon6e83b8490111::player_::Empty::internal_guard_fct86 bool operator()(EVT const& ,FSM&,SourceState& src,TargetState& ) 87 { 88 ++src.empty_internal_guard_counter; 89 return false; 90 } 91 }; 92 struct internal_action_fct 93 { 94 template <class EVT,class FSM,class SourceState,class TargetState> operator ()__anon6e83b8490111::player_::Empty::internal_action_fct95 void operator()(EVT const& ,FSM& ,SourceState& src,TargetState& ) 96 { 97 ++src.empty_internal_action_counter; 98 } 99 }; 100 // Transition table for Empty 101 struct internal_transition_table : mpl::vector< 102 // Start Event Next Action Guard 103 Internal < internal_evt , internal_action_fct ,internal_guard_fct > 104 // +---------+-------------+---------+---------------------+----------------------+ 105 > {}; 106 }; 107 struct Open : public msm::front::state<> 108 { 109 template <class Event,class FSM> on_entry__anon6e83b8490111::player_::Open110 void on_entry(Event const&,FSM& ) {++entry_counter;} 111 template <class Event,class FSM> on_exit__anon6e83b8490111::player_::Open112 void on_exit(Event const&,FSM& ) {++exit_counter;} 113 int entry_counter; 114 int exit_counter; 115 }; 116 117 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing 118 struct Stopped : public msm::front::state<> 119 { 120 template <class Event,class FSM> on_entry__anon6e83b8490111::player_::Stopped121 void on_entry(Event const&,FSM& ) {++entry_counter;} 122 template <class Event,class FSM> on_exit__anon6e83b8490111::player_::Stopped123 void on_exit(Event const&,FSM& ) {++exit_counter;} 124 int entry_counter; 125 int exit_counter; 126 }; 127 128 struct Playing : public msm::front::state<> 129 { 130 template <class Event,class FSM> on_entry__anon6e83b8490111::player_::Playing131 void on_entry(Event const&,FSM& ) {++entry_counter;} 132 template <class Event,class FSM> on_exit__anon6e83b8490111::player_::Playing133 void on_exit(Event const&,FSM& ) {++exit_counter;} 134 int entry_counter; 135 int exit_counter; 136 }; 137 138 // state not defining any entry or exit 139 struct Paused : public msm::front::state<> 140 { 141 template <class Event,class FSM> on_entry__anon6e83b8490111::player_::Paused142 void on_entry(Event const&,FSM& ) {++entry_counter;} 143 template <class Event,class FSM> on_exit__anon6e83b8490111::player_::Paused144 void on_exit(Event const&,FSM& ) {++exit_counter;} 145 int entry_counter; 146 int exit_counter; 147 }; 148 149 // the initial state of the player SM. Must be defined 150 typedef Empty initial_state; 151 152 // transition actions start_playback__anon6e83b8490111::player_153 void start_playback(play const&) {++start_playback_counter; } open_drawer__anon6e83b8490111::player_154 void open_drawer(open_close const&) { } store_cd_info__anon6e83b8490111::player_155 void store_cd_info(cd_detected const&) { } stop_playback__anon6e83b8490111::player_156 void stop_playback(stop const&) { } pause_playback__anon6e83b8490111::player_157 void pause_playback(pause const&) { } resume_playback__anon6e83b8490111::player_158 void resume_playback(end_pause const&) { } stop_and_open__anon6e83b8490111::player_159 void stop_and_open(open_close const&) { } stopped_again__anon6e83b8490111::player_160 void stopped_again(stop const&){} 161 struct internal_action 162 { 163 template <class EVT,class FSM,class SourceState,class TargetState> operator ()__anon6e83b8490111::player_::internal_action164 void operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) 165 { 166 ++fsm.internal_action_counter; 167 } 168 }; 169 struct internal_guard 170 { 171 template <class EVT,class FSM,class SourceState,class TargetState> operator ()__anon6e83b8490111::player_::internal_guard172 bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) 173 { 174 ++fsm.internal_guard_counter; 175 return false; 176 } 177 }; 178 struct internal_guard2 179 { 180 template <class EVT,class FSM,class SourceState,class TargetState> operator ()__anon6e83b8490111::player_::internal_guard2181 bool operator()(EVT const& ,FSM& fsm,SourceState& ,TargetState& ) 182 { 183 ++fsm.internal_guard_counter; 184 return true; 185 } 186 }; 187 // guard conditions good_disk_format__anon6e83b8490111::player_188 bool good_disk_format(cd_detected const& evt) 189 { 190 // to test a guard condition, let's say we understand only CDs, not DVD 191 if (evt.disc_type != DISK_CD) 192 { 193 return false; 194 } 195 return true; 196 } can_close_drawer__anon6e83b8490111::player_197 bool can_close_drawer(open_close const&) 198 { 199 ++can_close_drawer_counter; 200 return true; 201 } 202 203 typedef player_ p; // makes transition table cleaner 204 205 // Transition table for player 206 struct transition_table : mpl::vector< 207 // Start Event Next Action Guard 208 // +---------+-------------+---------+---------------------+----------------------+ 209 a_row < Stopped , play , Playing , &p::start_playback >, 210 a_row < Stopped , open_close , Open , &p::open_drawer >, 211 _row < Stopped , stop , Stopped >, 212 // +---------+-------------+---------+---------------------+----------------------+ 213 g_row < Open , open_close , Empty , &p::can_close_drawer >, 214 // +---------+-------------+---------+---------------------+----------------------+ 215 a_row < Empty , open_close , Open , &p::open_drawer >, 216 row < Empty , cd_detected , Stopped , &p::store_cd_info ,&p::good_disk_format >, 217 Row < Empty , internal_evt, none , internal_action ,internal_guard2 >, 218 Row < Empty , to_ignore , none , none , none >, 219 Row < Empty , cd_detected , none , none ,internal_guard >, 220 // +---------+-------------+---------+---------------------+----------------------+ 221 a_row < Playing , stop , Stopped , &p::stop_playback >, 222 a_row < Playing , pause , Paused , &p::pause_playback >, 223 a_row < Playing , open_close , Open , &p::stop_and_open >, 224 // +---------+-------------+---------+---------------------+----------------------+ 225 a_row < Paused , end_pause , Playing , &p::resume_playback >, 226 a_row < Paused , stop , Stopped , &p::stop_playback >, 227 a_row < Paused , open_close , Open , &p::stop_and_open > 228 // +---------+-------------+---------+---------------------+----------------------+ 229 > {}; 230 // Replaces the default no-transition response. 231 template <class FSM,class Event> no_transition__anon6e83b8490111::player_232 void no_transition(Event const&, FSM&,int) 233 { 234 BOOST_FAIL("no_transition called!"); 235 } 236 // init counters 237 template <class Event,class FSM> on_entry__anon6e83b8490111::player_238 void on_entry(Event const&,FSM& fsm) 239 { 240 fsm.template get_state<player_::Stopped&>().entry_counter=0; 241 fsm.template get_state<player_::Stopped&>().exit_counter=0; 242 fsm.template get_state<player_::Open&>().entry_counter=0; 243 fsm.template get_state<player_::Open&>().exit_counter=0; 244 fsm.template get_state<player_::Empty&>().entry_counter=0; 245 fsm.template get_state<player_::Empty&>().exit_counter=0; 246 fsm.template get_state<player_::Empty&>().empty_internal_guard_counter=0; 247 fsm.template get_state<player_::Empty&>().empty_internal_action_counter=0; 248 fsm.template get_state<player_::Playing&>().entry_counter=0; 249 fsm.template get_state<player_::Playing&>().exit_counter=0; 250 fsm.template get_state<player_::Paused&>().entry_counter=0; 251 fsm.template get_state<player_::Paused&>().exit_counter=0; 252 } 253 254 }; 255 // Pick a back-end 256 typedef msm::back::state_machine<player_> player; 257 258 // static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" }; 259 260 BOOST_AUTO_TEST_CASE(my_test)261 BOOST_AUTO_TEST_CASE( my_test ) 262 { 263 player p; 264 265 p.start(); 266 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly"); 267 // internal events 268 p.process_event(to_ignore()); 269 p.process_event(internal_evt()); 270 BOOST_CHECK_MESSAGE(p.internal_action_counter == 1,"Internal action not called correctly"); 271 BOOST_CHECK_MESSAGE(p.internal_guard_counter == 1,"Internal guard not called correctly"); 272 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_action_counter == 0,"Empty internal action not called correctly"); 273 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().empty_internal_guard_counter == 1,"Empty internal guard not called correctly"); 274 275 p.process_event(open_close()); 276 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open 277 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly"); 278 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly"); 279 280 p.process_event(open_close()); 281 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty 282 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); 283 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); 284 BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly"); 285 286 p.process_event( 287 cd_detected("louie, louie",DISK_DVD)); 288 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty 289 BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly"); 290 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly"); 291 BOOST_CHECK_MESSAGE(p.internal_guard_counter == 2,"Internal guard not called correctly"); 292 293 p.process_event( 294 cd_detected("louie, louie",DISK_CD)); 295 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 296 BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly"); 297 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly"); 298 BOOST_CHECK_MESSAGE(p.internal_guard_counter == 3,"Internal guard not called correctly"); 299 300 p.process_event(play()); 301 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 302 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly"); 303 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly"); 304 BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly"); 305 306 p.process_event(pause()); 307 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused 308 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly"); 309 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly"); 310 311 // go back to Playing 312 p.process_event(end_pause()); 313 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing 314 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly"); 315 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly"); 316 317 p.process_event(pause()); 318 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused 319 BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly"); 320 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly"); 321 322 p.process_event(stop()); 323 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 324 BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly"); 325 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly"); 326 327 p.process_event(stop()); 328 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 329 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly"); 330 BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly"); 331 } 332 } 333 334