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