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 #include <boost/msm/back/state_machine.hpp> 13 #include <boost/msm/front/euml/euml.hpp> 14 15 #ifndef BOOST_MSM_NONSTANDALONE_TEST 16 #define BOOST_TEST_MODULE MyTest 17 #endif 18 #include <boost/test/unit_test.hpp> 19 20 using namespace std; 21 using namespace boost::msm::front::euml; 22 namespace msm = boost::msm; 23 24 namespace 25 { 26 // A "complicated" event type that carries some data. 27 enum DiskTypeEnum 28 { 29 DISK_CD=0, 30 DISK_DVD=1 31 }; 32 // events 33 BOOST_MSM_EUML_EVENT(play) 34 BOOST_MSM_EUML_EVENT(end_pause) 35 BOOST_MSM_EUML_EVENT(stop) 36 BOOST_MSM_EUML_EVENT(pause) 37 BOOST_MSM_EUML_EVENT(open_close) 38 BOOST_MSM_EUML_EVENT(next_song) 39 BOOST_MSM_EUML_EVENT(previous_song) 40 BOOST_MSM_EUML_EVENT(region2_evt) 41 // A "complicated" event type that carries some data. 42 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) 43 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) 44 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) 45 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) 46 47 //states 48 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) 49 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) 50 51 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) 52 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) 53 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) 54 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) 55 56 // Playing is now a state machine itself. 57 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter) 58 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter) 59 // It has 5 substates 60 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song1) 61 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2) 62 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3) 63 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State1) 64 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State2) 65 66 // Playing has a transition table 67 BOOST_MSM_EUML_TRANSITION_TABLE(( 68 // +------------------------------------------------------------------------------+ 69 Song2 == Song1 + next_song , 70 Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , 71 Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) , 72 Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) , 73 Region2State2 == Region2State1 + region2_evt 74 // +------------------------------------------------------------------------------+ 75 ),playing_transition_table ) 76 77 BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT 78 init_ << Song1 << Region2State1, // Init State 79 ++state_(entry_counter), // Entry 80 ++state_(exit_counter), // Exit 81 attributes_ << entry_counter << exit_counter 82 << start_next_song_counter 83 << start_prev_song_guard_counter // Attributes 84 ),Playing_) 85 // choice of back-end 86 typedef msm::back::state_machine<Playing_> Playing_type; 87 Playing_type const Playing; 88 89 90 //fsm BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter)91 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) 92 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) 93 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) 94 BOOST_MSM_EUML_ACTION(No_Transition) 95 { 96 template <class FSM,class Event> 97 void operator()(Event const&,FSM&,int) 98 { 99 BOOST_FAIL("no_transition called!"); 100 } 101 }; BOOST_MSM_EUML_ACTION(good_disk_format)102 BOOST_MSM_EUML_ACTION(good_disk_format) 103 { 104 template <class FSM,class EVT,class SourceState,class TargetState> 105 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) 106 { 107 if (evt.get_attribute(cd_type)!=DISK_CD) 108 { 109 return false; 110 } 111 return true; 112 } 113 }; 114 BOOST_MSM_EUML_TRANSITION_TABLE(( 115 Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), 116 Playing == Paused + end_pause , 117 // +------------------------------------------------------------------------------+ 118 Empty == Open + open_close / ++fsm_(can_close_drawer_counter), 119 // +------------------------------------------------------------------------------+ 120 Open == Empty + open_close , 121 Open == Paused + open_close , 122 Open == Stopped + open_close , 123 Open == Playing + open_close , 124 // +------------------------------------------------------------------------------+ 125 Paused == Playing + pause , 126 // +------------------------------------------------------------------------------+ 127 Stopped == Playing + stop , 128 Stopped == Paused + stop , 129 Stopped == Empty + cd_detected [good_disk_format || 130 (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , 131 Stopped == Stopped + stop 132 // +------------------------------------------------------------------------------+ 133 ),transition_table) 134 135 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT 136 init_ << Empty, // Init State 137 no_action, // Entry 138 no_action, // Exit 139 attributes_ << start_playback_counter 140 << can_close_drawer_counter << test_fct_counter, // Attributes 141 configure_ << no_configure_, // configuration 142 No_Transition // no_transition handler 143 ), 144 player_) //fsm name 145 146 typedef msm::back::state_machine<player_> player; 147 148 // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; 149 150 BOOST_AUTO_TEST_CASE(my_test)151 BOOST_AUTO_TEST_CASE( my_test ) 152 { 153 player p; 154 155 p.start(); 156 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, 157 "Empty entry not called correctly"); 158 159 p.process_event(open_close()); 160 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open 161 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, 162 "Empty exit not called correctly"); 163 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, 164 "Open entry not called correctly"); 165 166 p.process_event(open_close()); 167 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 168 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 169 "Open exit not called correctly"); 170 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, 171 "Empty entry not called correctly"); 172 BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); 173 174 p.process_event( 175 cd_detected("louie, louie",DISK_DVD)); 176 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 177 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 178 "Open exit not called correctly"); 179 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, 180 "Empty entry not called correctly"); 181 182 p.process_event( 183 cd_detected("louie, louie",DISK_CD)); 184 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 185 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, 186 "Empty exit not called correctly"); 187 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, 188 "Stopped entry not called correctly"); 189 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, 190 "Stopped exit not called correctly"); 191 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1, 192 "Playing entry not called correctly"); 193 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); 194 BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); 195 196 p.process_event(next_song); 197 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 198 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); 199 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[1] == 3,"Region2State1 should be active"); 200 BOOST_CHECK_MESSAGE( 201 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State1)&>().get_attribute(entry_counter) == 1, 202 "Region2State1 entry not called correctly"); 203 BOOST_CHECK_MESSAGE( 204 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1, 205 "Song2 entry not called correctly"); 206 BOOST_CHECK_MESSAGE( 207 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1, 208 "Song1 exit not called correctly"); 209 BOOST_CHECK_MESSAGE( 210 p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0, 211 "submachine action not called correctly"); 212 213 p.process_event(next_song); 214 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 215 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active"); 216 BOOST_CHECK_MESSAGE( 217 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1, 218 "Song3 entry not called correctly"); 219 BOOST_CHECK_MESSAGE( 220 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1, 221 "Song2 exit not called correctly"); 222 BOOST_CHECK_MESSAGE( 223 p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1, 224 "submachine action not called correctly"); 225 226 p.process_event(previous_song); 227 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 228 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active"); 229 BOOST_CHECK_MESSAGE( 230 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2, 231 "Song2 entry not called correctly"); 232 BOOST_CHECK_MESSAGE( 233 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1, 234 "Song3 exit not called correctly"); 235 BOOST_CHECK_MESSAGE( 236 p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1, 237 "submachine guard not called correctly"); 238 BOOST_CHECK_MESSAGE( 239 p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State2)&>().get_attribute(entry_counter) == 0, 240 "Region2State2 entry not called correctly"); 241 242 243 p.process_event(pause()); 244 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 245 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1, 246 "Playing exit not called correctly"); 247 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, 248 "Paused entry not called correctly"); 249 250 // go back to Playing 251 p.process_event(end_pause()); 252 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 253 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, 254 "Paused exit not called correctly"); 255 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 2, 256 "Playing entry not called correctly"); 257 258 p.process_event(pause()); 259 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 260 BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2, 261 "Playing exit not called correctly"); 262 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, 263 "Paused entry not called correctly"); 264 265 p.process_event(stop()); 266 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 267 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, 268 "Paused exit not called correctly"); 269 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, 270 "Stopped entry not called correctly"); 271 272 p.process_event(stop()); 273 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 274 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, 275 "Stopped exit not called correctly"); 276 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, 277 "Stopped entry not called correctly"); 278 } 279 } 280 281