source: branches/testFACT++branch/src/StateMachineImp.h@ 18302

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