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

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