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

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