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 <boost/msm/back/state_machine.hpp> |
12 | #include <boost/msm/front/euml/euml.hpp> |
13 | #ifndef BOOST_MSM_NONSTANDALONE_TEST |
14 | #define BOOST_TEST_MODULE MySimpleInternalEumlTest |
15 | #endif |
16 | #include <boost/test/unit_test.hpp> |
17 | |
18 | using namespace std; |
19 | using namespace boost::msm::front::euml; |
20 | namespace msm = boost::msm; |
21 | |
22 | namespace |
23 | { |
24 | // A "complicated" event type that carries some data. |
25 | enum DiskTypeEnum |
26 | { |
27 | DISK_CD=0, |
28 | DISK_DVD=1 |
29 | }; |
30 | // events |
31 | BOOST_MSM_EUML_EVENT(play) |
32 | BOOST_MSM_EUML_EVENT(end_pause) |
33 | BOOST_MSM_EUML_EVENT(stop) |
34 | BOOST_MSM_EUML_EVENT(pause) |
35 | BOOST_MSM_EUML_EVENT(open_close) |
36 | BOOST_MSM_EUML_EVENT(internal_evt) |
37 | BOOST_MSM_EUML_EVENT(to_ignore) |
38 | // A "complicated" event type that carries some data. |
39 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(std::string,cd_name) |
40 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(DiskTypeEnum,cd_type) |
41 | BOOST_MSM_EUML_ATTRIBUTES((attributes_ << cd_name << cd_type ), cd_detected_attributes) |
42 | BOOST_MSM_EUML_EVENT_WITH_ATTRIBUTES(cd_detected,cd_detected_attributes) |
43 | |
44 | //states |
45 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter) |
46 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter) |
47 | |
48 | BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open) |
49 | BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped) |
50 | BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Playing) |
51 | BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused) |
52 | |
53 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_guard_counter) |
54 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,empty_internal_action_counter) |
55 | |
56 | BOOST_MSM_EUML_ACTION(internal_guard_fct) |
57 | { |
58 | template <class FSM,class EVT,class SourceState,class TargetState> |
59 | bool operator()(EVT const&, FSM& ,SourceState& src,TargetState& ) |
60 | { |
61 | ++src.get_attribute(empty_internal_guard_counter); |
62 | return false; |
63 | } |
64 | }; |
65 | BOOST_MSM_EUML_DECLARE_STATE((++state_(entry_counter),++state_(exit_counter), |
66 | attributes_ << entry_counter << exit_counter |
67 | << empty_internal_guard_counter << empty_internal_action_counter),Empty_def) |
68 | // derive to be able to add an internal transition table |
69 | struct Empty_impl : public Empty_def |
70 | { |
71 | Empty_impl(){} |
72 | BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE(( |
73 | internal_evt [internal_guard_fct] / ++source_(empty_internal_action_counter) |
74 | )) |
75 | }; |
76 | // declare an instance for the stt as we are manually declaring a state |
77 | Empty_impl const Empty; |
78 | |
79 | //fsm |
80 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_playback_counter) |
81 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,can_close_drawer_counter) |
82 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_action_counter) |
83 | BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,internal_guard_counter) |
84 | BOOST_MSM_EUML_ACTION(No_Transition) |
85 | { |
86 | template <class FSM,class Event> |
87 | void operator()(Event const&,FSM&,int) |
88 | { |
89 | BOOST_FAIL("no_transition called!" ); |
90 | } |
91 | }; |
92 | BOOST_MSM_EUML_ACTION(good_disk_format) |
93 | { |
94 | template <class FSM,class EVT,class SourceState,class TargetState> |
95 | bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& ) |
96 | { |
97 | if (evt.get_attribute(cd_type)!=DISK_CD) |
98 | { |
99 | return false; |
100 | } |
101 | return true; |
102 | } |
103 | }; |
104 | BOOST_MSM_EUML_ACTION(internal_guard) |
105 | { |
106 | template <class FSM,class EVT,class SourceState,class TargetState> |
107 | bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) |
108 | { |
109 | ++fsm.get_attribute(internal_guard_counter); |
110 | return false; |
111 | } |
112 | }; |
113 | BOOST_MSM_EUML_ACTION(internal_guard2) |
114 | { |
115 | template <class FSM,class EVT,class SourceState,class TargetState> |
116 | bool operator()(EVT const&,FSM& fsm,SourceState& ,TargetState& ) |
117 | { |
118 | ++fsm.get_attribute(internal_guard_counter); |
119 | return true; |
120 | } |
121 | }; |
122 | BOOST_MSM_EUML_TRANSITION_TABLE(( |
123 | Playing == Stopped + play / ++fsm_(start_playback_counter) , |
124 | Playing == Paused + end_pause , |
125 | // +------------------------------------------------------------------------------+ |
126 | Empty == Open + open_close / ++fsm_(can_close_drawer_counter), |
127 | Empty + to_ignore , |
128 | Empty + internal_evt [internal_guard2] / ++fsm_(internal_action_counter) , |
129 | Empty + cd_detected [internal_guard] , |
130 | // +------------------------------------------------------------------------------+ |
131 | Open == Empty + open_close , |
132 | Open == Paused + open_close , |
133 | Open == Stopped + open_close , |
134 | Open == Playing + open_close , |
135 | // +------------------------------------------------------------------------------+ |
136 | Paused == Playing + pause , |
137 | // +------------------------------------------------------------------------------+ |
138 | Stopped == Playing + stop , |
139 | Stopped == Paused + stop , |
140 | Stopped == Empty + cd_detected [good_disk_format] , |
141 | Stopped == Stopped + stop |
142 | // +------------------------------------------------------------------------------+ |
143 | ),transition_table) |
144 | |
145 | BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT |
146 | init_ << Empty, // Init State |
147 | no_action, // Entry |
148 | no_action, // Exit |
149 | attributes_ << start_playback_counter << can_close_drawer_counter |
150 | << internal_action_counter << internal_guard_counter, // Attributes |
151 | configure_ << no_configure_, // configuration |
152 | No_Transition // no_transition handler |
153 | ), |
154 | player_) //fsm name |
155 | |
156 | typedef msm::back::state_machine<player_> player; |
157 | |
158 | // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" }; |
159 | |
160 | |
161 | BOOST_AUTO_TEST_CASE( simple_internal_euml_test ) |
162 | { |
163 | player p; |
164 | |
165 | p.start(); |
166 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, |
167 | "Empty entry not called correctly" ); |
168 | |
169 | // internal events |
170 | p.process_event(evt: to_ignore); |
171 | p.process_event(evt: internal_evt); |
172 | BOOST_CHECK_MESSAGE(p.get_attribute(internal_action_counter) == 1,"Internal action not called correctly" ); |
173 | BOOST_CHECK_MESSAGE(p.get_attribute(internal_guard_counter) == 1,"Internal guard not called correctly" ); |
174 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_action_counter) == 0, |
175 | "Empty internal action not called correctly" ); |
176 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(empty_internal_guard_counter) == 1, |
177 | "Empty internal guard not called correctly" ); |
178 | |
179 | p.process_event(evt: open_close()); |
180 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active" ); //Open |
181 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 1, |
182 | "Empty exit not called correctly" ); |
183 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 1, |
184 | "Open entry not called correctly" ); |
185 | |
186 | p.process_event(evt: open_close()); |
187 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active" ); //Empty |
188 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, |
189 | "Open exit not called correctly" ); |
190 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, |
191 | "Empty entry not called correctly" ); |
192 | BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly" ); |
193 | |
194 | p.process_event( |
195 | evt: cd_detected("louie, louie" ,DISK_DVD)); |
196 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active" ); //Empty |
197 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1, |
198 | "Open exit not called correctly" ); |
199 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(entry_counter) == 2, |
200 | "Empty entry not called correctly" ); |
201 | |
202 | p.process_event( |
203 | evt: cd_detected("louie, louie" ,DISK_CD)); |
204 | p.process_event(evt: play); |
205 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active" ); //Playing |
206 | BOOST_CHECK_MESSAGE(p.get_state<Empty_impl&>().get_attribute(exit_counter) == 2, |
207 | "Empty exit not called correctly" ); |
208 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1, |
209 | "Stopped entry not called correctly" ); |
210 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1, |
211 | "Stopped exit not called correctly" ); |
212 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 1, |
213 | "Playing entry not called correctly" ); |
214 | BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly" ); |
215 | |
216 | p.process_event(evt: pause()); |
217 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active" ); //Paused |
218 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 1, |
219 | "Playing exit not called correctly" ); |
220 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1, |
221 | "Paused entry not called correctly" ); |
222 | |
223 | // go back to Playing |
224 | p.process_event(evt: end_pause()); |
225 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active" ); //Playing |
226 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 1, |
227 | "Paused exit not called correctly" ); |
228 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(entry_counter) == 2, |
229 | "Playing entry not called correctly" ); |
230 | |
231 | p.process_event(evt: pause()); |
232 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active" ); //Paused |
233 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Playing)&>().get_attribute(exit_counter) == 2, |
234 | "Playing exit not called correctly" ); |
235 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2, |
236 | "Paused entry not called correctly" ); |
237 | |
238 | p.process_event(evt: stop()); |
239 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
240 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2, |
241 | "Paused exit not called correctly" ); |
242 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2, |
243 | "Stopped entry not called correctly" ); |
244 | |
245 | p.process_event(evt: stop()); |
246 | BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active" ); //Stopped |
247 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2, |
248 | "Stopped exit not called correctly" ); |
249 | BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3, |
250 | "Stopped entry not called correctly" ); |
251 | } |
252 | } |
253 | |
254 | |