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 // back-end
13 #include <boost/msm/back/state_machine.hpp>
14 //front-end
15 #include <boost/msm/front/state_machine_def.hpp>
16 #include <boost/msm/front/row2.hpp>
17
18 namespace msm = boost::msm;
19 namespace mpl = boost::mpl;
20 using namespace msm::front;
21
22 namespace
23 {
24 // events
25 struct play {};
26 struct end_pause {};
27 struct stop {};
28 struct pause {};
29 struct open_close {};
30
31 // A "complicated" event type that carries some data.
32 enum DiskTypeEnum
33 {
34 DISK_CD=0,
35 DISK_DVD=1
36 };
37 struct cd_detected
38 {
cd_detected__anon6378e5050111::cd_detected39 cd_detected(std::string name, DiskTypeEnum diskType)
40 : name(name),
41 disc_type(diskType)
42 {}
43
44 std::string name;
45 DiskTypeEnum disc_type;
46 };
47
48 // front-end: define the FSM structure
49 struct player_ : public msm::front::state_machine_def<player_>
50 {
51 template <class Event,class FSM>
on_entry__anon6378e5050111::player_52 void on_entry(Event const& ,FSM&)
53 {
54 std::cout << "entering: Player" << std::endl;
55 }
56 template <class Event,class FSM>
on_exit__anon6378e5050111::player_57 void on_exit(Event const&,FSM& )
58 {
59 std::cout << "leaving: Player" << std::endl;
60 }
61
62 // The list of FSM states
63 struct Empty : public msm::front::state<>
64 {
65 // every (optional) entry/exit methods get the event passed.
66 template <class Event,class FSM>
on_entry__anon6378e5050111::player_::Empty67 void on_entry(Event const&,FSM& ) {std::cout << "entering: Empty" << std::endl;}
68 template <class Event,class FSM>
on_exit__anon6378e5050111::player_::Empty69 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Empty" << std::endl;}
open_drawer__anon6378e5050111::player_::Empty70 void open_drawer(open_close const&) { std::cout << "Empty::open_drawer\n"; }
71 // actions for Empty's internal transitions
internal_action__anon6378e5050111::player_::Empty72 void internal_action(cd_detected const&){ std::cout << "Empty::internal action\n"; }
internal_guard__anon6378e5050111::player_::Empty73 bool internal_guard(cd_detected const&)
74 {
75 std::cout << "Empty::internal guard\n";
76 return false;
77 }
78 };
79 struct Open : public msm::front::state<>
80 {
81 template <class Event,class FSM>
on_entry__anon6378e5050111::player_::Open82 void on_entry(Event const& ,FSM&) {std::cout << "entering: Open" << std::endl;}
83 template <class Event,class FSM>
on_exit__anon6378e5050111::player_::Open84 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Open" << std::endl;}
close_drawer__anon6378e5050111::player_::Open85 void close_drawer(open_close const&) { std::cout << "Open::close_drawer\n"; }
stop_and_open__anon6378e5050111::player_::Open86 void stop_and_open(open_close const&) { std::cout << "Open::stop_and_open\n"; }
87 };
88
89 // sm_ptr still supported but deprecated as functors are a much better way to do the same thing
90 struct Stopped : public msm::front::state<msm::front::default_base_state,msm::front::sm_ptr>
91 {
92 template <class Event,class FSM>
on_entry__anon6378e5050111::player_::Stopped93 void on_entry(Event const& ,FSM&) {std::cout << "entering: Stopped" << std::endl;}
94 template <class Event,class FSM>
on_exit__anon6378e5050111::player_::Stopped95 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Stopped" << std::endl;}
set_sm_ptr__anon6378e5050111::player_::Stopped96 void set_sm_ptr(player_* pl)
97 {
98 m_player=pl;
99 }
100 player_* m_player;
start_playback__anon6378e5050111::player_::Stopped101 void start_playback(play const&) { std::cout << "Stopped::start_playback\n"; }
stop_playback__anon6378e5050111::player_::Stopped102 void stop_playback(stop const&) { std::cout << "Stopped::stop_playback\n"; }
103 };
104
105 struct Playing : public msm::front::state<>
106 {
107 template <class Event,class FSM>
on_entry__anon6378e5050111::player_::Playing108 void on_entry(Event const&,FSM& ) {std::cout << "entering: Playing" << std::endl;}
109 template <class Event,class FSM>
on_exit__anon6378e5050111::player_::Playing110 void on_exit(Event const&,FSM& ) {std::cout << "leaving: Playing" << std::endl;}
111 // guard conditions
112 // used to show a transition conflict. This guard will simply deactivate one transition and thus
113 // solve the conflict
auto_start__anon6378e5050111::player_::Playing114 bool auto_start(cd_detected const&)
115 {
116 return false;
117 }
118 };
119
120 // state not defining any entry or exit
121 struct Paused : public msm::front::state<>
122 {
pause_playback__anon6378e5050111::player_::Paused123 void pause_playback(pause const&) { std::cout << "Paused::pause_playback\n"; }
resume_playback__anon6378e5050111::player_::Paused124 void resume_playback(end_pause const&) { std::cout << "Paused::resume_playback\n"; }
125 };
126
127 // action
store_cd_info__anon6378e5050111::player_128 void store_cd_info(cd_detected const&) { std::cout << "Player::store_cd_info\n"; }
129
130 // guard
good_disk_format__anon6378e5050111::player_131 bool good_disk_format(cd_detected const& evt)
132 {
133 // to test a guard condition, let's say we understand only CDs, not DVD
134 if (evt.disc_type != DISK_CD)
135 {
136 std::cout << "wrong disk, sorry" << std::endl;
137 return false;
138 }
139 return true;
140 }
141 // the initial state of the player SM. Must be defined
142 typedef Empty initial_state;
143
144 // Transition table for player
145 struct transition_table : mpl::vector<
146 // Start Event Next Action/Guard
147 // +---------+-------------+---------+---------------------+----------------------+
148 a_row2 < Stopped , play , Playing , Stopped , &Stopped::start_playback >,
149 a_row2 < Stopped , open_close , Open , Empty , &Empty::open_drawer >,
150 _row < Stopped , stop , Stopped >,
151 // +---------+-------------+---------+---------------------+----------------------+
152 a_row2 < Open , open_close , Empty , Open , &Open::close_drawer >,
153 // +---------+-------------+---------+---------------------+----------------------+
154 a_row2 < Empty , open_close , Open , Empty ,&Empty::open_drawer >,
155 row2 < Empty , cd_detected , Stopped , player_ ,&player_::store_cd_info
156 , player_ ,&player_::good_disk_format >,
157 row2 < Empty , cd_detected , Playing , player_ ,&player_::store_cd_info
158 , Playing ,&Playing::auto_start >,
159 // conflict with some internal rows
160 irow2 < Empty , cd_detected , Empty ,&Empty::internal_action
161 , Empty ,&Empty::internal_guard >,
162 g_irow2 < Empty , cd_detected , Empty ,&Empty::internal_guard >,
163 // +---------+-------------+---------+---------------------+----------------------+
164 a_row2 < Playing , stop , Stopped , Stopped ,&Stopped::stop_playback >,
165 a_row2 < Playing , pause , Paused , Paused ,&Paused::pause_playback >,
166 a_row2 < Playing , open_close , Open , Open ,&Open::stop_and_open >,
167 // +---------+-------------+---------+---------------------+----------------------+
168 a_row2 < Paused , end_pause , Playing , Paused ,&Paused::resume_playback >,
169 a_row2 < Paused , stop , Stopped , Stopped ,&Stopped::stop_playback >,
170 a_row2 < Paused , open_close , Open , Open ,&Open::stop_and_open >
171 // +---------+-------------+---------+---------------------+----------------------+
172 > {};
173 // Replaces the default no-transition response.
174 template <class FSM,class Event>
no_transition__anon6378e5050111::player_175 void no_transition(Event const& e, FSM&,int state)
176 {
177 std::cout << "no transition from state " << state
178 << " on event " << typeid(e).name() << std::endl;
179 }
180 };
181 // Pick a back-end
182 typedef msm::back::state_machine<player_> player;
183
184 //
185 // Testing utilities.
186 //
187 static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)188 void pstate(player const& p)
189 {
190 std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
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 // 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 p.process_event(play());
207
208 // at this point, Play is active
209 p.process_event(pause()); pstate(p);
210 // go back to Playing
211 p.process_event(end_pause()); pstate(p);
212 p.process_event(pause()); pstate(p);
213 p.process_event(stop()); pstate(p);
214 // event leading to the same state
215 // no action method called as it is not present in the transition table
216 p.process_event(stop()); pstate(p);
217 std::cout << "stop fsm" << std::endl;
218 p.stop();
219
220 }
221 }
222
main()223 int main()
224 {
225 test();
226 return 0;
227 }
228