]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 <iostream> | |
12 | #include <string> | |
13 | #include "boost/mpl/vector/vector30.hpp" | |
14 | ||
15 | #include <boost/msm/back/state_machine.hpp> | |
16 | #include <boost/msm/front/state_machine_def.hpp> | |
17 | #include <boost/msm/back/tools.hpp> | |
18 | ||
19 | using namespace std; | |
20 | namespace msm = boost::msm; | |
21 | namespace mpl = boost::mpl; | |
22 | ||
23 | namespace // Concrete FSM implementation | |
24 | { | |
25 | // events | |
26 | struct play {}; | |
27 | struct end_pause {}; | |
28 | struct stop {}; | |
29 | struct pause {}; | |
30 | struct open_close {}; | |
31 | struct NextSong {}; | |
32 | struct PreviousSong {}; | |
33 | struct ThreeSec {}; | |
34 | struct TenSec {}; | |
35 | struct error_found {}; | |
36 | struct end_error {}; | |
37 | struct cd_detected {}; | |
38 | ||
39 | // a simple visitor | |
40 | struct SomeVisitor | |
41 | { | |
42 | template <class T> | |
43 | void visit_state(T* astate,int i) | |
44 | { | |
45 | std::cout << "visiting state:" << typeid(*astate).name() | |
46 | << " with data:" << i << std::endl; | |
47 | } | |
48 | }; | |
49 | // base state for all states of ths fsm, to make them visitable | |
50 | struct my_visitable_state | |
51 | { | |
52 | // signature of the accept function | |
53 | typedef msm::back::args<void,SomeVisitor&,int> accept_sig; | |
54 | ||
55 | // we also want polymorphic states | |
56 | virtual ~my_visitable_state() {} | |
57 | // default implementation for states who do not need to be visited | |
58 | void accept(SomeVisitor&,int) const {} | |
59 | // or if you want all states to be visited, provide an implementation | |
60 | /* | |
61 | void accept(SomeVisitor& vis,int i) const | |
62 | { | |
63 | vis.visit_state(this,i); | |
64 | } | |
65 | */ | |
66 | }; | |
67 | ||
68 | // Concrete FSM implementation | |
69 | struct player_ : public msm::front::state_machine_def<player_,my_visitable_state> | |
70 | { | |
71 | template <class Event,class FSM> | |
72 | void on_entry(Event const&,FSM& ) {std::cout << "starting: player" << std::endl;} | |
73 | // The list of FSM states | |
74 | struct Empty : public msm::front::state<my_visitable_state> | |
75 | { | |
76 | typedef mpl::vector<play> deferred_events; | |
77 | template <class Event,class FSM> | |
78 | void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;} | |
79 | template <class Event,class FSM> | |
80 | void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;} | |
81 | // this state wants to be visited | |
82 | void accept(SomeVisitor& vis,int i) const | |
83 | { | |
84 | vis.visit_state(this,i); | |
85 | } | |
86 | }; | |
87 | struct Open : public msm::front::state<my_visitable_state> | |
88 | { | |
89 | typedef mpl::vector<play> deferred_events; | |
90 | template <class Event,class FSM> | |
91 | void on_entry(Event const&,FSM& ) {std::cout << "entering: Open" << std::endl;} | |
92 | template <class Event,class FSM> | |
93 | void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;} | |
94 | // this state wants to be visited | |
95 | void accept(SomeVisitor& vis,int i) const | |
96 | { | |
97 | vis.visit_state(this,i); | |
98 | } | |
99 | }; | |
100 | struct Stopped : public msm::front::state<my_visitable_state> | |
101 | { | |
102 | template <class Event,class FSM> | |
103 | void on_entry(Event const&,FSM& ) {std::cout << "entering: Stopped" << std::endl;} | |
104 | template <class Event,class FSM> | |
105 | void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;} | |
106 | // this state wants to be visited | |
107 | void accept(SomeVisitor& vis,int i) const | |
108 | { | |
109 | // note that visiting will recursively visit sub-states | |
110 | vis.visit_state(this,i); | |
111 | } | |
112 | }; | |
113 | ||
114 | struct Playing_ : public msm::front::state_machine_def<Playing_,my_visitable_state > | |
115 | { | |
116 | template <class Event,class FSM> | |
117 | void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;} | |
118 | template <class Event,class FSM> | |
119 | void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;} | |
120 | void accept(SomeVisitor& vis,int i) const | |
121 | { | |
122 | // note that visiting will recursively visit sub-states | |
123 | vis.visit_state(this,i); | |
124 | } | |
125 | // The list of FSM states | |
126 | // the Playing state machine contains a state which is himself a state machine | |
127 | // so we have a SM containing a SM containing a SM | |
128 | struct Song1_ : public msm::front::state_machine_def<Song1_,my_visitable_state> | |
129 | { | |
130 | template <class Event,class FSM> | |
131 | void on_entry(Event const&,FSM& ) {std::cout << "starting: First song" << std::endl;} | |
132 | template <class Event,class FSM> | |
133 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: First Song" << std::endl;} | |
134 | void accept(SomeVisitor& vis,int i) const | |
135 | { | |
136 | vis.visit_state(this,i); | |
137 | } | |
138 | struct LightOn : public msm::front::state<my_visitable_state> | |
139 | { | |
140 | template <class Event,class FSM> | |
141 | void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOn" << std::endl;} | |
142 | template <class Event,class FSM> | |
143 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOn" << std::endl;} | |
144 | void accept(SomeVisitor& vis,int i) const | |
145 | { | |
146 | // note that visiting will recursively visit sub-states | |
147 | vis.visit_state(this,i); | |
148 | } | |
149 | }; | |
150 | struct LightOff : public msm::front::state<my_visitable_state> | |
151 | { | |
152 | template <class Event,class FSM> | |
153 | void on_entry(Event const&,FSM& ) {std::cout << "starting: LightOff" << std::endl;} | |
154 | template <class Event,class FSM> | |
155 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: LightOff" << std::endl;} | |
156 | }; | |
157 | // the initial state. Must be defined | |
158 | typedef LightOn initial_state; | |
159 | // transition actions | |
160 | void turn_light_off(ThreeSec const&) { std::cout << "3s off::turn light off\n"; } | |
161 | // guard conditions | |
162 | ||
163 | typedef Song1_ s; // makes transition table cleaner | |
164 | // Transition table for Song1 | |
165 | struct transition_table : mpl::vector1< | |
166 | // Start Event Next Action Guard | |
167 | // +---------+-------------+---------+---------------------+----------------------+ | |
168 | a_row < LightOn , ThreeSec , LightOff, &s::turn_light_off > | |
169 | // +---------+-------------+---------+---------------------+----------------------+ | |
170 | > {}; | |
171 | // Replaces the default no-transition response. | |
172 | template <class FSM,class Event> | |
173 | void no_transition(Event const& e, FSM&,int state) | |
174 | { | |
175 | std::cout << "no transition from state " << state | |
176 | << " on event " << typeid(e).name() << std::endl; | |
177 | } | |
178 | }; | |
179 | typedef msm::back::state_machine<Song1_> Song1; | |
180 | ||
181 | struct Song2 : public msm::front::state<my_visitable_state> | |
182 | { | |
183 | template <class Event,class FSM> | |
184 | void on_entry(Event const&,FSM& ) {std::cout << "starting: Second song" << std::endl;} | |
185 | template <class Event,class FSM> | |
186 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: Second Song" << std::endl;} | |
187 | }; | |
188 | struct Song3 : public msm::front::state<my_visitable_state> | |
189 | { | |
190 | template <class Event,class FSM> | |
191 | void on_entry(Event const&,FSM& ) {std::cout << "starting: Third song" << std::endl;} | |
192 | template <class Event,class FSM> | |
193 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: Third Song" << std::endl;} | |
194 | }; | |
195 | // the initial state. Must be defined | |
196 | typedef Song1 initial_state; | |
197 | // transition actions | |
198 | void start_next_song(NextSong const&) { std::cout << "Playing::start_next_song\n"; } | |
199 | void start_prev_song(PreviousSong const&) { std::cout << "Playing::start_prev_song\n"; } | |
200 | // guard conditions | |
201 | ||
202 | typedef Playing_ pl; // makes transition table cleaner | |
203 | // Transition table for Playing | |
204 | struct transition_table : mpl::vector4< | |
205 | // Start Event Next Action Guard | |
206 | // +---------+-------------+---------+---------------------+----------------------+ | |
207 | a_row < Song1 , NextSong , Song2 , &pl::start_next_song >, | |
208 | a_row < Song2 , PreviousSong, Song1 , &pl::start_prev_song >, | |
209 | a_row < Song2 , NextSong , Song3 , &pl::start_next_song >, | |
210 | a_row < Song3 , PreviousSong, Song2 , &pl::start_prev_song > | |
211 | // +---------+-------------+---------+---------------------+----------------------+ | |
212 | > {}; | |
213 | // Replaces the default no-transition response. | |
214 | template <class FSM,class Event> | |
215 | void no_transition(Event const& e, FSM&,int state) | |
216 | { | |
217 | std::cout << "no transition from state " << state | |
218 | << " on event " << typeid(e).name() << std::endl; | |
219 | } | |
220 | }; | |
221 | typedef msm::back::state_machine<Playing_,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing; | |
222 | ||
223 | struct Paused : public msm::front::state<my_visitable_state> | |
224 | { | |
225 | template <class Event,class FSM> | |
226 | void on_entry(Event const&,FSM& ) {std::cout << "entering: Paused" << std::endl;} | |
227 | template <class Event,class FSM> | |
228 | void on_exit(Event const&,FSM& ) {std::cout << "leaving: Paused" << std::endl;} | |
229 | }; | |
230 | ||
231 | struct AllOk : public msm::front::state<my_visitable_state> | |
232 | { | |
233 | template <class Event,class FSM> | |
234 | void on_entry(Event const&,FSM& ) {std::cout << "starting: AllOk" << std::endl;} | |
235 | template <class Event,class FSM> | |
236 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: AllOk" << std::endl;} | |
237 | }; | |
238 | struct ErrorMode : | |
239 | public msm::front::interrupt_state<end_error,my_visitable_state> | |
240 | { | |
241 | template <class Event,class FSM> | |
242 | void on_entry(Event const&,FSM& ) {std::cout << "starting: ErrorMode" << std::endl;} | |
243 | template <class Event,class FSM> | |
244 | void on_exit(Event const&,FSM& ) {std::cout << "finishing: ErrorMode" << std::endl;} | |
245 | void accept(SomeVisitor& vis,int i) const | |
246 | { | |
247 | vis.visit_state(this,i); | |
248 | } | |
249 | }; | |
250 | ||
251 | // the initial state of the player SM. Must be defined | |
252 | typedef mpl::vector<Empty,AllOk> initial_state; | |
253 | ||
254 | // transition actions | |
255 | void start_playback(play const&) { std::cout << "player::start_playback\n"; } | |
256 | void open_drawer(open_close const&) { std::cout << "player::open_drawer\n"; } | |
257 | void close_drawer(open_close const&) { std::cout << "player::close_drawer\n"; } | |
258 | void store_cd_info(cd_detected const&) | |
259 | { | |
260 | std::cout << "player::store_cd_info\n"; | |
261 | } | |
262 | void stop_playback(stop const&) { std::cout << "player::stop_playback\n"; } | |
263 | void pause_playback(pause const&) { std::cout << "player::pause_playback\n"; } | |
264 | void resume_playback(end_pause const&) { std::cout << "player::resume_playback\n"; } | |
265 | void stop_and_open(open_close const&) { std::cout << "player::stop_and_open\n"; } | |
266 | void stopped_again(stop const&) {std::cout << "player::stopped_again\n";} | |
267 | void report_error(error_found const&) {std::cout << "player::report_error\n";} | |
268 | void report_end_error(end_error const&) {std::cout << "player::report_end_error\n";} | |
269 | // guard conditions | |
270 | ||
271 | ||
272 | typedef player_ p; // makes transition table cleaner | |
273 | ||
274 | // Transition table for player | |
275 | struct transition_table : mpl::vector< | |
276 | // Start Event Next Action Guard | |
277 | // +---------+-------------+---------+---------------------+----------------------+ | |
278 | a_row < Stopped , play , Playing , &p::start_playback >, | |
279 | a_row < Stopped , open_close , Open , &p::open_drawer >, | |
280 | a_row < Stopped , stop , Stopped , &p::stopped_again >, | |
281 | // +---------+-------------+---------+---------------------+----------------------+ | |
282 | a_row < Open , open_close , Empty , &p::close_drawer >, | |
283 | // +---------+-------------+---------+---------------------+----------------------+ | |
284 | a_row < Empty , open_close , Open , &p::open_drawer >, | |
285 | a_row < Empty , cd_detected , Stopped , &p::store_cd_info >, | |
286 | // +---------+-------------+---------+---------------------+----------------------+ | |
287 | a_row < Playing , stop , Stopped , &p::stop_playback >, | |
288 | a_row < Playing , pause , Paused , &p::pause_playback >, | |
289 | a_row < Playing , open_close , Open , &p::stop_and_open >, | |
290 | // +---------+-------------+---------+---------------------+----------------------+ | |
291 | a_row < Paused , end_pause , Playing , &p::resume_playback >, | |
292 | a_row < Paused , stop , Stopped , &p::stop_playback >, | |
293 | a_row < Paused , open_close , Open , &p::stop_and_open >, | |
294 | // +---------+-------------+---------+---------------------+----------------------+ | |
295 | a_row < AllOk , error_found ,ErrorMode, &p::report_error >, | |
296 | a_row <ErrorMode,end_error ,AllOk , &p::report_end_error > | |
297 | // +---------+-------------+---------+---------------------+----------------------+ | |
298 | > {}; | |
299 | ||
300 | // Replaces the default no-transition response. | |
301 | template <class FSM,class Event> | |
302 | void no_transition(Event const& e, FSM&,int state) | |
303 | { | |
304 | std::cout << "no transition from state " << state | |
305 | << " on event " << typeid(e).name() << std::endl; | |
306 | } | |
307 | }; | |
308 | ||
309 | // back-end | |
310 | typedef msm::back::state_machine<player_> player; | |
311 | ||
312 | // | |
313 | // Testing utilities. | |
314 | // | |
315 | ||
316 | void pstate(player const& p) | |
317 | { | |
318 | static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused","AllOk","ErrorMode","SleepMode" }; | |
319 | for (unsigned int i=0;i<player::nr_regions::value;++i) | |
320 | { | |
321 | std::cout << " -> " << state_names[p.current_state()[i]] << std::endl; | |
322 | } | |
323 | } | |
324 | void test() | |
325 | { | |
326 | player p; | |
327 | // needed to start the highest-level SM. This will call on_entry and mark the start of the SM | |
328 | p.start(); | |
329 | ||
330 | // test deferred event | |
331 | // deferred in Empty and Open, will be handled only after event cd_detected | |
332 | p.process_event(play()); | |
333 | ||
334 | // go to Open, call on_exit on Empty, then action, then on_entry on Open | |
335 | p.process_event(open_close()); pstate(p); | |
336 | // visiting Paused and AllOk, but only Paused cares | |
337 | SomeVisitor vis; | |
338 | p.visit_current_states(boost::ref(vis),1); | |
339 | p.process_event(open_close()); pstate(p); | |
340 | // visiting Empty and AllOk, but only Empty cares | |
341 | p.visit_current_states(boost::ref(vis),2); | |
342 | ||
343 | p.process_event(cd_detected()); | |
344 | // no need to call play() as the previous event does it in its action method | |
345 | //p.process_event(play()); | |
346 | // at this point, Play is active, along FirstSong and LightOn | |
347 | pstate(p); | |
348 | // visiting Playing+Song1+LightOn and AllOk, but only Playing+Song1+LightOn care | |
349 | p.visit_current_states(boost::ref(vis),3); | |
350 | ||
351 | // Stop will be active | |
352 | p.process_event(stop()); pstate(p); | |
353 | ||
354 | // visiting when both regions have an active state who wants to be visited | |
355 | p.process_event(error_found()); | |
356 | p.visit_current_states(boost::ref(vis),5); | |
357 | ||
358 | ||
359 | } | |
360 | } | |
361 | ||
362 | int main() | |
363 | { | |
364 | test(); | |
365 | return 0; | |
366 | } | |
367 | ||
368 |