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 
44     // Flags. Allow information about a property of the current state
45     BOOST_MSM_EUML_FLAG(PlayingPaused)
46     BOOST_MSM_EUML_FLAG(CDLoaded)
47     BOOST_MSM_EUML_FLAG(FirstSongPlaying)
48 
49     // Concrete FSM implementation
50 
51     // The list of FSM states
52 
53     BOOST_MSM_EUML_STATE((  Empty_Entry,
54                             Empty_Exit,
55                             attributes_ << no_attributes_,
56                             configure_ << play // defer play
57                           ),
58                           Empty)
59 
60     BOOST_MSM_EUML_STATE((  Open_Entry,
61                             Open_Exit,
62                             attributes_ << no_attributes_,
63                             configure_<< CDLoaded << play // defer play, flag state with CDLoaded
64                           ),
65                           Open)
66 
67     BOOST_MSM_EUML_STATE((  Stopped_Entry,
68                             Stopped_Exit,
69                             attributes_ << no_attributes_,
70                             configure_<< CDLoaded // flag state with CDLoaded
71                           ),
72                           Stopped)
73 
74     // state not defining any entry or exit
75     BOOST_MSM_EUML_STATE((  no_action,
76                             no_action,
77                             attributes_ << no_attributes_,
78                             configure_<< PlayingPaused << CDLoaded // flag state with CDLoaded and PlayingPaused
79                           ),
80                           Paused)
81 
82     BOOST_MSM_EUML_STATE(( AllOk_Entry,AllOk_Exit ),AllOk)
83 
84     // a terminate state
85     //BOOST_MSM_EUML_TERMINATE_STATE(( ErrorMode_Entry,ErrorMode_Exit ),ErrorMode)
86     // or as an interrupt state
87     BOOST_MSM_EUML_INTERRUPT_STATE(( end_error,ErrorMode_Entry,ErrorMode_Exit ),ErrorMode)
88 
89     // Playing is now a state machine itself.
90     // It has 3 substates
91     BOOST_MSM_EUML_STATE((  Song1_Entry,
92                             Song1_Exit,
93                             attributes_ << no_attributes_,
94                             configure_<< FirstSongPlaying ),Song1)
95 
96     BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2)
97     BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3)
98 
99     // Playing has a transition table
100     BOOST_MSM_EUML_TRANSITION_TABLE((
101         //  +------------------------------------------------------------------------------+
102         Song2  == Song1 + next_song       / start_next_song,
103         Song1  == Song2 + previous_song   / start_prev_song,
104         Song3  == Song2 + next_song       / start_next_song,
105         Song2  == Song3 + previous_song   / start_prev_song
106         //  +------------------------------------------------------------------------------+
107         ),playing_transition_table )
108 
109     BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT
110                                         init_ << Song1, // Init State
111                                         no_action, // entry
112                                         no_action, // exit
113                                         attributes_ << no_attributes_, //attributes
114                                         configure_<< PlayingPaused << CDLoaded //flags
115                                         ),Playing_)
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           //  +------------------------------------------------------------------------------+
144           Open      == Empty    + open_close  / open_drawer,
145           Open      == Paused   + open_close  / stop_and_open,
146           Open      == Stopped  + open_close  / open_drawer,
147           Open      == Playing  + open_close  / stop_and_open,
148           //  +------------------------------------------------------------------------------+
149           Paused    == Playing  + pause       / pause_playback,
150           //  +------------------------------------------------------------------------------+
151           Stopped   == Playing  + stop        / stop_playback,
152           Stopped   == Paused   + stop        / stop_playback,
153           Stopped   == Empty    + cd_detected [good_disk_format&&
154                                                      (event_(cd_type)==Int_<DISK_CD>())]
155                                                     / (store_cd_info,process_(play)),
156           Stopped   == Stopped  + stop,
157           ErrorMode == AllOk    + error_found / report_error,
158           AllOk     == ErrorMode+ end_error   / report_end_error
159           //  +------------------------------------------------------------------------------+
160           ),transition_table)
161 
162     // create a state machine "on the fly"
163     BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
164                                         init_ << Empty<< AllOk, // Init State
165                                         no_action, // Entry
166                                         no_action, // Exit
167                                         attributes_ << no_attributes_, // Attributes
168                                         configure_ << no_configure_, // configuration
169                                         Log_No_Transition // no_transition handler
170                                         ),
171                                       player_) //fsm name
172 
173     // choice of back-end
174     typedef msm::back::state_machine<player_> player;
175 
176     //
177     // Testing utilities.
178     //
179     static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing","AllOk","ErrorMode" };
pstate(player const & p)180     void pstate(player const& p)
181     {
182         // we have now several active states, which we show
183         for (unsigned int i=0;i<player::nr_regions::value;++i)
184         {
185             std::cout << " -> " << state_names[p.current_state()[i]] << std::endl;
186         }
187     }
188 
test()189     void test()
190     {
191         player p;
192         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
193         p.start();
194 
195         // tests some flags
196         std::cout << "CDLoaded active:" << std::boolalpha
197                  << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl; //=> false (no CD yet)
198         // go to Open, call on_exit on Empty, then action, then on_entry on Open
199         p.process_event(open_close); pstate(p);
200         p.process_event(open_close); pstate(p);
201         // will be rejected, wrong disk type
202         p.process_event(
203             cd_detected("louie, louie",DISK_DVD)); pstate(p);
204         p.process_event(
205             cd_detected("louie, louie",DISK_CD)); pstate(p);
206         // no need to call play as the previous event does it in its action method
207         //p.process_event(play);
208 
209         // at this point, Play is active
210         std::cout << "PlayingPaused active:" << std::boolalpha
211                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true
212         std::cout << "FirstSong active:" << std::boolalpha
213                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> true
214 
215         // make transition happen inside it. Player has no idea about this event but it's ok.
216         p.process_event(next_song);pstate(p); //2nd song active
217         p.process_event(next_song);pstate(p);//3rd song active
218         p.process_event(previous_song);pstate(p);//2nd song active
219         std::cout << "FirstSong active:" << std::boolalpha
220                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(FirstSongPlaying)>() << std::endl;//=> false
221         std::cout << "PlayingPaused active:" << std::boolalpha
222                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true
223 
224         // at this point, Play is active
225         p.process_event(pause); pstate(p);
226         std::cout << "PlayingPaused active:" << std::boolalpha
227                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> true
228 
229         // go back to Playing
230         p.process_event(end_pause);  pstate(p);
231         p.process_event(pause); pstate(p);
232         p.process_event(stop);  pstate(p);
233         std::cout << "PlayingPaused active:" << std::boolalpha
234                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(PlayingPaused)>() << std::endl;//=> false
235         std::cout << "CDLoaded active:" << std::boolalpha
236                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded)>() << std::endl;//=> true
237         // by default, the flags are OR'ed but you can also use AND. Then the flag must be present in
238         // all of the active states
239         std::cout << "CDLoaded active with AND:" << std::boolalpha
240                   << p.is_flag_active<BOOST_MSM_EUML_FLAG_NAME(CDLoaded),player::Flag_AND>() << std::endl;//=> false
241 
242         // event leading to the same state
243         // no action method called as none is defined in the transition table
244         p.process_event(stop);  pstate(p);
245 
246         // event leading to a terminal/interrupt state
247         p.process_event(error_found);  pstate(p);
248         // try generating more events
249         std::cout << "Trying to generate another event" << std::endl; // will not work, fsm is terminated or interrupted
250         p.process_event(play);pstate(p);
251         std::cout << "Trying to end the error" << std::endl; // will work only if ErrorMode is interrupt state
252         p.process_event(end_error);pstate(p);
253         std::cout << "Trying to generate another event" << std::endl; // will work only if ErrorMode is interrupt state
254         p.process_event(play);pstate(p);
255 
256     }
257 }
258 
main()259 int main()
260 {
261     test();
262     return 0;
263 }
264