• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 using namespace boost::msm::front;
20 namespace msm = boost::msm;
21 
22 // entry/exit/action/guard logging functors
23 #include "logging_functors.h"
24 
25 namespace  // Concrete FSM implementation
26 {
27     // events
28     // note that unlike the SimpleTutorial, events must derive from euml_event.
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(internal_event)
35     BOOST_MSM_EUML_EVENT(next_song)
36     BOOST_MSM_EUML_EVENT(previous_song)
37 
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     // Concrete FSM implementation
45 
46     // The list of FSM states
47     BOOST_MSM_EUML_STATE(( Empty_Entry,Empty_Exit ),Empty)
48 
49     // we just declare a state type but do not create any instance as we want to inherit from this type
50     BOOST_MSM_EUML_DECLARE_STATE((Open_Entry,Open_Exit),Open_def)
51     // derive to be able to add an internal transition table
52     struct Open_impl : public Open_def
53     {
54         BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE((
55             open_close  [internal_guard1] / internal_action1                ,
56             open_close  [internal_guard2] / internal_action2                ,
57             internal_event / internal_action
58             ))
59     };
60     // declare an instance for the stt as we are manually declaring a state
61     Open_impl const Open;
62     BOOST_MSM_EUML_STATE(( Stopped_Entry,Stopped_Exit ),Stopped)
63 
64     // Playing is a state machine itself.
65     // It has 3 substates
66     BOOST_MSM_EUML_STATE(( Song1_Entry,Song1_Exit ),Song1)
67     BOOST_MSM_EUML_STATE(( Song2_Entry,Song2_Exit ),Song2)
68     BOOST_MSM_EUML_STATE(( Song3_Entry,Song3_Exit ),Song3)
69 
70     // Playing has a transition table
71     BOOST_MSM_EUML_TRANSITION_TABLE((
72         //  +------------------------------------------------------------------------------+
73             Song2         == Song1          + next_song       / start_next_song,
74             Song1         == Song2          + previous_song   / start_prev_song,
75             Song3         == Song2          + next_song       / start_next_song,
76             Song2         == Song3          + previous_song   / start_prev_song
77         //  +------------------------------------------------------------------------------+
78         ),playing_transition_table )
79 
80     BOOST_MSM_EUML_DECLARE_STATE_MACHINE( (playing_transition_table, //STT
81                                         init_ << Song1 // Init State
82                                         ),Playing_def)
83 
84     // some action for the internal transition
BOOST_MSM_EUML_ACTION(playing_internal_action)85     BOOST_MSM_EUML_ACTION(playing_internal_action)
86     {
87         template <class FSM,class EVT,class SourceState,class TargetState>
88         void operator()(EVT const&, FSM& ,SourceState& ,TargetState& )
89         {
90             cout << "Playing::internal action" << endl;
91         }
92     };
93     // derive to be able to add an internal transition table
94     struct Playing_ : public Playing_def
95     {
96         BOOST_MSM_EUML_DECLARE_INTERNAL_TRANSITION_TABLE((
97             internal_event / playing_internal_action
98             ))
99     };
100     // choice of back-end
101     typedef msm::back::state_machine<Playing_> Playing_type;
102     Playing_type const Playing;
103 
104 
105     // state not needing any entry or exit
106     BOOST_MSM_EUML_STATE((),Paused)
107 
108     // guard conditions
BOOST_MSM_EUML_ACTION(good_disk_format)109     BOOST_MSM_EUML_ACTION(good_disk_format)
110     {
111         template <class FSM,class EVT,class SourceState,class TargetState>
112         bool operator()(EVT const& evt,FSM&,SourceState& ,TargetState& )
113         {
114             // to test a guard condition, let's say we understand only CDs, not DVD
115             if (evt.get_attribute(cd_type)!=DISK_CD)
116             {
117                 std::cout << "wrong disk, sorry" << std::endl;
118                 // just for logging, does not block any transition
119                 return true;
120             }
121             std::cout << "good disk" << std::endl;
122             return true;
123         }
124     };
125     // replaces the old transition table
126     BOOST_MSM_EUML_TRANSITION_TABLE((
127           Stopped + play        / start_playback          == Playing                    ,
128           Stopped + open_close  / open_drawer             == Open                       ,
129           Stopped + stop                                  == Stopped                    ,
130           //  +------------------------------------------------------------------------------+
131           Open    + open_close  / close_drawer            == Empty                      ,
132           //  +------------------------------------------------------------------------------+
133           Empty   + open_close  / open_drawer             == Open                       ,
134           Empty   + cd_detected
135             [good_disk_format&&(event_(cd_type)==Int_<DISK_CD>())]
136             / (store_cd_info,process_(play))
137                                                             == Stopped                  ,
138          //  +------------------------------------------------------------------------------+
139           Playing + stop        / stop_playback           == Stopped                    ,
140           Playing + pause       / pause_playback          == Paused                     ,
141           Playing + open_close  / stop_and_open           == Open                       ,
142           //  +------------------------------------------------------------------------------+
143           Paused  + end_pause   / resume_playback         == Playing                    ,
144           Paused  + stop        / stop_playback           == Stopped                    ,
145           Paused  + open_close  / stop_and_open           == Open
146           //  +------------------------------------------------------------------------------+
147           ),transition_table)
148 
149 
150 
151     // create a state machine "on the fly"
152     BOOST_MSM_EUML_DECLARE_STATE_MACHINE(( transition_table, //STT
153                                         init_ << Empty, // Init State
154                                         no_action, // Entry
155                                         no_action, // Exit
156                                         attributes_ << no_attributes_, // Attributes
157                                         configure_ << no_configure_, // configuration
158                                         Log_No_Transition // no_transition handler
159                                         ),
160                                       player_) //fsm name
161 
162     // choice of back-end
163     typedef msm::back::state_machine<player_> player;
164 
165     //
166     // Testing utilities.
167     //
168     static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
pstate(player const & p)169     void pstate(player const& p)
170     {
171         std::cout << " -> " << state_names[p.current_state()[0]] << std::endl;
172     }
173 
test()174     void test()
175     {
176         player p;
177         // needed to start the highest-level SM. This will call on_entry and mark the start of the SM
178         p.start();
179         // go to Open, call on_exit on Empty, then action, then on_entry on Open
180         p.process_event(open_close); pstate(p);
181         std::cout << "sending internal event (not rejected)" << std::endl;
182         p.process_event(internal_event);
183         std::cout << "sending open_close event. Conflict with internal transitions (rejecting event)" << std::endl;
184         p.process_event(open_close); pstate(p);
185         // will be rejected, wrong disk type
186         p.process_event(
187             cd_detected("louie, louie",DISK_DVD)); pstate(p);
188         p.process_event(
189             cd_detected("louie, louie",DISK_CD)); pstate(p);
190         // no need to call play as the previous event does it in its action method
191         //p.process_event(play);
192         // at this point, Play is active
193         // make transition happen inside it. Player has no idea about this event but it's ok.
194         p.process_event(next_song);pstate(p); //2nd song active
195         p.process_event(next_song);pstate(p);//3rd song active
196         p.process_event(previous_song);pstate(p);//2nd song active
197         // event handled internally in Playing, without region checking
198         std::cout << "sending internal event (not rejected)" << std::endl;
199         p.process_event(internal_event);
200 
201         p.process_event(pause); pstate(p);
202         // go back to Playing
203         p.process_event(end_pause);  pstate(p);
204         p.process_event(pause); pstate(p);
205         p.process_event(stop);  pstate(p);
206         // event leading to the same state
207         // no action method called as none is defined in the transition table
208         p.process_event(stop);  pstate(p);
209         // test call to no_transition
210         p.process_event(pause); pstate(p);
211     }
212 }
213 
main()214 int main()
215 {
216     test();
217     return 0;
218 }
219