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

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