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