• 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