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

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