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 // include headers that implement a archive in simple text format 20 #include <boost/archive/text_oarchive.hpp> 21 #include <boost/archive/text_iarchive.hpp> 22 #include <boost/serialization/tracking.hpp> 23 24 #include <fstream> 25 26 using namespace std; 27 using namespace boost::msm::front::euml; 28 namespace msm = boost::msm; 29 30 namespace 31 { 32 // A "complicated" event type that carries some data. 33 enum DiskTypeEnum 34 { 35 DISK_CD=0, 36 DISK_DVD=1 37 }; 38 // events 39 BOOST_MSM_EUML_EVENT(play) BOOST_MSM_EUML_EVENT(end_pause)40 BOOST_MSM_EUML_EVENT(end_pause) 41 BOOST_MSM_EUML_EVENT(stop) 42 BOOST_MSM_EUML_EVENT(pause) 43 BOOST_MSM_EUML_EVENT(open_close) 44 // A "complicated" event type that carries some data. 45 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) 46 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) 47 BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) 48 BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) 49 50 //states 51 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) 52 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) 53 54 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty) 55 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) 56 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) 57 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) 58 BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) 59 60 //fsm 61 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) 62 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) 63 BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,test_fct_counter) 64 BOOST_MSM_EUML_ACTION(No_Transition) 65 { 66 template <class FSM,class Event> 67 void operator()(Event const&,FSM&,int) 68 { 69 BOOST_FAIL("no_transition called!"); 70 } 71 }; BOOST_MSM_EUML_ACTION(good_disk_format)72 BOOST_MSM_EUML_ACTION(good_disk_format) 73 { 74 template <class FSM,class EVT,class SourceState,class TargetState> 75 bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) 76 { 77 if (evt.get_attribute(cd_type)!=DISK_CD) 78 { 79 return false; 80 } 81 return true; 82 } 83 }; 84 BOOST_MSM_EUML_TRANSITION_TABLE(( 85 Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ), 86 Playing == Paused + end_pause , 87 // +------------------------------------------------------------------------------+ 88 Empty == Open + open_close / ++fsm_(can_close_drawer_counter), 89 // +------------------------------------------------------------------------------+ 90 Open == Empty + open_close , 91 Open == Paused + open_close , 92 Open == Stopped + open_close , 93 Open == Playing + open_close , 94 // +------------------------------------------------------------------------------+ 95 Paused == Playing + pause , 96 // +------------------------------------------------------------------------------+ 97 Stopped == Playing + stop , 98 Stopped == Paused + stop , 99 Stopped == Empty + cd_detected [good_disk_format || 100 (event_(cd_type)==Int_<DISK_CD>())] / process_(play) , 101 Stopped == Stopped + stop 102 // +------------------------------------------------------------------------------+ 103 ),transition_table) 104 105 BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT 106 init_ << Empty, // Init State 107 no_action, // Entry 108 no_action, // Exit 109 attributes_ << start_playback_counter 110 << can_close_drawer_counter << test_fct_counter, // Attributes 111 configure_ << no_configure_, // configuration 112 No_Transition // no_transition handler 113 ), 114 player_) //fsm name 115 116 typedef msm::back::state_machine<player_> player; 117 118 // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; 119 120 BOOST_AUTO_TEST_CASE(my_test)121 BOOST_AUTO_TEST_CASE( my_test ) 122 { 123 player p2; 124 125 p2.start(); 126 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1, 127 "Empty entry not called correctly"); 128 129 p2.process_event(open_close()); 130 BOOST_CHECK_MESSAGE(p2.current_state()[0] == 2,"Open should be active"); //Open 131 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1, 132 "Empty exit not called correctly"); 133 BOOST_CHECK_MESSAGE(p2.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1, 134 "Open entry not called correctly"); 135 136 // test the serialization 137 std::ofstream ofs("fsm.txt"); 138 // save fsm to archive (current state is Open) 139 { 140 boost::archive::text_oarchive oa(ofs); 141 // write class instance to archive 142 oa << p2; 143 } 144 // reload fsm in state Open 145 player p; 146 { 147 // create and open an archive for input 148 std::ifstream ifs("fsm.txt"); 149 boost::archive::text_iarchive ia(ifs); 150 // read class state from archive 151 ia >> p; 152 } 153 154 p.process_event(open_close()); 155 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 156 157 p.process_event( 158 cd_detected("louie, louie",DISK_DVD)); 159 BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty 160 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, 161 "Open exit not called correctly"); 162 163 p.process_event( 164 cd_detected("louie, louie",DISK_CD)); 165 BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing 166 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, 167 "Stopped entry not called correctly"); 168 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, 169 "Stopped exit not called correctly"); 170 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, 171 "Playing entry not called correctly"); 172 BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly"); 173 BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action 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) == 1, 178 "Playing exit not called correctly"); 179 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, 180 "Paused entry not called correctly"); 181 182 // go back to Playing 183 p.process_event(end_pause()); 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(Paused)&>().get_attribute(exit_counter) == 1, 186 "Paused exit not called correctly"); 187 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, 188 "Playing entry not called correctly"); 189 190 p.process_event(pause()); 191 BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused 192 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, 193 "Playing exit not called correctly"); 194 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, 195 "Paused entry not called correctly"); 196 197 p.process_event(stop()); 198 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 199 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, 200 "Paused exit not called correctly"); 201 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, 202 "Stopped entry not called correctly"); 203 204 p.process_event(stop()); 205 BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped 206 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, 207 "Stopped exit not called correctly"); 208 BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, 209 "Stopped entry not called correctly"); 210 } 211 } 212 213 // eliminate object tracking (even if serialized through a pointer) 214 // at the risk of a programming error creating duplicate objects. 215 // this is to get rid of warning because p is not const 216 BOOST_CLASS_TRACKING(player, boost::serialization::track_never) 217