source: trunk/FACT++/src/StateMachineImp.h@ 14420

Last change on this file since 14420 was 14351, checked in by tbretz, 12 years ago
The state from the callback was not yet properly propagated. Implemented kSM_KeepState to signal that the state should not change.
File size: 9.5 KB
Line 
1#ifndef FACT_StateMachineImp
2#define FACT_StateMachineImp
3
4#include <map>
5#include <queue>
6#include <mutex>
7#include <vector>
8
9#include "MainImp.h"
10#include "MessageImp.h"
11
12class Event;
13class EventImp;
14
15class StateMachineImp : public MainImp, public MessageImp
16{
17public:
18 /// A list of default states available to any state machine.
19 /// Derived classes must define different state-number for
20 /// their purpose
21 enum DefaultStates_t
22 {
23 kSM_KeepState = -42, ///<
24 kSM_NotReady = -1, ///< Mainloop not running, state machine stopped
25 kSM_Ready = 0, ///< Mainloop running, state machine in operation
26 kSM_UserMode = 1, ///< First user defined mode (to be used in derived classes' enums)
27 kSM_Error = 0x100, ///< Error states should be between 0x100 and 0xffff
28 kSM_FatalError = 0xffff, ///< Fatal error: stop program
29 };
30
31private:
32 std::string fName; /// Name of the state-machine / server (e.g. DRIVE)
33
34 int fCurrentState; /// Current state of the state machine
35
36 typedef std::map<const int, std::pair<std::string, std::string>> StateNames;
37
38 /// Human readable names associated with the states
39 StateNames fStateNames;
40
41 std::vector<EventImp*> fListOfEvents; /// List of available commands as setup by user
42 std::queue<Event*> fEventQueue; /// Event queue (fifo) for the received commands
43
44 std::mutex fMutex; /// Mutex to ensure thread-safe access to the command fifo
45 std::mutex fMutexEvt; /// Mutex to ensure thread-safe access to the command fifo
46
47 bool fRunning; /// Machine is in main-loop
48 bool fBufferEvents; /// Flag if events should be buffered outside the event loop
49 int fExitRequested; /// This is a flag which is set true if the main loop should stop
50
51 /// Push a command into the fifo. The fifo takes over ownership
52 void PushEvent(Event *cmd);
53 /// Pop a command from the fifo. You take over owenership
54 Event *PopEvent();
55
56 bool HandleNewState(int newstate, const EventImp *evt, const char *txt);
57
58 virtual EventImp *CreateEvent(const std::string &name, const std::string &fmt) = 0;
59 virtual EventImp *CreateService(const std::string &);
60
61 /// Is called continously to execute actions in the current state
62 virtual int Execute() { return fCurrentState; }
63 /// Is called when a configuration event is to be processed (no transition of state)
64 //virtual int Configure(const Event &) { return kSM_FatalError; }
65 /// Is called when a transition change event is to be processed (from one state to another) is received
66 //virtual int Transition(const Event &) { return kSM_FatalError; }
67
68 virtual void Lock() { }
69 virtual void UnLock() { }
70
71 int Wrapper(const std::function<int(const EventImp &)> &f, const EventImp &imp)
72 {
73 const int rc = f(imp);
74 return rc==kSM_KeepState ? GetCurrentState() : rc;
75 }
76
77protected:
78
79 bool HandleEvent(const EventImp &evt);
80
81 /// This is an internal function to do some action in case of
82 /// a state change, like updating the corresponding service.
83 virtual std::string SetCurrentState(int state, const char *txt="", const std::string &cmd="");
84
85 EventImp &AddEvent(const std::string &name, const std::string &states, const std::string &fmt);
86 EventImp &AddEvent(const std::string &name, int s1=-1, int s2=-1, int s3=-1, int s4=-1, int s5=-1);
87 EventImp &AddEvent(const std::string &name, const std::string &fmt, int s1=-1, int s2=-1, int s3=-1, int s4=-1, int s5=-1);
88
89 virtual void AddStateName(const int state, const std::string &name, const std::string &doc="");
90
91 void SetDefaultStateNames();
92
93public:
94 StateMachineImp(std::ostream &out=std::cout, const std::string &name="");
95 ~StateMachineImp();
96
97 std::function<int(const EventImp &)> Wrap(const std::function<int(const EventImp &)> &func)
98 {
99 return bind(&StateMachineImp::Wrapper, this, func, std::placeholders::_1);
100 }
101
102 const std::string &GetName() const { return fName; }
103
104 EventImp &Subscribe(const std::string &name);
105 void Unsubscribe(EventImp *evt);
106
107 /// return the current state of the machine
108 int GetCurrentState() const { return fCurrentState; }
109
110 void SetReady() { SetCurrentState(kSM_Ready, "set manually"); }
111 void SetNotReady() { SetCurrentState(kSM_NotReady, "set manually"); }
112
113 /// Start the mainloop
114 int Run(bool dummy);
115 int Run() { return Run(false); }
116
117 /// Request to stop the mainloop
118 void Stop(int code=0);
119
120 /// Used to check if the main loop is already running or still running
121 bool IsRunning() const { return fRunning; }
122
123 /// Used to enable or disable buffering of events outside of the main loop
124 void EnableBuffer(bool b=true) { fBufferEvents=b; }
125
126 /// Post an event to the event queue
127 bool PostEvent(std::ostream &lout, const std::string &str);
128 bool PostEvent(const std::string &evt) { return PostEvent(std::cout, evt); }
129 bool PostEvent(const EventImp &evt);
130 bool PostEvent(const EventImp &evt, const char *ptr, size_t siz);
131
132 // Event handling
133 bool HasEvent(const EventImp *cmd);
134 EventImp *FindEvent(const std::string &evt);
135
136 bool IsQueueEmpty() const { return fEventQueue.size()==0; }
137
138 //const std::vector<EventImp*> &GetListOfEvents() const { return fListOfEvents; }
139 const std::vector<std::string> GetEventNames();
140
141 void PrintListOfEvents(std::ostream &out, const std::string &evt="");
142 void PrintListOfEvents(const std::string &str="");
143
144 void PrintListOfAllowedEvents(std::ostream &out);
145 void PrintListOfAllowedEvents();
146
147 void PrintListOfStates(std::ostream &out) const;
148 void PrintListOfStates() const;
149
150
151 const std::string GetStateName(int state) const;
152 const std::string GetStateName() const { return GetStateName(fCurrentState); }
153
154 const std::string GetStateDesc(int state) const;
155 const std::string GetStateDesc() const { return GetStateDesc(fCurrentState); }
156
157 const std::string GetStateDescription(int state) const;
158 const std::string GetStateDescription() const { return GetStateDescription(fCurrentState); }
159};
160
161#endif
162
163// ***************************************************************************
164/** @fn StateMachineImp::Execute()
165
166This is what the state machine is doing in a certain state
167continously. In an idle state this might just be doing nothing.
168
169In the tracking state of the drive system this might be sending
170new command values to the drive based on its current position.
171
172The current state of the state machine can be accessed by GetCurrentState()
173
174@returns
175 Usually it should just return the current state. However, sometimes
176 execution might lead to a new state, e.g. when a hardware error
177 is detected. In this case a new state can be returned to put the state
178 machine into a different state. Note, that the function is responsible
179 of doing all actions connected with the state change itself.
180 If not overwritten it returns the current status.
181
182**/
183// ***************************************************************************
184/** @fn StateMachineImp::Configure(const Event &evt)
185
186This function is called when a configuration event is to be processed.
187
188The current state of the state machine is accessible via GetCurrentState().
189
190The issued event and its corresponding data is accessible through
191evn. (see Event and DimEvent for details) Usually such an event
192will not change the state. In this case fCurrentState will be returned.
193However, to allow the machine to go into an error state it is possible
194to change the state by giving a different return value. When the
195Configure function is called the validity of the state transition has
196already been checked.
197
198@param evt
199 A reference to an Event object with the event which should
200 be processed. Note that the cmd-object will get deleted after the
201 function has returned.
202
203@returns
204 Usually it should just return the current state. However, sometimes
205 a configuration command which was not intended to change the state
206 has to change the state, e.g. to go to an error state. Return any
207 other state than GetCurrentState() can put the state machine into
208 a different state. Note, that the function is responsible
209 of doing all actions connected with the state change itself.
210 If not overwritten it returns kSM_FatalError.
211
212**/
213// ***************************************************************************
214/** @fn StateMachineImp::Transition(const Event &evt)
215
216This function is called if a state transision was requested.
217
218The current state of the state machine is accessible via GetCurrentState().
219
220The new state is accessible via evt.GetTargetState().
221
222The event and its corresponding data is accessible through evt.
223(see DimCommand and DimEvent for details) If the transition was
224successfull the new status should be returned. If it was unsuccessfull
225either the old or any other new status will be returned.
226
227When the Transition function is called the validity of the state
228transition has already been checked.
229
230@param evt
231 A reference to an Event object with the event which should
232 be processed. Note that the cmd-object will get deleted after the
233 function has returned.
234
235@returns
236 Usually it should return the new state. However, sometimes
237 a transition command might has to change the state to a different
238 state than the one requested (e.g. an error has occured) In this
239 case it is also allowed to return a different state. Note, that the
240 function is responsible of doing all actions connected with the
241 state change itself.
242 If not overwritten it returns kSM_FatalError.
243
244**/
245// ***************************************************************************
Note: See TracBrowser for help on using the repository browser.