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 // back-end
14 #include <boost/msm/back/state_machine.hpp>
15 //front-end
16 #include <boost/msm/front/state_machine_def.hpp>
17 // functors
18 #include <boost/msm/front/functor_row.hpp>
19 #include <boost/msm/front/euml/common.hpp>
20 // for And_ operator
21 #include <boost/msm/front/euml/operator.hpp>
22 // for func_state and func_state_machine
23 #include <boost/msm/front/euml/state_grammar.hpp>
24
25 using namespace std;
26 namespace msm = boost::msm;
27 namespace mpl = boost::mpl;
28 using namespace msm::front;
29 // for And_ operator
30 using namespace msm::front::euml;
31
32 namespace // Concrete FSM implementation
33 {
34 // events
35 struct play {};
36 struct end_pause {};
37 struct stop {};
38 struct pause {};
39 struct open_close {};
40
41 // A "complicated" event type that carries some data.
42 enum DiskTypeEnum
43 {
44 DISK_CD=0,
45 DISK_DVD=1
46 };
47 struct cd_detected
48 {
cd_detected__anonc272a8470111::cd_detected49 cd_detected(std::string name, DiskTypeEnum diskType)
50 : name(name),
51 disc_type(diskType)
52 {}
53
54 std::string name;
55 DiskTypeEnum disc_type;
56 };
57
58
59 // The list of FSM states
60 // entry and exit functors for Empty
61 struct Empty_Entry
62 {
63 template <class Event,class FSM,class STATE>
operator ()__anonc272a8470111::Empty_Entry64 void operator()(Event const&,FSM&,STATE& )
65 {
66 std::cout << "entering: Empty" << std::endl;
67 }
68 };
69 struct Empty_Exit
70 {
71 template <class Event,class FSM,class STATE>
operator ()__anonc272a8470111::Empty_Exit72 void operator()(Event const&,FSM&,STATE& )
73 {
74 std::cout << "leaving: Empty" << std::endl;
75 }
76 };
77 // definition of Empty
78 struct Empty_tag {};
79 typedef msm::front::euml::func_state<Empty_tag,Empty_Entry,Empty_Exit> Empty;
80
81 struct Open_Entry
82 {
83 template <class Event,class FSM,class STATE>
operator ()__anonc272a8470111::Open_Entry84 void operator()(Event const&,FSM&,STATE& )
85 {
86 std::cout << "entering: Open" << std::endl;
87 }
88 };
89 struct Open_Exit
90 {
91 template <class Event,class FSM,class STATE>
operator ()__anonc272a8470111::Open_Exit92 void operator()(Event const&,FSM&,STATE& )
93 {
94 std::cout << "leaving: Open" << std::endl;
95 }
96 };
97 struct Open_tag {};
98 typedef msm::front::euml::func_state<Open_tag,Open_Entry,Open_Exit> Open;
99
100 // states without entry/exit actions (can be declared as functor state, just without functors ;-) )
101 struct Stopped_tag {};
102 typedef msm::front::euml::func_state<Stopped_tag> Stopped;
103
104 struct Playing_tag {};
105 typedef msm::front::euml::func_state<Playing_tag> Playing;
106
107 // state not defining any entry or exit (declared as simple state. Equivalent)
108 struct Paused_tag {};
109 typedef msm::front::euml::func_state<Paused_tag> Paused;
110
111 // the initial state of the player SM. Must be defined
112 typedef Empty initial_state;
113
114 // transition actions
115 // as the functors are generic on events, fsm and source/target state,
116 // you can reuse them in another machine if you wish
117 struct TestFct
118 {
119 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::TestFct120 void operator()(EVT const&, FSM&,SourceState& ,TargetState& )
121 {
122 cout << "transition with event:" << typeid(EVT).name() << endl;
123 }
124 };
125 struct start_playback
126 {
127 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::start_playback128 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
129 {
130 cout << "player::start_playback" << endl;
131 }
132 };
133 struct open_drawer
134 {
135 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::open_drawer136 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
137 {
138 cout << "player::open_drawer" << endl;
139 }
140 };
141 struct close_drawer
142 {
143 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::close_drawer144 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
145 {
146 cout << "player::close_drawer" << endl;
147 }
148 };
149 struct store_cd_info
150 {
151 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::store_cd_info152 void operator()(EVT const&,FSM& fsm ,SourceState& ,TargetState& )
153 {
154 cout << "player::store_cd_info" << endl;
155 fsm.process_event(play());
156 }
157 };
158 struct stop_playback
159 {
160 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::stop_playback161 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
162 {
163 cout << "player::stop_playback" << endl;
164 }
165 };
166 struct pause_playback
167 {
168 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::pause_playback169 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
170 {
171 cout << "player::pause_playback" << endl;
172 }
173 };
174 struct resume_playback
175 {
176 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::resume_playback177 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
178 {
179 cout << "player::resume_playback" << endl;
180 }
181 };
182 struct stop_and_open
183 {
184 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::stop_and_open185 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
186 {
187 cout << "player::stop_and_open" << endl;
188 }
189 };
190 struct stopped_again
191 {
192 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::stopped_again193 void operator()(EVT const& ,FSM& ,SourceState& ,TargetState& )
194 {
195 cout << "player::stopped_again" << endl;
196 }
197 };
198 // guard conditions
199 struct DummyGuard
200 {
201 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::DummyGuard202 bool operator()(EVT const& evt,FSM& fsm,SourceState& src,TargetState& tgt)
203 {
204 return true;
205 }
206 };
207 struct good_disk_format
208 {
209 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::good_disk_format210 bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
211 {
212 // to test a guard condition, let's say we understand only CDs, not DVD
213 if (evt.disc_type != DISK_CD)
214 {
215 std::cout << "wrong disk, sorry" << std::endl;
216 return false;
217 }
218 return true;
219 }
220 };
221 struct always_true
222 {
223 template <class EVT,class FSM,class SourceState,class TargetState>
operator ()__anonc272a8470111::always_true224 bool operator()(EVT const& evt ,FSM&,SourceState& ,TargetState& )
225 {
226 return true;
227 }
228 };
229
230 // Transition table for player
231 struct player_transition_table : mpl::vector<
232 // Start Event Next Action Guard
233 // +---------+-------------+---------+---------------------------+----------------------+
234 Row < Stopped , play , Playing , ActionSequence_
235 <mpl::vector<
236 TestFct,start_playback> >
237 , DummyGuard >,
238 Row < Stopped , open_close , Open , open_drawer , none >,
239 Row < Stopped , stop , Stopped , none , none >,
240 // +---------+-------------+---------+---------------------------+----------------------+
241 Row < Open , open_close , Empty , close_drawer , none >,
242 // +---------+-------------+---------+---------------------------+----------------------+
243 Row < Empty , open_close , Open , open_drawer , none >,
244 Row < Empty , cd_detected , Stopped , store_cd_info , And_<good_disk_format,
245 always_true> >,
246 // +---------+-------------+---------+---------------------------+----------------------+
247 Row < Playing , stop , Stopped , stop_playback , none >,
248 Row < Playing , pause , Paused , pause_playback , none >,
249 Row < Playing , open_close , Open , stop_and_open , none >,
250 // +---------+-------------+---------+---------------------------+----------------------+
251 Row < Paused , end_pause , Playing , resume_playback , none >,
252 Row < Paused , stop , Stopped , stop_playback , none >,
253 Row < Paused , open_close , Open , stop_and_open , none >
254 // +---------+-------------+---------+---------------------------+----------------------+
255 > {};
256 // fsm definition
257 struct player_tag {};
258 typedef msm::front::euml::func_state_machine<Playing_tag,
259 // transition table
260 player_transition_table,
261 //Initial state
262 Empty> player_;
263 // Pick a back-end
264 typedef msm::back::state_machine<player_> player;
265
266 //
267 // Testing utilities.
268 //
269 static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)270 void pstate(player const& p)
271 {
272 std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
273 }
274
test()275 void test()
276 {
277 player p;
278 // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
279 p.start();
280 // go to Open, call on_exit on Empty, then action, then on_entry on Open
281 p.process_event(open_close()); pstate(p);
282 p.process_event(open_close()); pstate(p);
283 // will be rejected, wrong disk type
284 p.process_event(
285 cd_detected("louie, louie",DISK_DVD)); pstate(p);
286 p.process_event(
287 cd_detected("louie, louie",DISK_CD)); pstate(p);
288 // no need to call play() as the previous event does it in its action method
289 //p.process_event(play());
290
291 // at this point, Play is active
292 p.process_event(pause()); pstate(p);
293 // go back to Playing
294 p.process_event(end_pause()); pstate(p);
295 p.process_event(pause()); pstate(p);
296 p.process_event(stop()); pstate(p);
297 // event leading to the same state
298 // no action method called as it is not present in the transition table
299 p.process_event(stop()); pstate(p);
300 }
301 }
302
main()303 int main()
304 {
305 test();
306 return 0;
307 }
308