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

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