• 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 <vector>
12  #include <iostream>
13  
14  #include <boost/msm/back/state_machine.hpp>
15  #include <boost/msm/front/euml/euml.hpp>
16  
17  using namespace std;
18  using namespace boost::msm::front::euml;
19  namespace msm = boost::msm;
20  
21  // entry/exit/action/guard logging functors
22  #include "logging_functors.h"
23  
24  namespace  // Concrete FSM implementation
25  {
26      // events
27      BOOST_MSM_EUML_EVENT(play)
28      BOOST_MSM_EUML_EVENT(end_pause)
29      BOOST_MSM_EUML_EVENT(stop)
30      BOOST_MSM_EUML_EVENT(pause)
31      BOOST_MSM_EUML_EVENT(open_close)
32      BOOST_MSM_EUML_EVENT(next_song)
33      BOOST_MSM_EUML_EVENT(previous_song)
34      BOOST_MSM_EUML_EVENT(end_error)
35      BOOST_MSM_EUML_EVENT(error_found)
36  
37      // A "complicated" event type that carries some data.
38      BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name)
39      BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type)
40      BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes)
41      BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes)
42  
43      // Flags. Allow information about a property of the current state
44      BOOST_MSM_EUML_FLAG(PlayingPaused)
45      BOOST_MSM_EUML_FLAG(CDLoaded)
46      BOOST_MSM_EUML_FLAG(FirstSongPlaying)
47  
48      // Concrete FSM implementation
49      // The list of FSM states
50      BOOST_MSM_EUML_STATE((  Empty_Entry,
51                              Empty_Exit,
52                              attributes_ << no_attributes_,
53                              configure_ << no_configure_
54                            ),
55                            Empty)
56  
57      BOOST_MSM_EUML_STATE((  Open_Entry,
58                              Open_Exit,
59                              attributes_ << no_attributes_,
60                              configure_<< CDLoaded // flag state with CDLoaded
61                            ),
62                            Open)
63  
64      BOOST_MSM_EUML_STATE((  Stopped_Entry,
65                              Stopped_Exit,
66                              attributes_ << no_attributes_,
67                              configure_<< CDLoaded // flag state with CDLoaded
68                            ),
69                            Stopped)
70  
71      // state not defining any entry or exit
72      BOOST_MSM_EUML_STATE((  no_action,
73                              no_action,
74                              attributes_ << no_attributes_,
75                              configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused
76                            ),
77                            Paused)
78  
79      BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk)
80  
81      // a terminate state
82      //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode)
83      // or as an interrupt state
84      BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode)
85  
86      // Playing is now a state machine itself.
87  
88      // It has 3 substates
89      BOOST_MSM_EUML_STATE((  Song1_Entry,
90      Song1_Exit,
91      attributes_ << no_attributes_,
92      configure_<< FirstSongPlaying ),Song1)
93  
94      BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2)
95      BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3)
96  
97  
98      // Playing has a transition table
99      BOOST_MSM_EUML_TRANSITION_TABLE((
100          //  +------------------------------------------------------------------------------+
101          Song2  == Song1 + next_song       / start_next_song,
102          Song1  == Song2 + previous_song   / start_prev_song,
103          Song3  == Song2 + next_song       / start_next_song,
104          Song2  == Song3 + previous_song   / start_prev_song
105          //  +------------------------------------------------------------------------------+
106          ),playing_transition_table )
107  
108      BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT
109                                          init_ << Song1, // Init State
110                                          no_action, // entry
111                                          no_action, // exit
112                                          attributes_ << no_attributes_, //attributes
113                                          configure_<< PlayingPaused << CDLoaded //flags
114                                          ),Playing_)
115  
116      // choice of back-end
117      typedef msm::back::state_machine<Playing_> Playing_type;
118      Playing_type const Playing;
119  
120      // guard conditions
BOOST_MSM_EUML_ACTION(good_disk_format)121      BOOST_MSM_EUML_ACTION(good_disk_format)
122      {
123          template <class FSM,class EVT,class SourceState,class TargetState>
124          bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
125          {
126              // to test a guard condition, let's say we understand only CDs, not DVD
127              if (evt.get_attribute(cd_type)!=DISK_CD)
128              {
129                  std::cout << "wrong disk, sorry" << std::endl;
130                  // just for logging, does not block any transition
131                  return true;
132              }
133              std::cout << "good disk" << std::endl;
134              return true;
135          }
136      };
137      // replaces the old transition table
138      BOOST_MSM_EUML_TRANSITION_TABLE((
139            Playing   == Stopped  + play        / start_playback                  ,
140            Playing   == Paused   + end_pause   / resume_playback                 ,
141            //  +------------------------------------------------------------------------------+
142            Empty     == Open     + open_close  / close_drawer,
143            // we now defer using the defer_ function. This will need deferred_events as config (see below)
144            Empty                 + play        / defer_                          ,
145            //  +------------------------------------------------------------------------------+
146            Open      == Empty    + open_close  / open_drawer                     ,
147            Open      == Paused   + open_close  / stop_and_open                   ,
148            Open      == Stopped  + open_close  / open_drawer                     ,
149            Open      == Playing  + open_close  / stop_and_open                   ,
150            // we now defer using the defer_ function. This will need deferred_events as config (see below)
151            Open                  + play        / defer_                          ,
152            //  +------------------------------------------------------------------------------+
153            Paused    == Playing  + pause       / pause_playback                  ,
154            //  +------------------------------------------------------------------------------+
155            Stopped   == Playing  + stop        / stop_playback                   ,
156            Stopped   == Paused   + stop        / stop_playback                   ,
157            Stopped   == Empty    + cd_detected [good_disk_format&&
158                                                       (event_(cd_type)==Int_<DISK_CD>())]
159                                                      / (store_cd_info,process_(play)),
160            Stopped   == Stopped  + stop                                          ,
161            ErrorMode == AllOk    + error_found / report_error                    ,
162            AllOk     == ErrorMode+ end_error   / report_end_error
163            //  +------------------------------------------------------------------------------+
164            ),transition_table)
165  
166      // create a state machine "on the fly"
167      BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
168                                          init_ << Empty << AllOk, // Init State
169                                          no_action, // Entry
170                                          no_action, // Exit
171                                          attributes_ << no_attributes_, // Attributes
172                                          configure_ << deferred_events, // configuration
173                                          Log_No_Transition // no_transition handler
174                                          ),
175                                        player_) //fsm name
176  
177      // choice of back-end
178      typedef msm::back::state_machine<player_> player;
179  
180      //
181      // Testing utilities.
182      //
183      static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" };
pstate(player const & p)184      void pstate(player const& p)
185      {
186          // we have now several active states, which we show
187          for (unsigned int i=0;i<player::nr_regions::value;++i)
188          {
189              std::cout << " -> " << state_names[p.current_state()[i]] << std::endl;
190          }
191      }
192  
test()193      void test()
194      {
195          player p;
196          // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
197          p.start();
198  
199          // tests some flags
200          std::cout << "CDLoaded active:" << std::boolalpha
201                    << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet)
202          // go to Open, call on_exit on Empty, then action, then on_entry on Open
203          p.process_event(open_close); pstate(p);
204          p.process_event(open_close); pstate(p);
205          // will be rejected, wrong disk type
206          p.process_event(
207              cd_detected("louie, louie",DISK_DVD)); pstate(p);
208          p.process_event(
209              cd_detected("louie, louie",DISK_CD)); pstate(p);
210          // no need to call play as the previous event does it in its action method
211          //p.process_event(play);
212  
213          // at this point, Play is active
214          p.process_event(pause); pstate(p);
215          std::cout << "PlayingPaused active:" << std::boolalpha
216                    << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true
217  
218          // go back to Playing
219          p.process_event(end_pause);  pstate(p);
220          p.process_event(pause); pstate(p);
221          p.process_event(stop);  pstate(p);
222          std::cout << "PlayingPaused active:" << std::boolalpha
223                    << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false
224          std::cout << "CDLoaded active:" << std::boolalpha
225                    << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true
226          // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in
227          // all of the active states
228          std::cout << "CDLoaded active with AND:" << std::boolalpha
229                    << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false
230  
231          // event leading to the same state
232          // no action method called as none is defined in the transition table
233          p.process_event(stop);  pstate(p);
234  
235          // event leading to a terminal/interrupt state
236          p.process_event(error_found);  pstate(p);
237          // try generating more events
238          std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted
239          p.process_event(play);pstate(p);
240          std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state
241          p.process_event(end_error);pstate(p);
242          std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state
243          p.process_event(play);pstate(p);
244      }
245  }
246  
main()247  int main()
248  {
249      test();
250      return 0;
251  }
252