• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #ifndef BOOST_MSM_NONSTANDALONE_TEST
17 #define BOOST_TEST_MODULE MyTest
18 #endif
19 #include <boost/test/unit_test.hpp>
20 // include headers that implement a archive in simple text format
21 #include <boost/archive/text_oarchive.hpp>
22 #include <boost/archive/text_iarchive.hpp>
23 #include <boost/serialization/tracking.hpp>
24 
25 #include <fstream>
26 
27 namespace msm = boost::msm;
28 namespace mpl = boost::mpl;
29 
30 namespace
31 {
32     // events
33     struct play {};
34     struct end_pause {};
35     struct stop {};
36     struct pause {};
37     struct open_close {};
38 
39     // A "complicated" event type that carries some data.
40     enum DiskTypeEnum
41     {
42         DISK_CD=0,
43         DISK_DVD=1
44     };
45     struct cd_detected
46     {
cd_detected__anon5ecd1c550111::cd_detected47         cd_detected(std::string name, DiskTypeEnum diskType)
48             : name(name),
49             disc_type(diskType)
50         {}
51 
52         std::string name;
53         DiskTypeEnum disc_type;
54     };
55 
56     // front-end: define the FSM structure
57     struct player_ : public msm::front::state_machine_def<player_>
58     {
59         unsigned int start_playback_counter;
60         unsigned int can_close_drawer_counter;
61         int front_end_data;
62 
player___anon5ecd1c550111::player_63         player_():
64         start_playback_counter(0),
65         can_close_drawer_counter(0),
66         front_end_data(4)
67         {}
68 
69         //we want to serialize some data contained by the front-end
70         // to achieve this, ask for it
71         typedef int do_serialize;
72         // and provide a serialize
73         template<class Archive>
serialize__anon5ecd1c550111::player_74         void serialize(Archive & ar, const unsigned int )
75         {
76             ar & front_end_data;
77         }
78 
79         // The list of FSM states
80         struct Empty : public msm::front::state<>
81         {
82             template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_::Empty83             void on_entry(Event const&,FSM& ) {++entry_counter;}
84             template <class Event,class FSM>
on_exit__anon5ecd1c550111::player_::Empty85             void on_exit(Event const&,FSM& ) {++exit_counter;}
86             int entry_counter;
87             int exit_counter;
88             int some_dummy_data;
89             // we want Empty to be serialized
90             typedef int do_serialize;
91             template<class Archive>
serialize__anon5ecd1c550111::player_::Empty92             void serialize(Archive & ar, const unsigned int )
93             {
94                 ar & some_dummy_data;
95             }
96 
97         };
98         struct Open : public msm::front::state<>
99         {
100             template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_::Open101             void on_entry(Event const&,FSM& ) {++entry_counter;}
102             template <class Event,class FSM>
on_exit__anon5ecd1c550111::player_::Open103             void on_exit(Event const&,FSM& ) {++exit_counter;}
104             int entry_counter;
105             int exit_counter;
106         };
107 
108         // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
109         struct Stopped : public msm::front::state<>
110         {
111             template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_::Stopped112             void on_entry(Event const&,FSM& ) {++entry_counter;}
113             template <class Event,class FSM>
on_exit__anon5ecd1c550111::player_::Stopped114             void on_exit(Event const&,FSM& ) {++exit_counter;}
115             int entry_counter;
116             int exit_counter;
117         };
118 
119         struct Playing : public msm::front::state<>
120         {
121             template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_::Playing122             void on_entry(Event const&,FSM& ) {++entry_counter;}
123             template <class Event,class FSM>
on_exit__anon5ecd1c550111::player_::Playing124             void on_exit(Event const&,FSM& ) {++exit_counter;}
125             int entry_counter;
126             int exit_counter;
127         };
128 
129         // state not defining any entry or exit
130         struct Paused : public msm::front::state<>
131         {
132             template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_::Paused133             void on_entry(Event const&,FSM& ) {++entry_counter;}
134             template <class Event,class FSM>
on_exit__anon5ecd1c550111::player_::Paused135             void on_exit(Event const&,FSM& ) {++exit_counter;}
136             int entry_counter;
137             int exit_counter;
138         };
139 
140         // the initial state of the player SM. Must be defined
141         typedef Empty initial_state;
142 
143         // transition actions
start_playback__anon5ecd1c550111::player_144         void start_playback(play const&)       {++start_playback_counter; }
open_drawer__anon5ecd1c550111::player_145         void open_drawer(open_close const&)    {  }
store_cd_info__anon5ecd1c550111::player_146         void store_cd_info(cd_detected const&) {  }
stop_playback__anon5ecd1c550111::player_147         void stop_playback(stop const&)        {  }
pause_playback__anon5ecd1c550111::player_148         void pause_playback(pause const&)      {  }
resume_playback__anon5ecd1c550111::player_149         void resume_playback(end_pause const&)      {  }
stop_and_open__anon5ecd1c550111::player_150         void stop_and_open(open_close const&)  {  }
stopped_again__anon5ecd1c550111::player_151         void stopped_again(stop const&){}
152         // guard conditions
good_disk_format__anon5ecd1c550111::player_153         bool good_disk_format(cd_detected const& evt)
154         {
155             // to test a guard condition, let's say we understand only CDs, not DVD
156             if (evt.disc_type != DISK_CD)
157             {
158                 return false;
159             }
160             return true;
161         }
can_close_drawer__anon5ecd1c550111::player_162         bool can_close_drawer(open_close const&)
163         {
164             ++can_close_drawer_counter;
165             return true;
166         }
167 
168         typedef player_ p; // makes transition table cleaner
169 
170         // Transition table for player
171         struct transition_table : mpl::vector<
172             //    Start     Event         Next      Action               Guard
173             //  +---------+-------------+---------+---------------------+----------------------+
174           a_row < Stopped , play        , Playing , &p::start_playback                         >,
175           a_row < Stopped , open_close  , Open    , &p::open_drawer                            >,
176            _row < Stopped , stop        , Stopped                                              >,
177             //  +---------+-------------+---------+---------------------+----------------------+
178           g_row < Open    , open_close  , Empty   ,                      &p::can_close_drawer  >,
179             //  +---------+-------------+---------+---------------------+----------------------+
180           a_row < Empty   , open_close  , Open    , &p::open_drawer                            >,
181             row < Empty   , cd_detected , Stopped , &p::store_cd_info   ,&p::good_disk_format  >,
182             //  +---------+-------------+---------+---------------------+----------------------+
183           a_row < Playing , stop        , Stopped , &p::stop_playback                          >,
184           a_row < Playing , pause       , Paused  , &p::pause_playback                         >,
185           a_row < Playing , open_close  , Open    , &p::stop_and_open                          >,
186             //  +---------+-------------+---------+---------------------+----------------------+
187           a_row < Paused  , end_pause   , Playing , &p::resume_playback                        >,
188           a_row < Paused  , stop        , Stopped , &p::stop_playback                          >,
189           a_row < Paused  , open_close  , Open    , &p::stop_and_open                          >
190             //  +---------+-------------+---------+---------------------+----------------------+
191         > {};
192         // Replaces the default no-transition response.
193         template <class FSM,class Event>
no_transition__anon5ecd1c550111::player_194         void no_transition(Event const& , FSM&,int)
195         {
196             BOOST_FAIL("no_transition called!");
197         }
198         // init counters
199         template <class Event,class FSM>
on_entry__anon5ecd1c550111::player_200         void on_entry(Event const&,FSM& fsm)
201         {
202             fsm.template get_state<player_::Stopped&>().entry_counter=0;
203             fsm.template get_state<player_::Stopped&>().exit_counter=0;
204             fsm.template get_state<player_::Open&>().entry_counter=0;
205             fsm.template get_state<player_::Open&>().exit_counter=0;
206             fsm.template get_state<player_::Empty&>().entry_counter=0;
207             fsm.template get_state<player_::Empty&>().exit_counter=0;
208             fsm.template get_state<player_::Empty&>().some_dummy_data=3;
209             fsm.template get_state<player_::Playing&>().entry_counter=0;
210             fsm.template get_state<player_::Playing&>().exit_counter=0;
211             fsm.template get_state<player_::Paused&>().entry_counter=0;
212             fsm.template get_state<player_::Paused&>().exit_counter=0;
213         }
214 
215     };
216     // Pick a back-end
217     typedef msm::back::state_machine<player_> player;
218 
219 //    static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
220 
221 
BOOST_AUTO_TEST_CASE(my_test)222     BOOST_AUTO_TEST_CASE( my_test )
223     {
224         player p;
225 
226         p.start();
227         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 1,"Empty entry not called correctly");
228 
229         p.process_event(open_close());
230         BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Open should be active"); //Open
231         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 1,"Empty exit not called correctly");
232         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().entry_counter == 1,"Open entry not called correctly");
233 
234         // test the serialization
235         std::ofstream ofs("fsm.txt");
236         // save fsm to archive (current state is Open)
237         {
238             boost::archive::text_oarchive oa(ofs);
239             // write class instance to archive
240             oa << p;
241         }
242         // reload fsm in state Open
243         player p2;
244         {
245             // create and open an archive for input
246             std::ifstream ifs("fsm.txt");
247             boost::archive::text_iarchive ia(ifs);
248             // read class state from archive
249             ia >> p2;
250         }
251         // we now use p2 as it was loaded
252         // check that we kept Empty's data value
253         BOOST_CHECK_MESSAGE(p2.get_state<player_::Empty&>().some_dummy_data == 3,"Empty not deserialized correctly");
254         BOOST_CHECK_MESSAGE(p2.front_end_data == 4,"Front-end not deserialized correctly");
255 
256         p.process_event(open_close());
257         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
258         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
259         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
260         BOOST_CHECK_MESSAGE(p.can_close_drawer_counter == 1,"guard not called correctly");
261 
262         p.process_event(
263             cd_detected("louie, louie",DISK_DVD));
264         BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Empty should be active"); //Empty
265         BOOST_CHECK_MESSAGE(p.get_state<player_::Open&>().exit_counter == 1,"Open exit not called correctly");
266         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().entry_counter == 2,"Empty entry not called correctly");
267 
268         p.process_event(
269             cd_detected("louie, louie",DISK_CD));
270         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
271         BOOST_CHECK_MESSAGE(p.get_state<player_::Empty&>().exit_counter == 2,"Empty exit not called correctly");
272         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 1,"Stopped entry not called correctly");
273 
274         p.process_event(play());
275         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
276         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 1,"Stopped exit not called correctly");
277         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 1,"Playing entry not called correctly");
278         BOOST_CHECK_MESSAGE(p.start_playback_counter == 1,"action not called correctly");
279 
280         p.process_event(pause());
281         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
282         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 1,"Playing exit not called correctly");
283         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 1,"Paused entry not called correctly");
284 
285         // go back to Playing
286         p.process_event(end_pause());
287         BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Playing should be active"); //Playing
288         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 1,"Paused exit not called correctly");
289         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().entry_counter == 2,"Playing entry not called correctly");
290 
291         p.process_event(pause());
292         BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Paused should be active"); //Paused
293         BOOST_CHECK_MESSAGE(p.get_state<player_::Playing&>().exit_counter == 2,"Playing exit not called correctly");
294         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().entry_counter == 2,"Paused entry not called correctly");
295 
296         p.process_event(stop());
297         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
298         BOOST_CHECK_MESSAGE(p.get_state<player_::Paused&>().exit_counter == 2,"Paused exit not called correctly");
299         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 2,"Stopped entry not called correctly");
300 
301         p.process_event(stop());
302         BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
303         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().exit_counter == 2,"Stopped exit not called correctly");
304         BOOST_CHECK_MESSAGE(p.get_state<player_::Stopped&>().entry_counter == 3,"Stopped entry not called correctly");
305     }
306 }
307 // eliminate object tracking (even if serialized through a pointer)
308 // at the risk of a programming error creating duplicate objects.
309 // this is to get rid of warning because p is not const
310 BOOST_CLASS_TRACKING(player, boost::serialization::track_never)
311 
312