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 // A "complicated" event type that carries some data. 39 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) 40 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) 41 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) 42 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) 43 44 //states 45 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) 46 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) 47 48 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) 49 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) 50 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) 51 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) 52 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) 53 54 //fsm 55 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) 56 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) 57 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) 58 BOOST_MSM_EUML_ACTION(No_Transition) 59 { 60 template <class FSM,class Event> 61 void operator()(Event const&,FSM&,int) 62 { 63 BOOST_FAIL("no_transition called!"); 64 } 65 }; BOOST_MSM_EUML_ACTION(good_disk_format)66 BOOST_MSM_EUML_ACTION(good_disk_format) 67 { 68 template <class FSM,class EVT,class SourceState,class TargetState> 69 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) 70 { 71 if (evt.get_attribute(cd_type)!=DISK_CD) 72 { 73 return false; 74 } 75 return true; 76 } 77 }; 78 BOOST_MSM_EUML_TRANSITION_TABLE(( 79 Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), 80 Playing == Paused + end_pause , 81 // +------------------------------------------------------------------------------+ 82 Empty == Open + open_close / ++fsm_(can_close_drawer_counter), 83 // +------------------------------------------------------------------------------+ 84 Open == Empty + open_close , 85 Open == Paused + open_close , 86 Open == Stopped + open_close , 87 Open == Playing + open_close , 88 // +------------------------------------------------------------------------------+ 89 Paused == Playing + pause , 90 // +------------------------------------------------------------------------------+ 91 Stopped == Playing + stop , 92 Stopped == Paused + stop , 93 Stopped == Empty + cd_detected [good_disk_format || 94 (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , 95 Stopped == Stopped + stop 96 // +------------------------------------------------------------------------------+ 97 ),transition_table) 98 99 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT 100 init_ << Empty, // Init State 101 no_action, // Entry 102 no_action, // Exit 103 attributes_ << start_playback_counter 104 << can_close_drawer_counter << test_fct_counter, // Attributes 105 configure_ << no_configure_, // configuration 106 No_Transition // no_transition handler 107 ), 108 player_) //fsm name 109 110 typedef msm::back::state_machine<player_> player; 111 112 // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; 113 114 BOOST_AUTO_TEST_CASE(my_test)115 BOOST_AUTO_TEST_CASE( my_test ) 116 { 117 player p; 118 119 p.start(); 120 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, 121 "Empty entry not called correctly"); 122 123 p.process_event(open_close()); 124 BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open 125 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, 126 "Empty exit not called correctly"); 127 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, 128 "Open entry not called correctly"); 129 130 p.process_event(open_close()); 131 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 132 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 133 "Open exit not called correctly"); 134 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, 135 "Empty entry not called correctly"); 136 BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly"); 137 138 p.process_event( 139 cd_detected("louie, louie",DISK_DVD)); 140 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 141 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 142 "Open exit not called correctly"); 143 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2, 144 "Empty entry not called correctly"); 145 146 p.process_event( 147 cd_detected("louie, louie",DISK_CD)); 148 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 149 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2, 150 "Empty exit not called correctly"); 151 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, 152 "Stopped entry not called correctly"); 153 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, 154 "Stopped exit not called correctly"); 155 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, 156 "Playing entry not called correctly"); 157 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); 158 BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly"); 159 160 p.process_event(pause()); 161 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 162 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, 163 "Playing exit not called correctly"); 164 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, 165 "Paused entry not called correctly"); 166 167 // go back to Playing 168 p.process_event(end_pause()); 169 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 170 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, 171 "Paused exit not called correctly"); 172 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, 173 "Playing entry not called correctly"); 174 175 p.process_event(pause()); 176 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 177 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, 178 "Playing exit not called correctly"); 179 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, 180 "Paused entry not called correctly"); 181 182 p.process_event(stop()); 183 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 184 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, 185 "Paused exit not called correctly"); 186 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, 187 "Stopped entry not called correctly"); 188 189 p.process_event(stop()); 190 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 191 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, 192 "Stopped exit not called correctly"); 193 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, 194 "Stopped entry not called correctly"); 195 } 196 } 197 198