• 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 #include <string>
13 #include "boost/mpl/vector/vector30.hpp"
14 
15 #include <boost/msm/back/state_machine.hpp>
16 #include <boost/msm/front/state_machine_def.hpp>
17 #include <boost/msm/back/tools.hpp>
18 
19 using namespace std;
20 namespace msm = boost::msm;
21 namespace mpl = boost::mpl;
22 
23 namespace  // Concrete FSM implementation
24 {
25     // events
26     struct play {};
27     struct end_pause {};
28     struct stop {};
29     struct pause {};
30     struct open_close {};
31     struct NextSong {};
32     struct PreviousSong {};
33     struct ThreeSec {};
34     struct TenSec {};
35     struct error_found {};
36     struct end_error {};
37     struct cd_detected {};
38 
39     // a simple visitor
40     struct SomeVisitor
41     {
42         template <class T>
visit_state__anonc11a91470111::SomeVisitor43         void visit_state(T* astate,int i)
44         {
45             std::cout << "visiting state:" << typeid(*astate).name()
46                 << " with data:" << i << std::endl;
47         }
48     };
49     // base state for all states of ths fsm, to make them visitable
50     struct my_visitable_state
51     {
52         // signature of the accept function
53         typedef msm::back::args<void,SomeVisitor&,int> accept_sig;
54 
55         // we also want polymorphic states
~my_visitable_state__anonc11a91470111::my_visitable_state56         virtual ~my_visitable_state() {}
57         // default implementation for states who do not need to be visited
accept__anonc11a91470111::my_visitable_state58         void accept(SomeVisitor&,int) const {}
59         // or if you want all states to be visited, provide an implementation
60         /*
61         void accept(SomeVisitor& vis,int i) const
62         {
63             vis.visit_state(this,i);
64         }
65         */
66     };
67 
68     // Concrete FSM implementation
69     struct player_ : public msm::front::state_machine_def<player_,my_visitable_state>
70     {
71         template <class Event,class FSM>
on_entry__anonc11a91470111::player_72         void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;}
73         // The list of FSM states
74         struct Empty : public msm::front::state<my_visitable_state>
75         {
76             typedef mpl::vector<play> deferred_events;
77             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Empty78             void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
79             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Empty80             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
81             // this state wants to be visited
accept__anonc11a91470111::player_::Empty82             void accept(SomeVisitor& vis,int i) const
83             {
84                 vis.visit_state(this,i);
85             }
86         };
87         struct Open : public msm::front::state<my_visitable_state>
88         {
89             typedef mpl::vector<play> deferred_events;
90             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Open91             void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;}
92             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Open93             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
94             // this state wants to be visited
accept__anonc11a91470111::player_::Open95             void accept(SomeVisitor& vis,int i) const
96             {
97                 vis.visit_state(this,i);
98             }
99         };
100         struct Stopped : public msm::front::state<my_visitable_state>
101         {
102             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Stopped103             void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;}
104             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Stopped105             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
106             // this state wants to be visited
accept__anonc11a91470111::player_::Stopped107             void accept(SomeVisitor& vis,int i) const
108             {
109                 // note that visiting will recursively visit sub-states
110                 vis.visit_state(this,i);
111             }
112         };
113 
114         struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state >
115         {
116             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_117             void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
118             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_119             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
accept__anonc11a91470111::player_::Playing_120             void accept(SomeVisitor& vis,int i) const
121             {
122                 // note that visiting will recursively visit sub-states
123                 vis.visit_state(this,i);
124             }
125             // The list of FSM states
126             // the Playing state machine contains a state which is himself a state machine
127             // so we have a SM containing a SM containing a SM
128             struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state>
129             {
130                 template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_::Song1_131                 void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;}
132                 template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_::Song1_133                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;}
accept__anonc11a91470111::player_::Playing_::Song1_134                 void accept(SomeVisitor& vis,int i) const
135                 {
136                     vis.visit_state(this,i);
137                 }
138                 struct LightOn : public msm::front::state<my_visitable_state>
139                 {
140                     template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_::Song1_::LightOn141                     void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;}
142                     template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_::Song1_::LightOn143                     void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;}
accept__anonc11a91470111::player_::Playing_::Song1_::LightOn144                     void accept(SomeVisitor& vis,int i) const
145                     {
146                         // note that visiting will recursively visit sub-states
147                         vis.visit_state(this,i);
148                     }
149                 };
150                 struct LightOff : public msm::front::state<my_visitable_state>
151                 {
152                     template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_::Song1_::LightOff153                     void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;}
154                     template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_::Song1_::LightOff155                     void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;}
156                 };
157                 // the initial state. Must be defined
158                 typedef LightOn initial_state;
159                 // transition actions
turn_light_off__anonc11a91470111::player_::Playing_::Song1_160                 void turn_light_off(ThreeSec const&)       { std::cout << "3s off::turn light off\n"; }
161                 // guard conditions
162 
163                 typedef Song1_ s; // makes transition table cleaner
164                 // Transition table for Song1
165                 struct transition_table : mpl::vector1<
166                     //    Start     Event         Next      Action				Guard
167                     //  +---------+-------------+---------+---------------------+----------------------+
168                   a_row < LightOn , ThreeSec    , LightOff, &s::turn_light_off                        >
169                     //  +---------+-------------+---------+---------------------+----------------------+
170                 > {};
171                 // Replaces the default no-transition response.
172                 template <class FSM,class Event>
no_transition__anonc11a91470111::player_::Playing_::Song1_173                 void no_transition(Event const& e, FSM&,int state)
174                 {
175                     std::cout << "no transition from state " << state
176                         << " on event " << typeid(e).name() << std::endl;
177                 }
178             };
179             typedef msm::back::state_machine<Song1_> Song1;
180 
181             struct Song2 : public msm::front::state<my_visitable_state>
182             {
183                 template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_::Song2184                 void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;}
185                 template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_::Song2186                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;}
187             };
188             struct Song3 : public msm::front::state<my_visitable_state>
189             {
190                 template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Playing_::Song3191                 void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;}
192                 template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Playing_::Song3193                 void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;}
194             };
195             // the initial state. Must be defined
196             typedef Song1 initial_state;
197             // transition actions
start_next_song__anonc11a91470111::player_::Playing_198             void start_next_song(NextSong const&)       { std::cout << "Playing::start_next_song\n"; }
start_prev_song__anonc11a91470111::player_::Playing_199             void start_prev_song(PreviousSong const&)       { std::cout << "Playing::start_prev_song\n"; }
200             // guard conditions
201 
202             typedef Playing_ pl; // makes transition table cleaner
203             // Transition table for Playing
204             struct transition_table : mpl::vector4<
205                 //    Start     Event         Next      Action				Guard
206                 //  +---------+-------------+---------+---------------------+----------------------+
207                 a_row < Song1   , NextSong    , Song2   , &pl::start_next_song                      >,
208                 a_row < Song2   , PreviousSong, Song1   , &pl::start_prev_song                      >,
209                 a_row < Song2   , NextSong    , Song3   , &pl::start_next_song                      >,
210                 a_row < Song3   , PreviousSong, Song2   , &pl::start_prev_song                      >
211                 //  +---------+-------------+---------+---------------------+----------------------+
212             > {};
213             // Replaces the default no-transition response.
214             template <class FSM,class Event>
no_transition__anonc11a91470111::player_::Playing_215             void no_transition(Event const& e, FSM&,int state)
216             {
217                 std::cout << "no transition from state " << state
218                     << " on event " << typeid(e).name() << std::endl;
219             }
220         };
221         typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
222 
223         struct Paused : public msm::front::state<my_visitable_state>
224         {
225             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::Paused226             void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;}
227             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::Paused228             void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;}
229         };
230 
231         struct AllOk : public msm::front::state<my_visitable_state>
232         {
233             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::AllOk234             void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;}
235             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::AllOk236             void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;}
237         };
238         struct ErrorMode :
239             public msm::front::interrupt_state<end_error,my_visitable_state>
240         {
241             template <class Event,class FSM>
on_entry__anonc11a91470111::player_::ErrorMode242             void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;}
243             template <class Event,class FSM>
on_exit__anonc11a91470111::player_::ErrorMode244             void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;}
accept__anonc11a91470111::player_::ErrorMode245             void accept(SomeVisitor& vis,int i) const
246             {
247                 vis.visit_state(this,i);
248             }
249         };
250 
251         // the initial state of the player SM. Must be defined
252         typedef mpl::vector<Empty,AllOk> initial_state;
253 
254         // transition actions
start_playback__anonc11a91470111::player_255         void start_playback(play const&)       { std::cout << "player::start_playback\n"; }
open_drawer__anonc11a91470111::player_256         void open_drawer(open_close const&)    { std::cout << "player::open_drawer\n"; }
close_drawer__anonc11a91470111::player_257         void close_drawer(open_close const&)   { std::cout << "player::close_drawer\n"; }
store_cd_info__anonc11a91470111::player_258         void store_cd_info(cd_detected const&)
259         {
260             std::cout << "player::store_cd_info\n";
261         }
stop_playback__anonc11a91470111::player_262         void stop_playback(stop const&)        { std::cout << "player::stop_playback\n"; }
pause_playback__anonc11a91470111::player_263         void pause_playback(pause const&)      { std::cout << "player::pause_playback\n"; }
resume_playback__anonc11a91470111::player_264         void resume_playback(end_pause const&)      { std::cout << "player::resume_playback\n"; }
stop_and_open__anonc11a91470111::player_265         void stop_and_open(open_close const&)  { std::cout << "player::stop_and_open\n"; }
stopped_again__anonc11a91470111::player_266         void stopped_again(stop const&)	{std::cout << "player::stopped_again\n";}
report_error__anonc11a91470111::player_267         void report_error(error_found const&) {std::cout << "player::report_error\n";}
report_end_error__anonc11a91470111::player_268         void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";}
269         // guard conditions
270 
271 
272         typedef player_ p; // makes transition table cleaner
273 
274         // Transition table for player
275         struct transition_table : mpl::vector<
276             //    Start     Event         Next      Action				Guard
277             //  +---------+-------------+---------+---------------------+----------------------+
278           a_row < Stopped , play        , Playing , &p::start_playback                        >,
279           a_row < Stopped , open_close  , Open    , &p::open_drawer                           >,
280           a_row < Stopped , stop        , Stopped , &p::stopped_again                         >,
281             //  +---------+-------------+---------+---------------------+----------------------+
282           a_row < Open    , open_close  , Empty   , &p::close_drawer                          >,
283             //  +---------+-------------+---------+---------------------+----------------------+
284           a_row < Empty   , open_close  , Open    , &p::open_drawer                           >,
285           a_row < Empty   , cd_detected , Stopped , &p::store_cd_info                         >,
286             //  +---------+-------------+---------+---------------------+----------------------+
287           a_row < Playing , stop        , Stopped , &p::stop_playback                         >,
288           a_row < Playing , pause       , Paused  , &p::pause_playback                        >,
289           a_row < Playing , open_close  , Open    , &p::stop_and_open                         >,
290             //  +---------+-------------+---------+---------------------+----------------------+
291           a_row < Paused  , end_pause   , Playing , &p::resume_playback                       >,
292           a_row < Paused  , stop        , Stopped , &p::stop_playback                         >,
293           a_row < Paused  , open_close  , Open    , &p::stop_and_open                         >,
294             //  +---------+-------------+---------+---------------------+----------------------+
295           a_row < AllOk   , error_found ,ErrorMode, &p::report_error                          >,
296           a_row <ErrorMode,end_error    ,AllOk    , &p::report_end_error                      >
297             //  +---------+-------------+---------+---------------------+----------------------+
298         > {};
299 
300         // Replaces the default no-transition response.
301         template <class FSM,class Event>
no_transition__anonc11a91470111::player_302         void no_transition(Event const& e, FSM&,int state)
303         {
304             std::cout << "no transition from state " << state
305                 << " on event " << typeid(e).name() << std::endl;
306         }
307     };
308 
309     // back-end
310     typedef msm::back::state_machine<player_> player;
311 
312     //
313     // Testing utilities.
314     //
315 
pstate(player const & p)316     void pstate(player const& p)
317     {
318         static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" };
319         for (unsigned int i=0;i<player::nr_regions::value;++i)
320         {
321             std::cout << " -> " << state_names[p.current_state()[i]] << std::endl;
322         }
323     }
test()324     void test()
325     {
326         player p;
327         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
328         p.start();
329 
330         // test deferred event
331         // deferred in Empty and Open, will be handled only after event cd_detected
332         p.process_event(play());
333 
334         // go to Open, call on_exit on Empty, then action, then on_entry on Open
335         p.process_event(open_close()); pstate(p);
336         // visiting Paused and AllOk, but only Paused cares
337         SomeVisitor vis;
338         p.visit_current_states(boost::ref(vis),1);
339         p.process_event(open_close()); pstate(p);
340         // visiting Empty and AllOk, but only Empty cares
341         p.visit_current_states(boost::ref(vis),2);
342 
343         p.process_event(cd_detected());
344         // no need to call play() as the previous event does it in its action method
345         //p.process_event(play());
346         // at this point, Play is active, along FirstSong and LightOn
347         pstate(p);
348         // visiting Playing+Song1+LightOn and AllOk, but only Playing+Song1+LightOn care
349         p.visit_current_states(boost::ref(vis),3);
350 
351         // Stop will be active
352         p.process_event(stop());  pstate(p);
353 
354         // visiting when both regions have an active state who wants to be visited
355         p.process_event(error_found());
356         p.visit_current_states(boost::ref(vis),5);
357 
358 
359     }
360 }
361 
main()362 int main()
363 {
364     test();
365     return 0;
366 }
367 
368 
369