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) BOOST_MSM_EUML_EVENT(end_pause)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(internal_evt) 39 BOOST_MSM_EUML_EVENT(to_ignore) 40 // A "complicated" event type that carries some data. 41 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) 42 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) 43 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) 44 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) 45 46 //states 47 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) 48 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) 49 50 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) 51 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) 52 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) 53 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) 54 55 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_guard_counter) 56 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter) 57 58 BOOST_MSM_EUML_ACTION(internal_guard_fct) 59 { 60 template <class FSM,class EVT,class SourceState,class TargetState> 61 bool operator()(EVT const&, FSM& ,SourceState& src,TargetState& ) 62 { 63 ++src.get_attribute(empty_internal_guard_counter); 64 return false; 65 } 66 }; 67 BOOST_MSM_EUML_DECLARE_STATE((++state_(entry_counter),++state_(exit_counter), 68 attributes_ << entry_counter << exit_counter 69 << empty_internal_guard_counter << empty_internal_action_counter),Empty_def) 70 // derive to be able to add an internal transition table 71 struct Empty_impl : public Empty_def 72 { Empty_impl__anond06873e90111::Empty_impl73 Empty_impl(){} 74 BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( 75 internal_evt [internal_guard_fct] / ++source_(empty_internal_action_counter) 76 )) 77 }; 78 // declare an instance for the stt as we are manually declaring a state 79 Empty_impl const Empty; 80 81 //fsm BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter)82 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) 83 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) 84 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_action_counter) 85 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_guard_counter) 86 BOOST_MSM_EUML_ACTION(No_Transition) 87 { 88 template <class FSM,class Event> 89 void operator()(Event const&,FSM&,int) 90 { 91 BOOST_FAIL("no_transition called!"); 92 } 93 }; BOOST_MSM_EUML_ACTION(good_disk_format)94 BOOST_MSM_EUML_ACTION(good_disk_format) 95 { 96 template <class FSM,class EVT,class SourceState,class TargetState> 97 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) 98 { 99 if (evt.get_attribute(cd_type)!=DISK_CD) 100 { 101 return false; 102 } 103 return true; 104 } 105 }; BOOST_MSM_EUML_ACTION(internal_guard)106 BOOST_MSM_EUML_ACTION(internal_guard) 107 { 108 template <class FSM,class EVT,class SourceState,class TargetState> 109 bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) 110 { 111 ++fsm.get_attribute(internal_guard_counter); 112 return false; 113 } 114 }; BOOST_MSM_EUML_ACTION(internal_guard2)115 BOOST_MSM_EUML_ACTION(internal_guard2) 116 { 117 template <class FSM,class EVT,class SourceState,class TargetState> 118 bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) 119 { 120 ++fsm.get_attribute(internal_guard_counter); 121 return true; 122 } 123 }; 124 BOOST_MSM_EUML_TRANSITION_TABLE(( 125 Playing == Stopped + play / ++fsm_(start_playback_counter) , 126 Playing == Paused + end_pause , 127 // +------------------------------------------------------------------------------+ 128 Empty == Open + open_close / ++fsm_(can_close_drawer_counter), 129 Empty + to_ignore , 130 Empty + internal_evt [internal_guard2] / ++fsm_(internal_action_counter) , 131 Empty + cd_detected [internal_guard] , 132 // +------------------------------------------------------------------------------+ 133 Open == Empty + open_close , 134 Open == Paused + open_close , 135 Open == Stopped + open_close , 136 Open == Playing + open_close , 137 // +------------------------------------------------------------------------------+ 138 Paused == Playing + pause , 139 // +------------------------------------------------------------------------------+ 140 Stopped == Playing + stop , 141 Stopped == Paused + stop , 142 Stopped == Empty + cd_detected [good_disk_format] , 143 Stopped == Stopped + stop 144 // +------------------------------------------------------------------------------+ 145 ),transition_table) 146 147 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT 148 init_ << Empty, // Init State 149 no_action, // Entry 150 no_action, // Exit 151 attributes_ << start_playback_counter << can_close_drawer_counter 152 << internal_action_counter << internal_guard_counter, // Attributes 153 configure_ << no_configure_, // configuration 154 No_Transition // no_transition handler 155 ), 156 player_) //fsm name 157 158 typedef msm::back::state_machine<player_> player; 159 160 // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; 161 162 BOOST_AUTO_TEST_CASE(my_test)163 BOOST_AUTO_TEST_CASE( my_test ) 164 { 165 player p; 166 167 p.start(); 168 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, 169 "Empty entry not called correctly"); 170 171 // internal events 172 p.process_event(to_ignore); 173 p.process_event(internal_evt); 174 BOOST_CHECK_MESSAGE(p.get_attribute(internal_action_counter) == 1,"Internal action not called correctly"); 175 BOOST_CHECK_MESSAGE(p.get_attribute(internal_guard_counter) == 1,"Internal guard not called correctly"); 176 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_action_counter) == 0, 177 "Empty internal action not called correctly"); 178 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_guard_counter) == 1, 179 "Empty internal guard not called correctly"); 180 181 p.process_event(open_close()); 182 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open 183 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 1, 184 "Empty exit not called correctly"); 185 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, 186 "Open entry not called correctly"); 187 188 p.process_event(open_close()); 189 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 190 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 191 "Open exit not called correctly"); 192 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, 193 "Empty entry not called correctly"); 194 BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); 195 196 p.process_event( 197 cd_detected("louie, louie",DISK_DVD)); 198 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 199 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 200 "Open exit not called correctly"); 201 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, 202 "Empty entry not called correctly"); 203 204 p.process_event( 205 cd_detected("louie, louie",DISK_CD)); 206 p.process_event(play); 207 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 208 BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 2, 209 "Empty exit not called correctly"); 210 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, 211 "Stopped entry not called correctly"); 212 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, 213 "Stopped exit not called correctly"); 214 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, 215 "Playing entry not called correctly"); 216 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); 217 218 p.process_event(pause()); 219 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 220 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, 221 "Playing exit not called correctly"); 222 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, 223 "Paused entry not called correctly"); 224 225 // go back to Playing 226 p.process_event(end_pause()); 227 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 228 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, 229 "Paused exit not called correctly"); 230 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, 231 "Playing entry not called correctly"); 232 233 p.process_event(pause()); 234 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 235 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, 236 "Playing exit not called correctly"); 237 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, 238 "Paused entry not called correctly"); 239 240 p.process_event(stop()); 241 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 242 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, 243 "Paused exit not called correctly"); 244 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, 245 "Stopped entry not called correctly"); 246 247 p.process_event(stop()); 248 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 249 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, 250 "Stopped exit not called correctly"); 251 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, 252 "Stopped entry not called correctly"); 253 } 254 } 255 256