CompositeEuml.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. #include <iostream>
  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 MyTest
  15. #endif
  16. #include <boost/test/unit_test.hpp>
  17. using namespace std;
  18. using namespace boost::msm::front::euml;
  19. namespace msm = boost::msm;
  20. namespace
  21. {
  22. // A "complicated" event type that carries some data.
  23. enum DiskTypeEnum
  24. {
  25. DISK_CD=0,
  26. DISK_DVD=1
  27. };
  28. // events
  29. BOOST_MSM_EUML_EVENT(play)
  30. BOOST_MSM_EUML_EVENT(end_pause)
  31. BOOST_MSM_EUML_EVENT(stop)
  32. BOOST_MSM_EUML_EVENT(pause)
  33. BOOST_MSM_EUML_EVENT(open_close)
  34. BOOST_MSM_EUML_EVENT(next_song)
  35. BOOST_MSM_EUML_EVENT(previous_song)
  36. BOOST_MSM_EUML_EVENT(region2_evt)
  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. //states
  43. BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,entry_counter)
  44. BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,exit_counter)
  45. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Empty)
  46. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Open)
  47. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Stopped)
  48. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Paused)
  49. // Playing is now a state machine itself.
  50. BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_next_song_counter)
  51. BOOST_MSM_EUML_DECLARE_ATTRIBUTE(unsigned int,start_prev_song_guard_counter)
  52. // It has 5 substates
  53. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song1)
  54. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song2)
  55. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Song3)
  56. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State1)
  57. BOOST_MSM_EUML_STATE(( ++state_(entry_counter),++state_(exit_counter),attributes_ << entry_counter << exit_counter),Region2State2)
  58. // Playing has a transition table
  59. BOOST_MSM_EUML_TRANSITION_TABLE((
  60. // +------------------------------------------------------------------------------+
  61. Song2 == Song1 + next_song ,
  62. Song1 == Song2 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) ,
  63. Song3 == Song2 + next_song / ++fsm_(start_next_song_counter) ,
  64. Song2 == Song3 + previous_song [True_()] / ++fsm_(start_prev_song_guard_counter) ,
  65. Region2State2 == Region2State1 + region2_evt
  66. // +------------------------------------------------------------------------------+
  67. ),playing_transition_table )
  68. BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT
  69. init_ << Song1 << Region2State1, // Init State
  70. ++state_(entry_counter), // Entry
  71. ++state_(exit_counter), // Exit
  72. attributes_ << entry_counter << exit_counter
  73. << start_next_song_counter
  74. << start_prev_song_guard_counter // Attributes
  75. ),Playing_)
  76. // choice of back-end
  77. typedef msm::back::state_machine<Playing_> Playing_type;
  78. Playing_type const Playing;
  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,test_fct_counter)
  83. BOOST_MSM_EUML_ACTION(No_Transition)
  84. {
  85. template <class FSM,class Event>
  86. void operator()(Event const&,FSM&,int)
  87. {
  88. BOOST_FAIL("no_transition called!");
  89. }
  90. };
  91. BOOST_MSM_EUML_ACTION(good_disk_format)
  92. {
  93. template <class FSM,class EVT,class SourceState,class TargetState>
  94. bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
  95. {
  96. if (evt.get_attribute(cd_type)!=DISK_CD)
  97. {
  98. return false;
  99. }
  100. return true;
  101. }
  102. };
  103. BOOST_MSM_EUML_TRANSITION_TABLE((
  104. Playing == Stopped + play / (++fsm_(start_playback_counter),++fsm_(test_fct_counter) ),
  105. Playing == Paused + end_pause ,
  106. // +------------------------------------------------------------------------------+
  107. Empty == Open + open_close / ++fsm_(can_close_drawer_counter),
  108. // +------------------------------------------------------------------------------+
  109. Open == Empty + open_close ,
  110. Open == Paused + open_close ,
  111. Open == Stopped + open_close ,
  112. Open == Playing + open_close ,
  113. // +------------------------------------------------------------------------------+
  114. Paused == Playing + pause ,
  115. // +------------------------------------------------------------------------------+
  116. Stopped == Playing + stop ,
  117. Stopped == Paused + stop ,
  118. Stopped == Empty + cd_detected [good_disk_format ||
  119. (event_(cd_type)==Int_<DISK_CD>())] / process_(play) ,
  120. Stopped == Stopped + stop
  121. // +------------------------------------------------------------------------------+
  122. ),transition_table)
  123. BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
  124. init_ << Empty, // Init State
  125. no_action, // Entry
  126. no_action, // Exit
  127. attributes_ << start_playback_counter
  128. << can_close_drawer_counter << test_fct_counter, // Attributes
  129. configure_ << no_configure_, // configuration
  130. No_Transition // no_transition handler
  131. ),
  132. player_) //fsm name
  133. typedef msm::back::state_machine<player_> player;
  134. // static char const* const state_names[] = { "Stopped", "Paused", "Open", "Empty", "Playing" };
  135. BOOST_AUTO_TEST_CASE( my_test )
  136. {
  137. player p;
  138. p.start();
  139. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 1,
  140. "Empty entry not called correctly");
  141. p.process_event(open_close());
  142. BOOST_CHECK_MESSAGE(p.current_state()[0] == 2,"Open should be active"); //Open
  143. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 1,
  144. "Empty exit not called correctly");
  145. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(entry_counter) == 1,
  146. "Open entry not called correctly");
  147. p.process_event(open_close());
  148. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
  149. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1,
  150. "Open exit not called correctly");
  151. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2,
  152. "Empty entry not called correctly");
  153. BOOST_CHECK_MESSAGE(p.get_attribute(can_close_drawer_counter) == 1,"guard not called correctly");
  154. p.process_event(
  155. cd_detected("louie, louie",DISK_DVD));
  156. BOOST_CHECK_MESSAGE(p.current_state()[0] == 3,"Empty should be active"); //Empty
  157. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Open)&>().get_attribute(exit_counter) == 1,
  158. "Open exit not called correctly");
  159. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(entry_counter) == 2,
  160. "Empty entry not called correctly");
  161. p.process_event(
  162. cd_detected("louie, louie",DISK_CD));
  163. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
  164. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Empty)&>().get_attribute(exit_counter) == 2,
  165. "Empty exit not called correctly");
  166. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 1,
  167. "Stopped entry not called correctly");
  168. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 1,
  169. "Stopped exit not called correctly");
  170. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(entry_counter) == 1,
  171. "Playing entry not called correctly");
  172. BOOST_CHECK_MESSAGE(p.get_attribute(start_playback_counter) == 1,"action not called correctly");
  173. BOOST_CHECK_MESSAGE(p.get_attribute(test_fct_counter) == 1,"action not called correctly");
  174. p.process_event(next_song);
  175. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
  176. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active");
  177. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[1] == 3,"Region2State1 should be active");
  178. BOOST_CHECK_MESSAGE(
  179. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State1)&>().get_attribute(entry_counter) == 1,
  180. "Region2State1 entry not called correctly");
  181. BOOST_CHECK_MESSAGE(
  182. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 1,
  183. "Song2 entry not called correctly");
  184. BOOST_CHECK_MESSAGE(
  185. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song1)&>().get_attribute(exit_counter) == 1,
  186. "Song1 exit not called correctly");
  187. BOOST_CHECK_MESSAGE(
  188. p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 0,
  189. "submachine action not called correctly");
  190. p.process_event(next_song);
  191. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
  192. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 2,"Song3 should be active");
  193. BOOST_CHECK_MESSAGE(
  194. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(entry_counter) == 1,
  195. "Song3 entry not called correctly");
  196. BOOST_CHECK_MESSAGE(
  197. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(exit_counter) == 1,
  198. "Song2 exit not called correctly");
  199. BOOST_CHECK_MESSAGE(
  200. p.get_state<Playing_type&>().get_attribute(start_next_song_counter) == 1,
  201. "submachine action not called correctly");
  202. p.process_event(previous_song);
  203. BOOST_CHECK_MESSAGE(p.current_state()[0] == 4,"Playing should be active"); //Playing
  204. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().current_state()[0] == 1,"Song2 should be active");
  205. BOOST_CHECK_MESSAGE(
  206. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song2)&>().get_attribute(entry_counter) == 2,
  207. "Song2 entry not called correctly");
  208. BOOST_CHECK_MESSAGE(
  209. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Song3)&>().get_attribute(exit_counter) == 1,
  210. "Song3 exit not called correctly");
  211. BOOST_CHECK_MESSAGE(
  212. p.get_state<Playing_type&>().get_attribute(start_prev_song_guard_counter) == 1,
  213. "submachine guard not called correctly");
  214. BOOST_CHECK_MESSAGE(
  215. p.get_state<Playing_type&>().get_state<BOOST_MSM_EUML_STATE_NAME(Region2State2)&>().get_attribute(entry_counter) == 0,
  216. "Region2State2 entry not called correctly");
  217. p.process_event(pause());
  218. BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
  219. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 1,
  220. "Playing exit not called correctly");
  221. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 1,
  222. "Paused entry not called correctly");
  223. // go back to Playing
  224. p.process_event(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<Playing_type&>().get_attribute(entry_counter) == 2,
  229. "Playing entry not called correctly");
  230. p.process_event(pause());
  231. BOOST_CHECK_MESSAGE(p.current_state()[0] == 1,"Paused should be active"); //Paused
  232. BOOST_CHECK_MESSAGE(p.get_state<Playing_type&>().get_attribute(exit_counter) == 2,
  233. "Playing exit not called correctly");
  234. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(entry_counter) == 2,
  235. "Paused entry not called correctly");
  236. p.process_event(stop());
  237. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  238. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Paused)&>().get_attribute(exit_counter) == 2,
  239. "Paused exit not called correctly");
  240. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 2,
  241. "Stopped entry not called correctly");
  242. p.process_event(stop());
  243. BOOST_CHECK_MESSAGE(p.current_state()[0] == 0,"Stopped should be active"); //Stopped
  244. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(exit_counter) == 2,
  245. "Stopped exit not called correctly");
  246. BOOST_CHECK_MESSAGE(p.get_state<BOOST_MSM_EUML_STATE_NAME(Stopped)&>().get_attribute(entry_counter) == 3,
  247. "Stopped entry not called correctly");
  248. }
  249. }